--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PipxInterface/PipxAppStartDialog.py Mon Jun 24 19:48:46 2024 +0200 @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- + +""" +Module implementing a dialog to enter the application command line parameters and +to execute the app. +""" + +import os +import shlex + +from PyQt6.QtCore import QCoreApplication, QProcess, Qt, QTimer, pyqtSlot +from PyQt6.QtWidgets import QAbstractButton, QDialog, QDialogButtonBox + +from eric7 import Preferences +from eric7.EricGui import EricPixmapCache +from eric7.EricWidgets import EricMessageBox + +from .Ui_PipxAppStartDialog import Ui_PipxAppStartDialog + + +class PipxAppStartDialog(QDialog, Ui_PipxAppStartDialog): + """ + Class implementing a dialog to enter the application command line parameters and + to execute the app. + """ + + def __init__(self, app, parent=None): + """ + Constructor + + @param app path of the application to be executed + @type str + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + self.setupUi(self) + + self.executeButton.setIcon(EricPixmapCache.getIcon("start")) + + self.__process = None + + self.appLabel.setText(app) + self.errorGroup.hide() + + self.parametersEdit.returnPressed.connect(self.on_executeButton_clicked) + + def closeEvent(self, e): + """ + Protected slot implementing a close event handler. + + @param e close event + @type QCloseEvent + """ + self.__cancel() + e.accept() + + 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.__process = None + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) + self.executeButton.setEnabled(True) + + self.parametersEdit.setFocus(Qt.FocusReason.OtherFocusReason) + self.parametersEdit.selectAll() + + def __cancel(self): + """ + Private slot to cancel the current action. + """ + self.__finish() + + @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): + self.close() + elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): + self.__cancel() + + @pyqtSlot(int, QProcess.ExitStatus) + def __procFinished(self, _exitCode, _exitStatus): + """ + Private slot connected to the finished signal. + + @param _exitCode exit code of the process (unused) + @type int + @param _exitStatus exit status of the process (unused) + @type QProcess.ExitStatus + """ + self.__finish() + + @pyqtSlot() + def on_executeButton_clicked(self): + """ + Private slot to execute the selected app with the entered parameters. + """ + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True) + self.executeButton.setEnabled(False) + + command = self.parametersEdit.text() + args = shlex.split(command) + + self.__process = QProcess() + self.__process.finished.connect(self.__procFinished) + self.__process.readyReadStandardOutput.connect(self.__readStdout) + self.__process.readyReadStandardError.connect(self.__readStderr) + self.__process.start(self.appLabel.text(), args) + procStarted = self.__process.waitForStarted(5000) + if not procStarted: + self.buttonBox.setFocus() + EricMessageBox.critical( + self, + self.tr("Process Generation Error"), + self.tr("The process {0} could not be started.").format( + os.path.basename(self.appLabel.text()) + ), + ) + + def __readStdout(self): + """ + Private slot to handle the readyReadStandardOutput signal. + + It reads the output of the process, formats it and inserts it into + the contents pane. + """ + if self.__process is not None: + txt = str( + self.__process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + "replace", + ) + self.__addOutput(txt) + + def __addOutput(self, txt): + """ + Private method to add some text to the output pane. + + @param txt text to be added + @type str + """ + self.resultbox.insertPlainText(txt) + 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()