Tue, 10 Nov 2020 19:38:00 +0100
Continued implementing the "Run Server" function.
--- a/PluginFlask.e4p Mon Nov 09 20:00:56 2020 +0100 +++ b/PluginFlask.e4p Tue Nov 10 19:38:00 2020 +0100 @@ -15,6 +15,7 @@ <Eol index="1"/> <Sources> <Source>PluginProjectFlask.py</Source> + <Source>ProjectFlask/AnsiTools.py</Source> <Source>ProjectFlask/ConfigurationPage/FlaskPage.py</Source> <Source>ProjectFlask/ConfigurationPage/__init__.py</Source> <Source>ProjectFlask/Project.py</Source> @@ -23,8 +24,8 @@ <Source>__init__.py</Source> </Sources> <Forms> + <Form>ProjectFlask/ConfigurationPage/FlaskPage.ui</Form> <Form>ProjectFlask/RunServerDialog.ui</Form> - <Form>ProjectFlask/ConfigurationPage/FlaskPage.ui</Form> </Forms> <Others> <Other>.hgignore</Other>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ProjectFlask/AnsiTools.py Tue Nov 10 19:38:00 2020 +0100 @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing functions to work with ANSI escape sequences for colors. +""" + +from PyQt5.QtGui import QColor, QBrush + +# ANSI Colors (see https://en.wikipedia.org/wiki/ANSI_escape_code) +_AnsiColorSchemes = { + "Windows 7": { + 0: QBrush(QColor(0, 0, 0)), + 1: QBrush(QColor(128, 0, 0)), + 2: QBrush(QColor(0, 128, 0)), + 3: QBrush(QColor(128, 128, 0)), + 4: QBrush(QColor(0, 0, 128)), + 5: QBrush(QColor(128, 0, 128)), + 6: QBrush(QColor(0, 128, 128)), + 7: QBrush(QColor(192, 192, 192)), + 10: QBrush(QColor(128, 128, 128)), + 11: QBrush(QColor(255, 0, 0)), + 12: QBrush(QColor(0, 255, 0)), + 13: QBrush(QColor(255, 255, 0)), + 14: QBrush(QColor(0, 0, 255)), + 15: QBrush(QColor(255, 0, 255)), + 16: QBrush(QColor(0, 255, 255)), + 17: QBrush(QColor(255, 255, 255)), + }, + "Windows 10": { + 0: QBrush(QColor(12, 12, 12)), + 1: QBrush(QColor(197, 15, 31)), + 2: QBrush(QColor(19, 161, 14)), + 3: QBrush(QColor(193, 156, 0)), + 4: QBrush(QColor(0, 55, 218)), + 5: QBrush(QColor(136, 23, 152)), + 6: QBrush(QColor(58, 150, 221)), + 7: QBrush(QColor(204, 204, 204)), + 10: QBrush(QColor(118, 118, 118)), + 11: QBrush(QColor(231, 72, 86)), + 12: QBrush(QColor(22, 198, 12)), + 13: QBrush(QColor(249, 241, 165)), + 14: QBrush(QColor(59, 12, 255)), + 15: QBrush(QColor(180, 0, 158)), + 16: QBrush(QColor(97, 214, 214)), + 17: QBrush(QColor(242, 242, 242)), + }, + "PuTTY": { + 0: QBrush(QColor(0, 0, 0)), + 1: QBrush(QColor(187, 0, 0)), + 2: QBrush(QColor(0, 187, 0)), + 3: QBrush(QColor(187, 187, 0)), + 4: QBrush(QColor(0, 0, 187)), + 5: QBrush(QColor(187, 0, 187)), + 6: QBrush(QColor(0, 187, 187)), + 7: QBrush(QColor(187, 187, 187)), + 10: QBrush(QColor(85, 85, 85)), + 11: QBrush(QColor(255, 85, 85)), + 12: QBrush(QColor(85, 255, 85)), + 13: QBrush(QColor(255, 255, 85)), + 14: QBrush(QColor(85, 85, 255)), + 15: QBrush(QColor(255, 85, 255)), + 16: QBrush(QColor(85, 255, 255)), + 17: QBrush(QColor(255, 255, 255)), + }, + "xterm": { + 0: QBrush(QColor(0, 0, 0)), + 1: QBrush(QColor(205, 0, 0)), + 2: QBrush(QColor(0, 205, 0)), + 3: QBrush(QColor(205, 205, 0)), + 4: QBrush(QColor(0, 0, 238)), + 5: QBrush(QColor(205, 0, 205)), + 6: QBrush(QColor(0, 205, 205)), + 7: QBrush(QColor(229, 229, 229)), + 10: QBrush(QColor(127, 127, 127)), + 11: QBrush(QColor(255, 0, 0)), + 12: QBrush(QColor(0, 255, 0)), + 13: QBrush(QColor(255, 255, 0)), + 14: QBrush(QColor(0, 0, 255)), + 15: QBrush(QColor(255, 0, 255)), + 16: QBrush(QColor(0, 255, 255)), + 17: QBrush(QColor(255, 255, 255)), + }, + "Ubuntu": { + 0: QBrush(QColor(1, 1, 1)), + 1: QBrush(QColor(222, 56, 43)), + 2: QBrush(QColor(57, 181, 74)), + 3: QBrush(QColor(255, 199, 6)), + 4: QBrush(QColor(0, 11, 184)), + 5: QBrush(QColor(118, 38, 113)), + 6: QBrush(QColor(44, 181, 233)), + 7: QBrush(QColor(204, 204, 204)), + 10: QBrush(QColor(128, 128, 128)), + 11: QBrush(QColor(255, 0, 0)), + 12: QBrush(QColor(0, 255, 0)), + 13: QBrush(QColor(255, 255, 0)), + 14: QBrush(QColor(0, 0, 255)), + 15: QBrush(QColor(255, 0, 255)), + 16: QBrush(QColor(0, 255, 255)), + 17: QBrush(QColor(255, 255, 255)), + }, + "Ubuntu (dark)": { + 0: QBrush(QColor(96, 96, 96)), + 1: QBrush(QColor(235, 58, 45)), + 2: QBrush(QColor(57, 181, 74)), + 3: QBrush(QColor(255, 199, 29)), + 4: QBrush(QColor(25, 56, 230)), + 5: QBrush(QColor(200, 64, 193)), + 6: QBrush(QColor(48, 200, 255)), + 7: QBrush(QColor(204, 204, 204)), + 10: QBrush(QColor(128, 128, 128)), + 11: QBrush(QColor(255, 0, 0)), + 12: QBrush(QColor(0, 255, 0)), + 13: QBrush(QColor(255, 255, 0)), + 14: QBrush(QColor(0, 0, 255)), + 15: QBrush(QColor(255, 0, 255)), + 16: QBrush(QColor(0, 255, 255)), + 17: QBrush(QColor(255, 255, 255)), + }, + "Breeze (dark)": { + 0: QBrush(QColor(35, 38, 39)), + 1: QBrush(QColor(237, 21, 21)), + 2: QBrush(QColor(17, 209, 22)), + 3: QBrush(QColor(246, 116, 0)), + 4: QBrush(QColor(29, 153, 243)), + 5: QBrush(QColor(155, 89, 182)), + 6: QBrush(QColor(26, 188, 156)), + 7: QBrush(QColor(252, 252, 252)), + 10: QBrush(QColor(127, 140, 141)), + 11: QBrush(QColor(192, 57, 43)), + 12: QBrush(QColor(28, 220, 154)), + 13: QBrush(QColor(253, 188, 75)), + 14: QBrush(QColor(61, 174, 233)), + 15: QBrush(QColor(142, 68, 173)), + 16: QBrush(QColor(22, 160, 133)), + 17: QBrush(QColor(255, 255, 255)), + }, +} + + +def getAvailableColorSchemes(): + """ + Function to get a list of available color schemes. + + @return list containing the names of the supported color schemes + @rtype list of str + """ + return list(_AnsiColorSchemes.keys()) + + +def getColor(scheme, color): + """ + Function to get the brush for a given scheme and color. + + @paran scheme name of the color scheme + @type str + @param color ANSI color code + @type int + """ + try: + return _AnsiColorSchemes[scheme][color] + except KeyError: + return QBrush()
--- a/ProjectFlask/ConfigurationPage/FlaskPage.py Mon Nov 09 20:00:56 2020 +0100 +++ b/ProjectFlask/ConfigurationPage/FlaskPage.py Tue Nov 10 19:38:00 2020 +0100 @@ -19,6 +19,7 @@ import UI.PixmapCache +# TODO: add selection for the ANSI color scheme (see MicroPython) class FlaskPage(ConfigurationPageBase, Ui_FlaskPage): """ Class implementing the Flask configuration page.
--- a/ProjectFlask/RunServerDialog.py Mon Nov 09 20:00:56 2020 +0100 +++ b/ProjectFlask/RunServerDialog.py Tue Nov 10 19:38:00 2020 +0100 @@ -7,7 +7,9 @@ Module implementing a dialog to run the Flask server. """ -from PyQt5.QtCore import pyqtSlot, QProcess +import re + +from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton from E5Gui import E5MessageBox @@ -31,9 +33,13 @@ self.__process = None + self.__ansiRe = re.compile("(\\x1b\[\d+m)") + self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) + + self.__defaultTextFormat = self.outputEdit.currentCharFormat() def startServer(self, command, workdir, env): """ @@ -48,14 +54,13 @@ @return flag indicating a successful start @rtype bool """ - self.errorsEdit.hide() - self.__process = QProcess() - self.__process.readyReadStandardOutput.connect(self.__readStdOut) - self.__process.readyReadStandardError.connect(self.__readStdErr) - self.__process.finished.connect(self.__processFinished) self.__process.setProcessEnvironment(env) self.__process.setWorkingDirectory(workdir) + self.__process.setProcessChannelMode(QProcess.MergedChannels) + + self.__process.readyReadStandardOutput.connect(self.__readStdOut) + self.__process.finished.connect(self.__processFinished) self.__process.start(command, ["run"]) ok = self.__process.waitForStarted(10000) @@ -78,7 +83,8 @@ @param evt reference to the close event @type QCloseEvent """ - # TODO: not implemented yet + self.__cancel() + evt.accept() @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): @@ -100,27 +106,38 @@ """ if self.__process is not None: out = str(self.__process.readAllStandardOutput(), "utf-8") - self.outputEdit.appendPlainText(out) - - @pyqtSlot() - def __readStdErr(self): - """ - Private slot to add the server process errors to the errors pane. - """ - if self.__process is not None: - err = str(self.__process.readAllStandardError(), "utf-8") - self.errorsEdit.appendPlainText(err) - - self.errorsEdit.show() + for txt in self.__ansiRe.split(out): + if txt.startswith("\x1b["): + # TODO: process ANSI escape sequences for coloring + pass + else: + self.outputEdit.insertPlainText(txt) @pyqtSlot() def __processFinished(self): - # TODO: implement it - pass + """ + Private slot handling the finishing of the server process. + """ + if ( + self.__process is not None and + self.__process.state() != QProcess.NotRunning + ): + self.__process.terminate() + QTimer.singleShot(2000, self.__process.kill) + self.__process.waitForFinished(3000) + + self.__process = None + + + self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) + self.buttonBox.button(QDialogButtonBox.Close).setFocus( + Qt.OtherFocusReason) @pyqtSlot() def __cancel(self): """ Private slot to cancel the running server. """ - # TODO: not implemented yet + self.__processFinished()
--- a/ProjectFlask/RunServerDialog.ui Mon Nov 09 20:00:56 2020 +0100 +++ b/ProjectFlask/RunServerDialog.ui Tue Nov 10 19:38:00 2020 +0100 @@ -19,12 +19,6 @@ <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <widget class="QGroupBox" name="groupBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>2</verstretch> - </sizepolicy> - </property> <property name="title"> <string>Output</string> </property> @@ -49,37 +43,6 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_2"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - <property name="title"> - <string>Errors</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QPlainTextEdit" name="errorsEdit"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="lineWrapMode"> - <enum>QPlainTextEdit::NoWrap</enum> - </property> - <property name="readOnly"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Horizontal</enum>