Wed, 11 Nov 2020 20:03:21 +0100
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)