ProjectFlask/RunServerDialog.py

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 9
79094fb72c18
permissions
-rw-r--r--

Continued implementing the "Run Server" function.

# -*- coding: utf-8 -*-

# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a dialog to run the Flask server.
"""

import re

from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer
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, 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.__plugin = plugin
        
        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.Close).setDefault(True)
        
        self.__defaultTextFormat = self.outputEdit.currentCharFormat()
    
    def startServer(self, project, development=False):
        """
        Public method to start the Flask server process.
        
        @param project reference to the project object
        @type Project
        @param development flag indicating development mode
        @type bool
        @return flag indicating success
        @rtype bool
        """
        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:
            ok = False
        
        return ok
    
    def closeEvent(self, evt):
        """
        Protected method handling a close event.
        
        @param evt reference to the close event
        @type QCloseEvent
        """
        self.on_stopServerButton_clicked()
        evt.accept()
    
    @pyqtSlot()
    def __readStdOut(self):
        """
        Private slot to add the server process output to the output pane.
        """
        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["):
                    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)
    
    @pyqtSlot()
    def __processFinished(self):
        """
        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.startBrowserButton.setEnabled(False)
        self.stopServerButton.setEnabled(False)
        self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
        self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
        self.buttonBox.button(QDialogButtonBox.Close).setFocus(
            Qt.OtherFocusReason)
    
    @pyqtSlot()
    def on_stopServerButton_clicked(self):
        """
        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)

eric ide

mercurial