|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to run a flask command and show its output. |
|
8 """ |
|
9 |
|
10 from PyQt5.QtCore import pyqtSlot, Qt, QProcess, QTimer |
|
11 from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QAbstractButton |
|
12 |
|
13 from E5Gui import E5MessageBox |
|
14 |
|
15 from ..Ui_FlaskCommandDialog import Ui_FlaskCommandDialog |
|
16 |
|
17 |
|
18 class PyBabelCommandDialog(QDialog, Ui_FlaskCommandDialog): |
|
19 """ |
|
20 Class implementing a dialog to run a flask command and show its output. |
|
21 """ |
|
22 def __init__(self, project, title="", msgSuccess="", msgError="", |
|
23 parent=None): |
|
24 """ |
|
25 Constructor |
|
26 |
|
27 @param project reference to the project object |
|
28 @type Project |
|
29 @param title window title of the dialog |
|
30 @type str |
|
31 @param msgSuccess success message to be shown |
|
32 @type str |
|
33 @param msgError message to be shown on error |
|
34 @type str |
|
35 @param parent reference to the parent widget |
|
36 @type QWidget |
|
37 """ |
|
38 super(PyBabelCommandDialog, self).__init__(parent) |
|
39 self.setupUi(self) |
|
40 |
|
41 if title: |
|
42 self.setWindowTitle(title) |
|
43 |
|
44 self.__project = project |
|
45 self.__successMessage = msgSuccess |
|
46 self.__errorMessage = msgError |
|
47 |
|
48 self.__process = None |
|
49 self.__argsLists = [] |
|
50 self.__workdir = "" |
|
51 |
|
52 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
|
53 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
|
54 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
|
55 |
|
56 def startCommand(self, command, args, workdir, clearOutput=True): |
|
57 """ |
|
58 Public method to start a pybabel command and show its output. |
|
59 |
|
60 @param command pybabel command to be run |
|
61 @type str |
|
62 @param args list of command line arguments for the command |
|
63 @type list of str |
|
64 @param workdir working directory for the command |
|
65 @type str |
|
66 @param clearOutput flag indicating to clear the output |
|
67 @type bool |
|
68 @return flag indicating a successful start |
|
69 @rtype bool |
|
70 """ |
|
71 babelCommand = self.__project.getBabelCommand() |
|
72 |
|
73 self.__process = QProcess() |
|
74 self.__process.setWorkingDirectory(workdir) |
|
75 self.__process.setProcessChannelMode(QProcess.MergedChannels) |
|
76 |
|
77 self.__process.readyReadStandardOutput.connect(self.__readStdOut) |
|
78 self.__process.finished.connect(self.__processFinished) |
|
79 |
|
80 if clearOutput: |
|
81 self.outputEdit.clear() |
|
82 |
|
83 babelArgs = [command] |
|
84 if args: |
|
85 babelArgs += args |
|
86 |
|
87 self.__process.start(babelCommand, babelArgs) |
|
88 ok = self.__process.waitForStarted(10000) |
|
89 if not ok: |
|
90 E5MessageBox.critical( |
|
91 None, |
|
92 self.tr("Execute PyBabel Command"), |
|
93 self.tr("""The pybabel process could not be started.""")) |
|
94 else: |
|
95 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
|
96 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
|
97 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) |
|
98 self.buttonBox.button(QDialogButtonBox.Cancel).setFocus( |
|
99 Qt.OtherFocusReason) |
|
100 |
|
101 return ok |
|
102 |
|
103 def startBatchCommand(self, argsLists, workdir): |
|
104 """ |
|
105 Public method to start a pybabel command repeatedly with a list of |
|
106 arguments and show the output. |
|
107 |
|
108 @param argsLists list of command line arguments for the batch commands |
|
109 @type list of lists of str |
|
110 @param workdir working directory for the command |
|
111 @type str |
|
112 @return flag indicating a successful start of the first process |
|
113 @rtype bool |
|
114 """ |
|
115 self.__argsLists = argsLists[:] |
|
116 self.__workdir = workdir |
|
117 |
|
118 # start the first process |
|
119 args = self.__argsLists.pop(0) |
|
120 res = self.startCommand(args[0], args[1:], workdir) |
|
121 if not res: |
|
122 self.__argsLists = [] |
|
123 |
|
124 return res |
|
125 |
|
126 def closeEvent(self, evt): |
|
127 """ |
|
128 Protected method handling the close event of the dialog. |
|
129 |
|
130 @param evt reference to the close event object |
|
131 @type QCloseEvent |
|
132 """ |
|
133 self.__argsLists = [] |
|
134 self.__cancelProcess() |
|
135 evt.accept() |
|
136 |
|
137 @pyqtSlot() |
|
138 def __readStdOut(self): |
|
139 """ |
|
140 Private slot to add the server process output to the output pane. |
|
141 """ |
|
142 if self.__process is not None: |
|
143 out = str(self.__process.readAllStandardOutput(), "utf-8") |
|
144 self.outputEdit.insertPlainText(out) |
|
145 |
|
146 def __processFinished(self, exitCode, exitStatus): |
|
147 """ |
|
148 Private slot connected to the finished signal. |
|
149 |
|
150 @param exitCode exit code of the process |
|
151 @type int |
|
152 @param exitStatus exit status of the process |
|
153 @type QProcess.ExitStatus |
|
154 """ |
|
155 normal = (exitStatus == QProcess.NormalExit) and (exitCode == 0) |
|
156 self.__cancelProcess() |
|
157 |
|
158 if self.__argsLists: |
|
159 args = self.__argsLists.pop(0) |
|
160 self.startCommand(args[0], args[1:], self.__workdir, |
|
161 clearOutput=False) |
|
162 return |
|
163 |
|
164 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
|
165 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
|
166 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
|
167 self.buttonBox.button(QDialogButtonBox.Close).setFocus( |
|
168 Qt.OtherFocusReason) |
|
169 |
|
170 if normal and self.__successMessage: |
|
171 self.outputEdit.insertPlainText(self.__successMessage) |
|
172 elif not normal and self.__errorMessage: |
|
173 self.outputEdit.insertPlainText(self.__errorMessage) |
|
174 |
|
175 @pyqtSlot() |
|
176 def __cancelProcess(self): |
|
177 """ |
|
178 Private slot to terminate the current process. |
|
179 """ |
|
180 if ( |
|
181 self.__process is not None and |
|
182 self.__process.state() != QProcess.NotRunning |
|
183 ): |
|
184 self.__process.terminate() |
|
185 QTimer.singleShot(2000, self.__process.kill) |
|
186 self.__process.waitForFinished(3000) |
|
187 |
|
188 self.__process = None |
|
189 |
|
190 @pyqtSlot(QAbstractButton) |
|
191 def on_buttonBox_clicked(self, button): |
|
192 """ |
|
193 Private slot handling presses of the button box buttons. |
|
194 |
|
195 @param button reference to the button been clicked |
|
196 @type QAbstractButton |
|
197 """ |
|
198 if button is self.buttonBox.button(QDialogButtonBox.Close): |
|
199 self.close() |
|
200 elif button is self.buttonBox.button(QDialogButtonBox.Cancel): |
|
201 self.__argsLists = [] |
|
202 self.__cancelProcess() |