8 """ |
8 """ |
9 |
9 |
10 import re |
10 import re |
11 |
11 |
12 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer |
12 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer |
13 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton |
13 from PyQt5.QtGui import QTextCharFormat |
|
14 from PyQt5.QtWidgets import QDialog, QDialogButtonBox |
14 |
15 |
15 from E5Gui import E5MessageBox |
16 from E5Gui import E5MessageBox |
|
17 from E5Gui.E5Application import e5App |
16 |
18 |
17 from .Ui_RunServerDialog import Ui_RunServerDialog |
19 from .Ui_RunServerDialog import Ui_RunServerDialog |
18 |
20 |
|
21 from . import AnsiTools |
19 |
22 |
|
23 |
|
24 # TODO: should this be placed into the sidebar as a sidebar widget? |
20 class RunServerDialog(QDialog, Ui_RunServerDialog): |
25 class RunServerDialog(QDialog, Ui_RunServerDialog): |
21 """ |
26 """ |
22 Class implementing a dialog to run the Flask server. |
27 Class implementing a dialog to run the Flask server. |
23 """ |
28 """ |
24 def __init__(self, parent=None): |
29 def __init__(self, plugin, parent=None): |
25 """ |
30 """ |
26 Constructor |
31 Constructor |
27 |
32 |
|
33 @param plugin reference to the plug-in object |
|
34 @type PluginProjectFlask |
28 @param parent reference to the parent widget |
35 @param parent reference to the parent widget |
29 @type QWidget |
36 @type QWidget |
30 """ |
37 """ |
31 super(RunServerDialog, self).__init__(parent) |
38 super(RunServerDialog, self).__init__(parent) |
32 self.setupUi(self) |
39 self.setupUi(self) |
33 |
40 |
|
41 self.__plugin = plugin |
|
42 |
34 self.__process = None |
43 self.__process = None |
|
44 self.__serverUrl = "" |
35 |
45 |
36 self.__ansiRe = re.compile("(\\x1b\[\d+m)") |
46 self.__ansiRe = re.compile(r"""(\\x1b\[\d+m)""") |
|
47 |
|
48 self.__urlRe = re.compile(r""" * Running on ([^(]+) \(.*""") |
37 |
49 |
38 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
50 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
39 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
|
40 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
51 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
41 |
52 |
42 self.__defaultTextFormat = self.outputEdit.currentCharFormat() |
53 self.__defaultTextFormat = self.outputEdit.currentCharFormat() |
43 |
54 |
44 def startServer(self, command, workdir, env): |
55 def startServer(self, project, development=False): |
45 """ |
56 """ |
46 Public method to start the Flask server process. |
57 Public method to start the Flask server process. |
47 |
58 |
48 @param command path of the flask command |
59 @param project reference to the project object |
49 @type str |
60 @type Project |
50 @param workdir working directory for the Flask server |
61 @param development flag indicating development mode |
51 @type str |
62 @type bool |
52 @param env environment for the Flask server process |
63 @return flag indicating success |
53 @type QProcessEnvironment |
|
54 @return flag indicating a successful start |
|
55 @rtype bool |
64 @rtype bool |
56 """ |
65 """ |
57 self.__process = QProcess() |
66 workdir, env = project.prepareRuntimeEnvironment( |
58 self.__process.setProcessEnvironment(env) |
67 development=development) |
59 self.__process.setWorkingDirectory(workdir) |
68 if env is not None: |
60 self.__process.setProcessChannelMode(QProcess.MergedChannels) |
69 command = project.getFlaskCommand() |
61 |
70 |
62 self.__process.readyReadStandardOutput.connect(self.__readStdOut) |
71 self.__process = QProcess() |
63 self.__process.finished.connect(self.__processFinished) |
72 self.__process.setProcessEnvironment(env) |
64 |
73 self.__process.setWorkingDirectory(workdir) |
65 self.__process.start(command, ["run"]) |
74 self.__process.setProcessChannelMode(QProcess.MergedChannels) |
66 ok = self.__process.waitForStarted(10000) |
75 |
67 if not ok: |
76 self.__process.readyReadStandardOutput.connect(self.__readStdOut) |
68 E5MessageBox.critical( |
77 self.__process.finished.connect(self.__processFinished) |
69 None, |
78 |
70 self.tr("Run Flask Server"), |
79 args = ["run"] |
71 self.tr("""The Flask server process could not be started.""")) |
80 # if host: |
|
81 # args += ["--host", host] |
|
82 # if port: |
|
83 # args += ["--port", str(port)] |
|
84 |
|
85 self.__process.start(command, args) |
|
86 ok = self.__process.waitForStarted(10000) |
|
87 if not ok: |
|
88 E5MessageBox.critical( |
|
89 None, |
|
90 self.tr("Run Flask Server"), |
|
91 self.tr("""The Flask server process could not be""" |
|
92 """ started.""")) |
|
93 else: |
|
94 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
|
95 self.stopServerButton.setEnabled(True) |
|
96 self.stopServerButton.setDefault(True) |
72 else: |
97 else: |
73 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
98 ok = False |
74 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) |
|
75 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
|
76 |
99 |
77 return ok |
100 return ok |
78 |
101 |
79 def closeEvent(self, evt): |
102 def closeEvent(self, evt): |
80 """ |
103 """ |
81 Private method handling a close event. |
104 Protected method handling a close event. |
82 |
105 |
83 @param evt reference to the close event |
106 @param evt reference to the close event |
84 @type QCloseEvent |
107 @type QCloseEvent |
85 """ |
108 """ |
86 self.__cancel() |
109 self.on_stopServerButton_clicked() |
87 evt.accept() |
110 evt.accept() |
88 |
|
89 @pyqtSlot(QAbstractButton) |
|
90 def on_buttonBox_clicked(self, button): |
|
91 """ |
|
92 Private slot handling button presses. |
|
93 |
|
94 @param button button that was pressed |
|
95 @type QAbstractButton |
|
96 """ |
|
97 if button is self.buttonBox.button(QDialogButtonBox.Cancel): |
|
98 self.__cancel() |
|
99 elif button is self.buttonBox.button(QDialogButtonBox.Close): |
|
100 self.close() |
|
101 |
111 |
102 @pyqtSlot() |
112 @pyqtSlot() |
103 def __readStdOut(self): |
113 def __readStdOut(self): |
104 """ |
114 """ |
105 Private slot to add the server process output to the output pane. |
115 Private slot to add the server process output to the output pane. |
106 """ |
116 """ |
107 if self.__process is not None: |
117 if self.__process is not None: |
108 out = str(self.__process.readAllStandardOutput(), "utf-8") |
118 out = str(self.__process.readAllStandardOutput(), "utf-8") |
|
119 if not self.__serverUrl: |
|
120 urlMatch = self.__urlRe.search(out) |
|
121 if urlMatch: |
|
122 self.__serverUrl = urlMatch.group(1) |
|
123 self.startBrowserButton.setEnabled(True) |
|
124 |
109 for txt in self.__ansiRe.split(out): |
125 for txt in self.__ansiRe.split(out): |
110 if txt.startswith("\x1b["): |
126 if txt.startswith("\x1b["): |
111 # TODO: process ANSI escape sequences for coloring |
127 color = int(txt[2:-1]) # strip off ANSI command parts |
112 pass |
128 if color == 0: |
|
129 self.outputEdit.setCurrentCharFormat( |
|
130 self.__defaultTextFormat) |
|
131 elif 30 <= color <= 37: |
|
132 brush = AnsiTools.getColor( |
|
133 self.__plugin.getPreferences("AnsiColorScheme"), |
|
134 color - 30) |
|
135 if brush is not None: |
|
136 charFormat = QTextCharFormat( |
|
137 self.__defaultTextFormat) |
|
138 charFormat.setForeground(brush) |
|
139 self.outputEdit.setCurrentCharFormat(charFormat) |
113 else: |
140 else: |
114 self.outputEdit.insertPlainText(txt) |
141 self.outputEdit.insertPlainText(txt) |
115 |
142 |
116 @pyqtSlot() |
143 @pyqtSlot() |
117 def __processFinished(self): |
144 def __processFinished(self): |