--- a/eric7/E5Gui/EricProcessDialog.py Sat May 22 19:52:34 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,314 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2019 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> -# - -""" -Module implementing a dialog starting a process and showing its output. -""" - -import os -import re - -from PyQt6.QtCore import ( - QProcess, QTimer, pyqtSlot, Qt, QCoreApplication, QProcessEnvironment -) -from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QLineEdit - -from E5Gui import EricMessageBox - -from .Ui_EricProcessDialog import Ui_EricProcessDialog - -from Globals import strToQByteArray -import Preferences - - -class EricProcessDialog(QDialog, Ui_EricProcessDialog): - """ - 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, outputTitle="", windowTitle="", showProgress=False, - parent=None): - """ - Constructor - - @param outputTitle title for the output group - @type str - @param windowTitle title of the dialog - @type str - @param showProgress flag indicating to show a progress bar - @type bool - @param parent reference to the parent widget - @type QWidget - """ - super().__init__(parent) - self.setupUi(self) - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) - - font = Preferences.getEditorOtherFonts("MonospacedFont") - self.resultbox.setFontFamily(font.family()) - self.resultbox.setFontPointSize(font.pointSize()) - self.errors.setFontFamily(font.family()) - self.errors.setFontPointSize(font.pointSize()) - - if windowTitle: - self.setWindowTitle(windowTitle) - if outputTitle: - self.outputGroup.setTitle(outputTitle) - self.__showProgress = showProgress - self.progressBar.setVisible(self.__showProgress) - - self.__process = None - self.__progressRe = re.compile(r"""(\d{1,3})\s*%""") - - self.show() - QCoreApplication.processEvents() - - def __finish(self): - """ - Private slot called when the process finished or the user pressed - the button. - """ - if ( - self.__process is not None and - self.__process.state() != QProcess.ProcessState.NotRunning - ): - self.__process.terminate() - QTimer.singleShot(2000, self.__process.kill) - self.__process.waitForFinished(3000) - - self.inputGroup.setEnabled(False) - self.inputGroup.hide() - - self.__process = None - - 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.Close).setFocus( - Qt.FocusReason.OtherFocusReason) - - 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 - ): - self.close() - elif button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel - ): - self.statusLabel.setText(self.tr("Process canceled.")) - self.__finish() - - 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) - ) - if self.__normal: - self.statusLabel.setText(self.tr("Process finished successfully.")) - elif exitStatus == QProcess.ExitStatus.CrashExit: - self.statusLabel.setText(self.tr("Process crashed.")) - else: - self.statusLabel.setText( - self.tr("Process finished with exit code {0}") - .format(exitCode)) - self.__finish() - - def startProcess(self, program, args, workingDir=None, showArgs=True, - environment=None): - """ - Public slot used to start the process. - - @param program path of the program to be executed - @type str - @param args list of arguments for the process - @type list of str - @param workingDir working directory for the process - @type str - @param showArgs flag indicating to show the arguments - @type bool - @param environment dictionary of environment settings to add - or change for the process - @type dict - @return flag indicating a successful start of the process - @rtype bool - """ - self.errorGroup.hide() - self.__normal = False - self.__intercept = False - - if environment is None: - environment = {} - - if showArgs: - self.resultbox.append(program + ' ' + ' '.join(args)) - self.resultbox.append('') - - self.__process = QProcess() - if environment: - env = QProcessEnvironment.systemEnvironment() - for key, value in environment.items(): - env.insert(key, value) - self.__process.setProcessEnvironment(env) - - self.__process.finished.connect(self.__procFinished) - self.__process.readyReadStandardOutput.connect(self.__readStdout) - self.__process.readyReadStandardError.connect(self.__readStderr) - - if workingDir: - self.__process.setWorkingDirectory(workingDir) - - self.__process.start(program, args) - procStarted = self.__process.waitForStarted(10000) - if not procStarted: - self.buttonBox.setFocus() - self.inputGroup.setEnabled(False) - EricMessageBox.critical( - self, - self.tr('Process Generation Error'), - self.tr( - '<p>The process <b>{0}</b> could not be started.</p>' - ).format(program)) - else: - self.inputGroup.setEnabled(True) - self.inputGroup.show() - - return procStarted - - 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 readyReadStandardOutput signal. - - It reads the output of the process and inserts it into the - output pane. - """ - if self.__process is not None: - s = str(self.__process.readAllStandardOutput(), - Preferences.getSystem("IOEncoding"), - 'replace') - if self.__showProgress: - match = self.__progressRe.search(s) - if match: - progress = int(match.group(1)) - self.progressBar.setValue(progress) - if not s.endswith("\n"): - s += "\n" - self.resultbox.insertPlainText(s) - self.resultbox.ensureCursorVisible() - - QCoreApplication.processEvents() - - def __readStderr(self): - """ - Private slot to handle the readyReadStandardError signal. - - It reads the error output of the process and inserts it into the - error pane. - """ - if self.__process is not None: - s = str(self.__process.readAllStandardError(), - Preferences.getSystem("IOEncoding"), - 'replace') - - self.errorGroup.show() - self.errors.insertPlainText(s) - self.errors.ensureCursorVisible() - - QCoreApplication.processEvents() - - 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 - """ - if isOn: - self.input.setEchoMode(QLineEdit.EchoMode.Password) - else: - self.input.setEchoMode(QLineEdit.EchoMode.Normal) - - @pyqtSlot() - def on_sendButton_clicked(self): - """ - Private slot to send the input to the git process. - """ - 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.__process.write(strToQByteArray(inputTxt)) - - self.passwordCheckBox.setChecked(False) - self.input.clear() - - 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 (QKeyEvent) - """ - if self.__intercept: - self.__intercept = False - evt.accept() - return - - super().keyPressEvent(evt)