Continued implementing the "Run Server" function.

Wed, 11 Nov 2020 20:03:21 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 11 Nov 2020 20:03:21 +0100
changeset 6
d491ccab7343
parent 5
550e5ea385cb
child 7
a140b2a8ba93

Continued implementing the "Run Server" function.

PluginFlask.e4p file | annotate | diff | comparison | revisions
PluginProjectFlask.py file | annotate | diff | comparison | revisions
ProjectFlask/AnsiTools.py file | annotate | diff | comparison | revisions
ProjectFlask/ConfigurationPage/FlaskPage.py file | annotate | diff | comparison | revisions
ProjectFlask/ConfigurationPage/FlaskPage.ui file | annotate | diff | comparison | revisions
ProjectFlask/Project.py file | annotate | diff | comparison | revisions
ProjectFlask/RunServerDialog.py file | annotate | diff | comparison | revisions
ProjectFlask/RunServerDialog.ui file | annotate | diff | comparison | revisions
--- a/PluginFlask.e4p	Tue Nov 10 19:38:00 2020 +0100
+++ b/PluginFlask.e4p	Wed Nov 11 20:03:21 2020 +0100
@@ -162,4 +162,294 @@
     <FiletypeAssociation pattern="Ui_*.py" type="__IGNORE__"/>
     <FiletypeAssociation pattern="makefile" type="OTHERS"/>
   </FiletypeAssociations>
+  <Checkers>
+    <CheckersParams>
+      <dict>
+        <key>
+          <string>Pep8Checker</string>
+        </key>
+        <value>
+          <dict>
+            <key>
+              <string>AnnotationsChecker</string>
+            </key>
+            <value>
+              <dict>
+                <key>
+                  <string>MaximumComplexity</string>
+                </key>
+                <value>
+                  <int>3</int>
+                </value>
+                <key>
+                  <string>MinimumCoverage</string>
+                </key>
+                <value>
+                  <int>75</int>
+                </value>
+              </dict>
+            </value>
+            <key>
+              <string>BlankLines</string>
+            </key>
+            <value>
+              <tuple>
+                <int>2</int>
+                <int>1</int>
+              </tuple>
+            </value>
+            <key>
+              <string>BuiltinsChecker</string>
+            </key>
+            <value>
+              <dict>
+                <key>
+                  <string>bytes</string>
+                </key>
+                <value>
+                  <list>
+                    <string>unicode</string>
+                  </list>
+                </value>
+                <key>
+                  <string>chr</string>
+                </key>
+                <value>
+                  <list>
+                    <string>unichr</string>
+                  </list>
+                </value>
+                <key>
+                  <string>str</string>
+                </key>
+                <value>
+                  <list>
+                    <string>unicode</string>
+                  </list>
+                </value>
+              </dict>
+            </value>
+            <key>
+              <string>CommentedCodeChecker</string>
+            </key>
+            <value>
+              <dict>
+                <key>
+                  <string>Aggressive</string>
+                </key>
+                <value>
+                  <bool>False</bool>
+                </value>
+              </dict>
+            </value>
+            <key>
+              <string>CopyrightAuthor</string>
+            </key>
+            <value>
+              <string></string>
+            </value>
+            <key>
+              <string>CopyrightMinFileSize</string>
+            </key>
+            <value>
+              <int>0</int>
+            </value>
+            <key>
+              <string>DocstringType</string>
+            </key>
+            <value>
+              <string>eric</string>
+            </value>
+            <key>
+              <string>EnabledCheckerCategories</string>
+            </key>
+            <value>
+              <string>C, D, E, M, N, S, W</string>
+            </value>
+            <key>
+              <string>ExcludeFiles</string>
+            </key>
+            <value>
+              <string>*/Ui_*.py,</string>
+            </value>
+            <key>
+              <string>ExcludeMessages</string>
+            </key>
+            <value>
+              <string>C101,E265,E266,E305,E402,M201,M301,M302,M303,M304,M305,M306,M307,M308,M311,M312,M313,M314,M315,M321,M701,M702,M811,M834,N802,N803,N807,N808,N821,W293,W504</string>
+            </value>
+            <key>
+              <string>FixCodes</string>
+            </key>
+            <value>
+              <string></string>
+            </value>
+            <key>
+              <string>FixIssues</string>
+            </key>
+            <value>
+              <bool>False</bool>
+            </value>
+            <key>
+              <string>FutureChecker</string>
+            </key>
+            <value>
+              <string>unicode_literals</string>
+            </value>
+            <key>
+              <string>HangClosing</string>
+            </key>
+            <value>
+              <bool>False</bool>
+            </value>
+            <key>
+              <string>IncludeMessages</string>
+            </key>
+            <value>
+              <string></string>
+            </value>
+            <key>
+              <string>LineComplexity</string>
+            </key>
+            <value>
+              <int>20</int>
+            </value>
+            <key>
+              <string>LineComplexityScore</string>
+            </key>
+            <value>
+              <int>10</int>
+            </value>
+            <key>
+              <string>MaxCodeComplexity</string>
+            </key>
+            <value>
+              <int>10</int>
+            </value>
+            <key>
+              <string>MaxDocLineLength</string>
+            </key>
+            <value>
+              <int>79</int>
+            </value>
+            <key>
+              <string>MaxLineLength</string>
+            </key>
+            <value>
+              <int>79</int>
+            </value>
+            <key>
+              <string>NoFixCodes</string>
+            </key>
+            <value>
+              <string>E501</string>
+            </value>
+            <key>
+              <string>RepeatMessages</string>
+            </key>
+            <value>
+              <bool>True</bool>
+            </value>
+            <key>
+              <string>SecurityChecker</string>
+            </key>
+            <value>
+              <dict>
+                <key>
+                  <string>CheckTypedException</string>
+                </key>
+                <value>
+                  <bool>False</bool>
+                </value>
+                <key>
+                  <string>HardcodedTmpDirectories</string>
+                </key>
+                <value>
+                  <list>
+                    <string>/tmp</string>
+                    <string>/var/tmp</string>
+                    <string>/dev/shm</string>
+                    <string>~/tmp</string>
+                  </list>
+                </value>
+                <key>
+                  <string>InsecureHashes</string>
+                </key>
+                <value>
+                  <list>
+                    <string>md4</string>
+                    <string>md5</string>
+                    <string>sha</string>
+                    <string>sha1</string>
+                  </list>
+                </value>
+                <key>
+                  <string>InsecureSslProtocolVersions</string>
+                </key>
+                <value>
+                  <list>
+                    <string>PROTOCOL_SSLv2</string>
+                    <string>SSLv2_METHOD</string>
+                    <string>SSLv23_METHOD</string>
+                    <string>PROTOCOL_SSLv3</string>
+                    <string>PROTOCOL_TLSv1</string>
+                    <string>SSLv3_METHOD</string>
+                    <string>TLSv1_METHOD</string>
+                  </list>
+                </value>
+                <key>
+                  <string>WeakKeySizeDsaHigh</string>
+                </key>
+                <value>
+                  <string>1024</string>
+                </value>
+                <key>
+                  <string>WeakKeySizeDsaMedium</string>
+                </key>
+                <value>
+                  <string>2048</string>
+                </value>
+                <key>
+                  <string>WeakKeySizeEcHigh</string>
+                </key>
+                <value>
+                  <string>160</string>
+                </value>
+                <key>
+                  <string>WeakKeySizeEcMedium</string>
+                </key>
+                <value>
+                  <string>224</string>
+                </value>
+                <key>
+                  <string>WeakKeySizeRsaHigh</string>
+                </key>
+                <value>
+                  <string>1024</string>
+                </value>
+                <key>
+                  <string>WeakKeySizeRsaMedium</string>
+                </key>
+                <value>
+                  <string>2048</string>
+                </value>
+              </dict>
+            </value>
+            <key>
+              <string>ShowIgnored</string>
+            </key>
+            <value>
+              <bool>False</bool>
+            </value>
+            <key>
+              <string>ValidEncodings</string>
+            </key>
+            <value>
+              <string>latin-1, utf-8</string>
+            </value>
+          </dict>
+        </value>
+      </dict>
+    </CheckersParams>
+  </Checkers>
 </Project>
--- a/PluginProjectFlask.py	Tue Nov 10 19:38:00 2020 +0100
+++ b/PluginProjectFlask.py	Wed Nov 11 20:03:21 2020 +0100
@@ -154,6 +154,12 @@
 #            
 #            "RecentNumberDatabaseNames": 10,
         }
+        if isWindowsPlatform():
+            self.__defaults["AnsiColorScheme"] = "Windows 10"
+        elif isMacPlatform():
+            self.__defaults["AnsiColorScheme"] = "xterm"
+        else:
+            self.__defaults["AnsiColorScheme"] = "Ubuntu"
 #        if isWindowsPlatform():
 #            self.__defaults["ConsoleCommandNoClose"] = "cmd.exe /k"
 #            self.__defaults["ConsoleCommand"] = "cmd.exe /c"
--- a/ProjectFlask/AnsiTools.py	Tue Nov 10 19:38:00 2020 +0100
+++ b/ProjectFlask/AnsiTools.py	Wed Nov 11 20:03:21 2020 +0100
@@ -20,14 +20,6 @@
         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)),
@@ -38,14 +30,6 @@
         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)),
@@ -56,14 +40,6 @@
         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)),
@@ -74,14 +50,6 @@
         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)),
@@ -92,14 +60,6 @@
         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)),
@@ -110,14 +70,6 @@
         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)),
@@ -128,14 +80,6 @@
         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)),
     },
 }
 
@@ -154,12 +98,14 @@
     """
     Function to get the brush for a given scheme and color.
     
-    @paran scheme name of the color scheme
+    @param scheme name of the color scheme
     @type str
     @param color ANSI color code
     @type int
+    @return brush for the given parameters or None in case of error
+    @rtype QBrush or None
     """
     try:
         return _AnsiColorSchemes[scheme][color]
     except KeyError:
-        return QBrush()
+        return None
--- a/ProjectFlask/ConfigurationPage/FlaskPage.py	Tue Nov 10 19:38:00 2020 +0100
+++ b/ProjectFlask/ConfigurationPage/FlaskPage.py	Wed Nov 11 20:03:21 2020 +0100
@@ -18,6 +18,8 @@
 
 import UI.PixmapCache
 
+from .. import AnsiTools
+
 
 # TODO: add selection for the ANSI color scheme (see MicroPython)
 class FlaskPage(ConfigurationPageBase, Ui_FlaskPage):
@@ -34,6 +36,9 @@
         self.setupUi(self)
         self.setObjectName("FlaskPage")
         
+        self.colorSchemeComboBox.addItems(
+            sorted(AnsiTools.getAvailableColorSchemes()))
+        
         self.__plugin = plugin
         
         self.urlResetButton.setIcon(
@@ -57,6 +62,10 @@
                 index = 0
             self.py3VenvNameComboBox.setCurrentIndex(index)
         
+        self.colorSchemeComboBox.setCurrentIndex(
+            self.colorSchemeComboBox.findText(
+                self.__plugin.getPreferences("AnsiColorScheme")))
+        
         self.urlEdit.setText(
             self.__plugin.getPreferences("FlaskDocUrl"))
     
@@ -72,6 +81,10 @@
             self.py3VenvNameComboBox.currentText())
         
         self.__plugin.setPreferences(
+            "AnsiColorScheme",
+            self.colorSchemeComboBox.currentText())
+        
+        self.__plugin.setPreferences(
             "FlaskDocUrl", self.urlEdit.text())
     
     @pyqtSlot()
--- a/ProjectFlask/ConfigurationPage/FlaskPage.ui	Tue Nov 10 19:38:00 2020 +0100
+++ b/ProjectFlask/ConfigurationPage/FlaskPage.ui	Wed Nov 11 20:03:21 2020 +0100
@@ -53,7 +53,7 @@
    <item>
     <widget class="QGroupBox" name="flaskVirtualEnvironmentPy3Group">
      <property name="title">
-      <string>Flak Virtual Environment</string>
+      <string>Flask Virtual Environment</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout_2">
       <item>
@@ -84,9 +84,38 @@
     </widget>
    </item>
    <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Server Output</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Color Scheme for ANSI Escape Codes:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="colorSchemeComboBox">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Select the color scheme to be applied for ANSI color escape codes</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
     <widget class="QGroupBox" name="flaskDocumentationGroup">
      <property name="title">
-      <string>Pyramid Documentation</string>
+      <string>Flask Documentation</string>
      </property>
      <layout class="QHBoxLayout" name="horizontalLayout_2">
       <item>
@@ -128,6 +157,14 @@
    </item>
   </layout>
  </widget>
+ <tabstops>
+  <tabstop>externalBrowserCheckBox</tabstop>
+  <tabstop>py3VenvNameComboBox</tabstop>
+  <tabstop>py3VenvNamesReloadButton</tabstop>
+  <tabstop>colorSchemeComboBox</tabstop>
+  <tabstop>urlEdit</tabstop>
+  <tabstop>urlResetButton</tabstop>
+ </tabstops>
  <resources/>
  <connections/>
 </ui>
--- a/ProjectFlask/Project.py	Tue Nov 10 19:38:00 2020 +0100
+++ b/ProjectFlask/Project.py	Wed Nov 11 20:03:21 2020 +0100
@@ -85,6 +85,20 @@
         self.runServerAct.triggered.connect(self.__runServer)
         self.actions.append(self.runServerAct)
         
+        self.runDevServerAct = E5Action(
+            self.tr('Run Development Server'),
+            self.tr('Run &Development Server'),
+            0, 0,
+            self, 'flask_run_dev_server')
+        self.runDevServerAct.setStatusTip(self.tr(
+            'Starts the Flask Web server in development mode'))
+        self.runDevServerAct.setWhatsThis(self.tr(
+            """<b>Run Development Server</b>"""
+            """<p>Starts the Flask Web server in development mode.</p>"""
+        ))
+        self.runDevServerAct.triggered.connect(self.__runDevelopmentServer)
+        self.actions.append(self.runDevServerAct)
+        
         ##################################
         ## documentation action below   ##
         ##################################
@@ -134,6 +148,7 @@
         menu.setTearOffEnabled(True)
         
         menu.addAction(self.runServerAct)
+        menu.addAction(self.runDevServerAct)
         menu.addSeparator()
         menu.addAction(self.documentationAct)
         menu.addSeparator()
@@ -175,6 +190,8 @@
         """
         Public method to handle the closing of a project.
         """
+        if self.__serverDialog is not None:
+            self.__serverDialog.close()
 ##        if self.__serverProc is not None:
 ##            self.__serverProcFinished()
     
@@ -384,32 +401,30 @@
     ## slots below implement run functions
     ##################################################################
     
-    # TODO: does the flask server support logging?
-    def __runServer(self, logging=False):
+    @pyqtSlot()
+    def __runServer(self, development=False):
         """
         Private slot to start the Flask Web server.
         
-        @param logging flag indicating to enable logging
+        @param development flag indicating development mode
         @type bool
         """
-        # TODO: implement this (flask run)
-        workdir, env = self.prepareRuntimeEnvironment()
-        if env is not None:
-            cmd = self.getFlaskCommand()
-            
-            dlg = RunServerDialog()
-            if dlg.startServer(cmd, workdir, env):
-                dlg.show()
-                self.__serverDialog = dlg
+        if self.__serverDialog is not None:
+            self.__serverDialog.close()
+        
+        dlg = RunServerDialog(self.__plugin)
+        if dlg.startServer(self, development=development):
+            dlg.show()
+            self.__serverDialog = dlg
     
-    def __runDevelopmentServer(self, logging=False):
+    @pyqtSlot()
+    def __runDevelopmentServer(self):
         """
         Private slot to start the Flask Web server in development mode.
-        
-        @param logging flag indicating to enable logging
-        @type bool
         """
-        # TODO: implement this (flask run with FLASK_ENV=development)
+        self.__runServer(development=True)
+    
+    # TODO: add method to start a server with parameters
     
 ##    def __serverProcFinished(self):
 ##        """
--- a/ProjectFlask/RunServerDialog.py	Tue Nov 10 19:38:00 2020 +0100
+++ b/ProjectFlask/RunServerDialog.py	Wed Nov 11 20:03:21 2020 +0100
@@ -10,95 +10,105 @@
 import re
 
 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer
-from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton
+from PyQt5.QtGui import QTextCharFormat
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox
 
 from E5Gui import E5MessageBox
+from E5Gui.E5Application import e5App
 
 from .Ui_RunServerDialog import Ui_RunServerDialog
 
+from . import AnsiTools
 
+
+# TODO: should this be placed into the sidebar as a sidebar widget?
 class RunServerDialog(QDialog, Ui_RunServerDialog):
     """
     Class implementing a dialog to run the Flask server.
     """
-    def __init__(self, parent=None):
+    def __init__(self, plugin, parent=None):
         """
         Constructor
         
+        @param plugin reference to the plug-in object
+        @type PluginProjectFlask
         @param parent reference to the parent widget
         @type QWidget
         """
         super(RunServerDialog, self).__init__(parent)
         self.setupUi(self)
         
-        self.__process = None
+        self.__plugin = plugin
         
-        self.__ansiRe = re.compile("(\\x1b\[\d+m)")
+        self.__process = None
+        self.__serverUrl = ""
+        
+        self.__ansiRe = re.compile(r"""(\\x1b\[\d+m)""")
+        
+        self.__urlRe = re.compile(r""" * Running on ([^(]+) \(.*""")
         
         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):
+    def startServer(self, project, development=False):
         """
         Public method to start the Flask server process.
         
-        @param command path of the flask command
-        @type str
-        @param workdir working directory for the Flask server
-        @type str
-        @param env environment for the Flask server process
-        @type QProcessEnvironment
-        @return flag indicating a successful start
+        @param project reference to the project object
+        @type Project
+        @param development flag indicating development mode
+        @type bool
+        @return flag indicating success
         @rtype bool
         """
-        self.__process = QProcess()
-        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)
-        if not ok:
-            E5MessageBox.critical(
-                None,
-                self.tr("Run Flask Server"),
-                self.tr("""The Flask server process could not be started."""))
+        workdir, env = project.prepareRuntimeEnvironment(
+            development=development)
+        if env is not None:
+            command = project.getFlaskCommand()
+            
+            self.__process = QProcess()
+            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)
+            
+            args = ["run"]
+#            if host:
+#                args += ["--host", host]
+#            if port:
+#                args += ["--port", str(port)]
+            
+            self.__process.start(command, args)
+            ok = self.__process.waitForStarted(10000)
+            if not ok:
+                E5MessageBox.critical(
+                    None,
+                    self.tr("Run Flask Server"),
+                    self.tr("""The Flask server process could not be"""
+                            """ started."""))
+            else:
+                self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
+                self.stopServerButton.setEnabled(True)
+                self.stopServerButton.setDefault(True)
         else:
-            self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
-            self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True)
-            self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
+            ok = False
         
         return ok
     
     def closeEvent(self, evt):
         """
-        Private method handling a close event.
+        Protected method handling a close event.
         
         @param evt reference to the close event
         @type QCloseEvent
         """
-        self.__cancel()
+        self.on_stopServerButton_clicked()
         evt.accept()
     
-    @pyqtSlot(QAbstractButton)
-    def on_buttonBox_clicked(self, button):
-        """
-        Private slot handling button presses.
-        
-        @param button button that was pressed
-        @type QAbstractButton
-        """
-        if button is self.buttonBox.button(QDialogButtonBox.Cancel):
-            self.__cancel()
-        elif button is self.buttonBox.button(QDialogButtonBox.Close):
-            self.close()
-    
     @pyqtSlot()
     def __readStdOut(self):
         """
@@ -106,10 +116,27 @@
         """
         if self.__process is not None:
             out = str(self.__process.readAllStandardOutput(), "utf-8")
+            if not self.__serverUrl:
+                urlMatch = self.__urlRe.search(out)
+                if urlMatch:
+                    self.__serverUrl = urlMatch.group(1)
+                    self.startBrowserButton.setEnabled(True)
+            
             for txt in self.__ansiRe.split(out):
                 if txt.startswith("\x1b["):
-                    # TODO: process ANSI escape sequences for coloring
-                    pass
+                    color = int(txt[2:-1])      # strip off ANSI command parts
+                    if color == 0:
+                        self.outputEdit.setCurrentCharFormat(
+                            self.__defaultTextFormat)
+                    elif 30 <= color <= 37:
+                        brush = AnsiTools.getColor(
+                            self.__plugin.getPreferences("AnsiColorScheme"),
+                            color - 30)
+                        if brush is not None:
+                            charFormat = QTextCharFormat(
+                                self.__defaultTextFormat)
+                            charFormat.setForeground(brush)
+                            self.outputEdit.setCurrentCharFormat(charFormat)
                 else:
                     self.outputEdit.insertPlainText(txt)
     
@@ -128,16 +155,28 @@
         
         self.__process = None
         
-        
+        self.startBrowserButton.setEnabled(False)
+        self.stopServerButton.setEnabled(False)
         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):
+    def on_stopServerButton_clicked(self):
         """
-        Private slot to cancel the running server.
+        Private slot to stop the running server.
         """
         self.__processFinished()
+    
+    @pyqtSlot()
+    def on_startBrowserButton_clicked(self):
+        """
+        Private slot to start a web browser with the server URL.
+        """
+        if self.__plugin.getPreferences("UseExternalBrowser"):
+            import webbrowser
+            webbrowser.open(self.__serverUrl)
+        else:
+            e5App().getObject("UserInterface").launchHelpViewer(
+                self.__serverUrl)
--- a/ProjectFlask/RunServerDialog.ui	Tue Nov 10 19:38:00 2020 +0100
+++ b/ProjectFlask/RunServerDialog.ui	Wed Nov 11 20:03:21 2020 +0100
@@ -16,7 +16,7 @@
   <property name="sizeGripEnabled">
    <bool>true</bool>
   </property>
-  <layout class="QVBoxLayout" name="verticalLayout_3">
+  <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
     <widget class="QGroupBox" name="groupBox">
      <property name="title">
@@ -43,17 +43,82 @@
     </widget>
    </item>
    <item>
-    <widget class="QDialogButtonBox" name="buttonBox">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Close</set>
-     </property>
-    </widget>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="startBrowserButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Press to start a web browser with the server URL.</string>
+       </property>
+       <property name="text">
+        <string>Start Browser</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="stopServerButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="toolTip">
+        <string>Press to stop the running server</string>
+       </property>
+       <property name="text">
+        <string>Stop Server</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Close</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
    </item>
   </layout>
  </widget>
+ <tabstops>
+  <tabstop>outputEdit</tabstop>
+  <tabstop>startBrowserButton</tabstop>
+  <tabstop>stopServerButton</tabstop>
+ </tabstops>
  <resources/>
- <connections/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>RunServerDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>505</x>
+     <y>474</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>593</x>
+     <y>419</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
 </ui>

eric ide

mercurial