ProjectPyramid/PyramidDialog.py

changeset 2
e691c51ab655
child 3
76c45d31d462
equal deleted inserted replaced
1:012c492a9bd6 2:e691c51ab655
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2012 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog starting a process and showing its output.
8 """
9
10 import os
11
12 from PyQt4.QtCore import QProcess, QTimer, pyqtSlot, Qt, QCoreApplication
13 from PyQt4.QtGui import QDialog, QDialogButtonBox, QLineEdit, QTextEdit
14
15 from E5Gui import E5MessageBox
16
17 from .Ui_PyramidDialog import Ui_PyramidDialog
18
19 import Preferences
20 from Globals import isWindowsPlatform
21
22
23 class PyramidDialog(QDialog, Ui_PyramidDialog):
24 """
25 Class implementing a dialog starting a process and showing its output.
26
27 It starts a QProcess and displays a dialog that
28 shows the output of the process. The dialog is modal,
29 which causes a synchronized execution of the process.
30 """
31 def __init__(self, text, fixed = False, linewrap = True,
32 msgSuccess = None, msgError = None,
33 parent = None):
34 """
35 Constructor
36
37 @param text text to be shown by the label (string or QString)
38 @keyparam fixed flag indicating a fixed font should be used (boolean)
39 @keyparam linewrap flag indicating to wrap long lines (boolean)
40 @keyparam msgSuccess optional string to show upon successful execution
41 (string or QString)
42 @keyparam msgError optional string to show upon unsuccessful execution
43 (string or QString)
44 @keyparam parent parent widget (QWidget)
45 """
46 super().__init__(parent)
47 self.setupUi(self)
48
49 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
50 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
51
52 self.proc = None
53 self.argsLists = []
54 self.workingDir = None
55 self.msgSuccess = msgSuccess
56 self.msgError = msgError
57
58 self.outputGroup.setTitle(text)
59
60 if fixed:
61 if isWindowsPlatform():
62 self.resultbox.setFontFamily("Lucida Console")
63 else:
64 self.resultbox.setFontFamily("Monospace")
65
66 if not linewrap:
67 self.resultbox.setLineWrapMode(QTextEdit.NoWrap)
68
69 self.show()
70 QCoreApplication.processEvents()
71
72 def finish(self):
73 """
74 Public slot called when the process finished or the user pressed the button.
75 """
76 if self.proc is not None and \
77 self.proc.state() != QProcess.NotRunning:
78 self.proc.terminate()
79 QTimer.singleShot(2000, self.proc.kill)
80 self.proc.waitForFinished(3000)
81
82 self.inputGroup.setEnabled(False)
83 self.inputGroup.hide()
84
85 self.proc = None
86
87 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
88 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
89 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
90 self.buttonBox.button(QDialogButtonBox.Close).setFocus(Qt.OtherFocusReason)
91
92 if self.argsLists:
93 args = self.argsLists[0][:]
94 del self.argsLists[0]
95 self.startProcess(args, self.workingDir)
96
97 def on_buttonBox_clicked(self, button):
98 """
99 Private slot called by a button of the button box clicked.
100
101 @param button button that was clicked (QAbstractButton)
102 """
103 if button == self.buttonBox.button(QDialogButtonBox.Close):
104 self.close()
105 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
106 self.finish()
107
108 def __procFinished(self, exitCode, exitStatus):
109 """
110 Private slot connected to the finished signal.
111
112 @param exitCode exit code of the process (integer)
113 @param exitStatus exit status of the process (QProcess.ExitStatus)
114 """
115 self.normal = (exitStatus == QProcess.NormalExit) and (exitCode == 0)
116 self.finish()
117
118 if self.normal and self.msgSuccess:
119 self.resultbox.insertPlainText(self.msgSuccess)
120 elif not self.normal and self.msgError:
121 self.resultbox.insertPlainText(self.msgError)
122 self.resultbox.ensureCursorVisible()
123
124 def startProcess(self, command, args, workingDir=None, showArgs=True):
125 """
126 Public slot used to start the process.
127
128 @param command command to start (string)
129 @param args list of arguments for the process (list of strings)
130 @keyparam workingDir working directory for the process (string)
131 @keyparam showArgs flag indicating to show the arguments (boolean)
132 @return flag indicating a successful start of the process
133 """
134 self.errorGroup.hide()
135 self.normal = False
136 self.intercept = False
137
138 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
139 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True)
140 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
141 self.buttonBox.button(QDialogButtonBox.Cancel).setFocus(Qt.OtherFocusReason)
142
143 if showArgs:
144 self.resultbox.append(command + ' ' + ' '.join(args))
145 self.resultbox.append('')
146
147 self.proc = QProcess()
148
149 self.proc.finished.connect(self.__procFinished)
150 self.proc.readyReadStandardOutput.connect(self.__readStdout)
151 self.proc.readyReadStandardError.connect(self.__readStderr)
152
153 if workingDir:
154 self.proc.setWorkingDirectory(workingDir)
155 self.proc.start(command, args)
156 procStarted = self.proc.waitForStarted()
157 if not procStarted:
158 self.buttonBox.setFocus()
159 self.inputGroup.setEnabled(False)
160 E5MessageBox.critical(self,
161 self.trUtf8('Process Generation Error'),
162 self.trUtf8(
163 'The process {0} could not be started. '
164 'Ensure, that it is in the search path.'
165 ).format(command))
166 else:
167 self.inputGroup.setEnabled(True)
168 self.inputGroup.show()
169 return procStarted
170
171 def startBatchProcesses(self, argsLists, workingDir = None):
172 """
173 Public slot used to start a batch of processes.
174
175 @param argsLists list of lists of arguments for the processes
176 (list of list of string)
177 @param workingDir working directory for the process (string)
178 @return flag indicating a successful start of the first process (boolean)
179 """
180 self.argsLists = argsLists[:]
181 self.workingDir = workingDir
182
183 # start the first process
184 args = self.argsLists[0][:]
185 del self.argsLists[0]
186 res = self.startProcess(args, self.workingDir)
187 if not res:
188 self.argsLists = []
189
190 return res
191
192 def normalExit(self):
193 """
194 Public method to check for a normal process termination.
195
196 @return flag indicating normal process termination (boolean)
197 """
198 return self.normal
199
200 def normalExitWithoutErrors(self):
201 """
202 Public method to check for a normal process termination without
203 error messages.
204
205 @return flag indicating normal process termination (boolean)
206 """
207 return self.normal and self.errors.toPlainText() == ""
208
209 def __readStdout(self):
210 """
211 Private slot to handle the readyReadStandardOutput signal.
212
213 It reads the output of the process, formats it and inserts it into
214 the contents pane.
215 """
216 if self.proc is not None:
217 out = str(self.proc.readAllStandardOutput(),
218 Preferences.getSystem("IOEncoding"),
219 'replace')
220 self.resultbox.insertPlainText(out)
221 self.resultbox.ensureCursorVisible()
222
223 QCoreApplication.processEvents()
224
225 def __readStderr(self):
226 """
227 Private slot to handle the readyReadStandardError signal.
228
229 It reads the error output of the process and inserts it into the
230 error pane.
231 """
232 if self.proc is not None:
233 err = str(self.proc.readAllStandardError(),
234 Preferences.getSystem("IOEncoding"),
235 'replace')
236 self.errorGroup.show()
237 self.errors.insertPlainText(err)
238 self.errors.ensureCursorVisible()
239
240 QCoreApplication.processEvents()
241
242 def on_passwordCheckBox_toggled(self, isOn):
243 """
244 Private slot to handle the password checkbox toggled.
245
246 @param isOn flag indicating the status of the check box (boolean)
247 """
248 if isOn:
249 self.input.setEchoMode(QLineEdit.Password)
250 else:
251 self.input.setEchoMode(QLineEdit.Normal)
252
253 @pyqtSlot()
254 def on_sendButton_clicked(self):
255 """
256 Private slot to send the input to the subversion process.
257 """
258 input = self.input.text()
259 input += os.linesep
260
261 if self.passwordCheckBox.isChecked():
262 self.errors.insertPlainText(os.linesep)
263 self.errors.ensureCursorVisible()
264 else:
265 self.resultbox.insertPlainText(input)
266 self.resultbox.ensureCursorVisible()
267
268 self.proc.write(input)
269
270 self.passwordCheckBox.setChecked(False)
271 self.input.clear()
272
273 def on_input_returnPressed(self):
274 """
275 Private slot to handle the press of the return key in the input field.
276 """
277 self.intercept = True
278 self.on_sendButton_clicked()
279
280 def keyPressEvent(self, evt):
281 """
282 Protected slot to handle a key press event.
283
284 @param evt the key press event (QKeyEvent)
285 """
286 if self.intercept:
287 self.intercept = False
288 evt.accept()
289 return
290 super().keyPressEvent(evt)

eric ide

mercurial