diff -r 8413c2429808 -r 64339135bd61 ProjectDjango/DjangoDialog.py --- a/ProjectDjango/DjangoDialog.py Fri Dec 31 13:17:55 2021 +0100 +++ b/ProjectDjango/DjangoDialog.py Wed Sep 21 16:42:20 2022 +0200 @@ -11,7 +11,11 @@ from PyQt6.QtCore import pyqtSlot, QProcess, QTimer, QFileInfo from PyQt6.QtWidgets import ( - QDialog, QDialogButtonBox, QAbstractButton, QTextEdit, QLineEdit + QDialog, + QDialogButtonBox, + QAbstractButton, + QTextEdit, + QLineEdit, ) from EricWidgets import EricMessageBox, EricFileDialog @@ -26,18 +30,26 @@ class DjangoDialog(QDialog, Ui_DjangoDialog): """ Class implementing a dialog starting a process and showing its output. - + It starts a QProcess and displays a dialog that shows the output of the process. The dialog is modal, which causes a synchronized execution of the process. """ - def __init__(self, text, fixed=False, linewrap=True, - msgSuccess=None, msgError=None, - saveFilters=None, showInput=False, - parent=None): + + def __init__( + self, + text, + fixed=False, + linewrap=True, + msgSuccess=None, + msgError=None, + saveFilters=None, + showInput=False, + parent=None, + ): """ Constructor - + @param text text to be shown by the label @type str @param fixed flag indicating a fixed font should be used @@ -57,19 +69,15 @@ """ super().__init__(parent) self.setupUi(self) - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Save).setEnabled(False) + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Save).setEnabled(False) if saveFilters is None: - self.buttonBox.button( - QDialogButtonBox.StandardButton.Save).setHidden(True) - + self.buttonBox.button(QDialogButtonBox.StandardButton.Save).setHidden(True) + self.ioEncoding = Preferences.getSystem("IOEncoding") - + self.proc = None self.argsLists = [] self.workingDir = None @@ -79,99 +87,84 @@ self.fileFilters = saveFilters self.showInput = showInput self.intercept = False - + self.outputGroup.setTitle(text) - + if fixed: if isWindowsPlatform(): self.resultbox.setFontFamily("Lucida Console") else: self.resultbox.setFontFamily("Monospace") - + if not linewrap: self.resultbox.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap) - + @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. - + @param button button that was clicked @type QAbstractButton """ - if button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Close - ): + if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): self.close() - elif button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel - ): + elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): self.__finish() - elif button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Save - ): + elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Save): self.__saveData() - + def __finish(self): """ Private slot called when the process finished or the user pressed the button. """ if ( - self.proc is not None and - self.proc.state() != QProcess.ProcessState.NotRunning + self.proc is not None + and self.proc.state() != QProcess.ProcessState.NotRunning ): self.proc.terminate() QTimer.singleShot(2000, self.proc.kill) self.proc.waitForFinished(3000) - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setDefault(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Save).setEnabled(True) - + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Save).setEnabled(True) + self.inputGroup.setEnabled(False) self.inputGroup.hide() - + self.proc = None - + if self.argsLists: args = self.argsLists[0][:] del self.argsLists[0] - self.startProcess(args, self.workingDir, - mergedOutput=self.mergedOutput) - + self.startProcess(args, self.workingDir, mergedOutput=self.mergedOutput) + def __procFinished(self, exitCode, exitStatus): """ Private slot connected to the finished signal. - + @param exitCode exit code of the process @type int @param exitStatus exit status of the process @type QProcess.ExitStatus """ - self.normal = ( - exitStatus == QProcess.ExitStatus.NormalExit and - exitCode == 0 - ) + self.normal = exitStatus == QProcess.ExitStatus.NormalExit and exitCode == 0 self.__finish() - + if self.normal and self.msgSuccess: self.resultbox.insertPlainText(self.msgSuccess) elif not self.normal and self.msgError: self.resultbox.insertPlainText(self.msgError) self.errorGroup.show() self.resultbox.ensureCursorVisible() - - def startProcess(self, args, workingDir=None, showCommand=True, - mergedOutput=False): + + def startProcess(self, args, workingDir=None, showCommand=True, mergedOutput=False): """ Public slot used to start the process. - + @param args list of arguments for the process @type list of str @param workingDir working directory for the process @@ -184,28 +177,27 @@ @rtype bool """ self.errorGroup.hide() - + self.normal = False - + self.proc = QProcess() if mergedOutput: - self.proc.setProcessChannelMode( - QProcess.ProcessChannelMode.MergedChannels) - + self.proc.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels) + if showCommand: - self.resultbox.append(' '.join(args)) - self.resultbox.append('') - + self.resultbox.append(" ".join(args)) + self.resultbox.append("") + self.proc.finished.connect(self.__procFinished) self.proc.readyReadStandardOutput.connect(self.__readStdout) self.proc.readyReadStandardError.connect(self.__readStderr) - + if workingDir: self.proc.setWorkingDirectory(workingDir) self.workingDir = workingDir else: self.workingDir = "" - + prog = args[0] del args[0] self.proc.start(prog, args) @@ -215,11 +207,12 @@ self.inputGroup.setEnabled(False) EricMessageBox.critical( self, - self.tr('Process Generation Error'), + self.tr("Process Generation Error"), self.tr( - 'The process {0} could not be started. ' - 'Ensure, that it is in the search path.' - ).format(prog)) + "The process {0} could not be started. " + "Ensure, that it is in the search path." + ).format(prog), + ) else: if self.showInput: self.inputGroup.setEnabled(True) @@ -227,14 +220,13 @@ else: self.inputGroup.setEnabled(False) self.inputGroup.hide() - + return procStarted - - def startBatchProcesses(self, argsLists, workingDir=None, - mergedOutput=False): + + def startBatchProcesses(self, argsLists, workingDir=None, mergedOutput=False): """ Public slot used to start a batch of processes. - + @param argsLists list of lists of arguments for the processes @type list of list of str @param workingDir working directory for the process @@ -247,83 +239,77 @@ self.argsLists = argsLists[:] self.workingDir = workingDir self.mergedOutput = mergedOutput - + # start the first process args = self.argsLists[0][:] del self.argsLists[0] - res = self.startProcess(args, self.workingDir, - mergedOutput=self.mergedOutput) + res = self.startProcess(args, self.workingDir, mergedOutput=self.mergedOutput) if not res: self.argsLists = [] - + return res - + def normalExit(self): """ Public method to check for a normal process termination. - + @return flag indicating normal process termination @rtype bool """ return self.normal - + def normalExitWithoutErrors(self): """ Public method to check for a normal process termination without error messages. - + @return flag indicating normal process termination @rtype bool """ return self.normal and self.errors.toPlainText() == "" - + def __readStdout(self): """ Private slot to handle the readyReadStdout signal. - + It reads the output of the process, formats it and inserts it into the contents pane. """ if self.proc is not None: - s = str(self.proc.readAllStandardOutput(), self.ioEncoding, - 'replace') + s = str(self.proc.readAllStandardOutput(), self.ioEncoding, "replace") self.resultbox.insertPlainText(s) self.resultbox.ensureCursorVisible() - + def __readStderr(self): """ Private slot to handle the readyReadStderr signal. - + It reads the error output of the process and inserts it into the error pane. """ if self.proc is not None: self.errorGroup.show() - s = str(self.proc.readAllStandardError(), self.ioEncoding, - 'replace') + s = str(self.proc.readAllStandardError(), self.ioEncoding, "replace") self.errors.insertPlainText(s) self.errors.ensureCursorVisible() - + def __saveData(self): """ Private slot to save the output to a file. """ fname, selectedFilter = EricFileDialog.getSaveFileNameAndFilter( - self, - self.tr("Select data file"), - self.workingDir, - self.fileFilters, - None) - + self, self.tr("Select data file"), self.workingDir, self.fileFilters, None + ) + if fname: ext = QFileInfo(fname).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fname += ex - + txt = self.resultbox.toPlainText() - + try: with open(fname, "w", encoding="utf-8") as f: f.write(txt) @@ -331,14 +317,16 @@ EricMessageBox.critical( self, self.tr("Error saving data"), - self.tr("""<p>The data could not be written""" - """ to <b>{0}</b></p><p>Reason: {1}</p>""") - .format(fname, str(err))) - + self.tr( + """<p>The data could not be written""" + """ to <b>{0}</b></p><p>Reason: {1}</p>""" + ).format(fname, str(err)), + ) + def on_passwordCheckBox_toggled(self, isOn): """ Private slot to handle the password checkbox toggled. - + @param isOn flag indicating the status of the check box @type bool """ @@ -346,7 +334,7 @@ self.input.setEchoMode(QLineEdit.EchoMode.Password) else: self.input.setEchoMode(QLineEdit.EchoMode.Normal) - + @pyqtSlot() def on_sendButton_clicked(self): """ @@ -354,30 +342,30 @@ """ inputTxt = self.input.text() inputTxt += os.linesep - + if self.passwordCheckBox.isChecked(): self.errors.insertPlainText(os.linesep) self.errors.ensureCursorVisible() else: self.errors.insertPlainText(inputTxt) self.errors.ensureCursorVisible() - + self.proc.write(strToQByteArray(inputTxt)) - + self.input.clear() self.passwordCheckBox.setChecked(False) - + def on_input_returnPressed(self): """ Private slot to handle the press of the return key in the input field. """ self.intercept = True self.on_sendButton_clicked() - + def keyPressEvent(self, evt): """ Protected slot to handle a key press event. - + @param evt the key press event @type QKeyEvent """ @@ -385,5 +373,5 @@ self.intercept = False evt.accept() return - + super().keyPressEvent(evt)