class MotivoAccess: motivoDirectory = MOTIVO_DIR def __init__(self): self.motivoProcess = QProcess() self.buildProcess = QProcess() self.mergeProcess = QProcess() self.sampleProcess = QProcess() self.graphProcess = QProcess() # TODO check motivo directory and produce an error message if needed self.motivoProcess.setWorkingDirectory(MotivoAccess.motivoDirectory + '/scripts/') self.motivoProcess.setProgram('motivo.sh') # Qproces emit readReady signal when data is ready to be read # Connect the handler for read the data self.buildProcess.setWorkingDirectory(MotivoAccess.motivoDirectory + '/build/bin/') self.buildProcess.setProgram('motivo-build') self.mergeProcess.setWorkingDirectory(MotivoAccess.motivoDirectory + '/build/bin/') self.mergeProcess.setProgram('motivo-merge') self.sampleProcess.setWorkingDirectory(MotivoAccess.motivoDirectory + '/build/bin/') self.sampleProcess.setProgram('motivo-sample') self.graphProcess.setWorkingDirectory(MotivoAccess.motivoDirectory + '/build/bin/') self.graphProcess.setProgram('motivo-graph') def runMotivo(self, motivoArguments): self.motivoProcess.setArguments(motivoArguments) self.motivoProcess.start() def runBuild(self, buildArguments): self.buildProcess.setArguments(buildArguments) self.buildProcess.start() def runMerge(self, mergeArguments): self.mergeProcess.setArguments(mergeArguments) self.mergeProcess.start() def runSample(self, sampleArguments): self.sampleProcess.setArguments(sampleArguments) self.sampleProcess.start() def runConvertTxtToBinary(self, convertTxtToBinaryArguments): self.graphProcess.setArguments(convertTxtToBinaryArguments) self.graphProcess.start()
class Form(QPlainTextEdit): processFinished = Signal() def __init__(self, fileName, parent=None): super(Form, self).__init__(parent) self._fileName = fileName self.setStyleSheet("font-family: Source Code Pro; font-size: 16px; ") self._process = QProcess() self._process.readyReadStandardOutput.connect(self._processStdOut) self._process.readyReadStandardError.connect(self._processStdErr) self._process.finished.connect(self._processFinished) def run(self, args): self._append("> " + shlex.join(args.args) + "\n") self._lastPos = 0 self._process.setWorkingDirectory(args.dir) self._process.setProgram(args.args[0]) self._process.setArguments(args.args[1:]) self._process.start() @Slot() def putTextToFile(self): open(self._fileName, "w").write(self.toPlainText()) def keyPressEvent(self, event): k = event.key() if not self.textCursor().position() < self._lastPos: super().keyPressEvent(event) if k == Qt.Key_Return or k == Qt.Key_Enter: self._process.write( bytes(self.toPlainText()[self._lastPos:], "utf-8")) self._lastPos = self.textCursor().position() def _processStdOut(self): self._append(str(self._process.readAllStandardOutput(), "utf-8")) def _processStdErr(self): self._append(str(self._process.readAllStandardError(), "utf-8")) def _append(self, output): self.moveCursor(QTextCursor.End) self.insertPlainText(output) self.moveCursor(QTextCursor.End) self._lastPos = self.textCursor().position() def _processFinished(self, exitCode): self._append("\nProcess Finished with exit code: " + str(exitCode) + "\n") self.processFinished.emit()
class QProcessExecutionManager(ExecutionManager): """Class to manage tool instance execution using a PySide2 QProcess.""" def __init__(self, logger, program="", args=None, silent=False, semisilent=False): """Class constructor. Args: logger (LoggerInterface): a logger instance program (str): Path to program to run in the subprocess (e.g. julia.exe) args (list, optional): List of argument for the program (e.g. path to script file) silent (bool): Whether or not to emit logger msg signals semisilent (bool): If True, show Process Log messages """ super().__init__(logger) self._program = program self._args = args if args is not None else [] self._silent = silent # Do not show Event Log nor Process Log messages self._semisilent = semisilent # Do not show Event Log messages but show Process Log messages self.process_failed = False self.process_failed_to_start = False self.user_stopped = False self._process = QProcess(self) self.process_output = None # stdout when running silent self.process_error = None # stderr when running silent self._out_chunks = [] self._err_chunks = [] def program(self): """Program getter method.""" return self._program def args(self): """Program argument getter method.""" return self._args # noinspection PyUnresolvedReferences def start_execution(self, workdir=None): """Starts the execution of a command in a QProcess. Args: workdir (str, optional): Work directory """ if workdir is not None: self._process.setWorkingDirectory(workdir) self._process.started.connect(self.process_started) self._process.finished.connect(self.on_process_finished) if not self._silent and not self._semisilent: # Loud self._process.readyReadStandardOutput.connect(self.on_ready_stdout) self._process.readyReadStandardError.connect(self.on_ready_stderr) self._process.errorOccurred.connect(self.on_process_error) self._process.stateChanged.connect(self.on_state_changed) elif self._semisilent: # semi-silent self._process.readyReadStandardOutput.connect(self.on_ready_stdout) self._process.readyReadStandardError.connect(self.on_ready_stderr) self._process.start(self._program, self._args) if self._process is not None and not self._process.waitForStarted(msecs=10000): self.process_failed = True self.process_failed_to_start = True self._process.deleteLater() self._process = None self.execution_finished.emit(-9998) def wait_for_process_finished(self, msecs=30000): """Wait for subprocess to finish. Args: msecs (int): Timeout in milliseconds Return: True if process finished successfully, False otherwise """ if self._process is None: return False if self.process_failed or self.process_failed_to_start: return False if not self._process.waitForFinished(msecs): self.process_failed = True self._process.close() self._process = None return False return True @Slot() def process_started(self): """Run when subprocess has started.""" @Slot(int) def on_state_changed(self, new_state): """Runs when QProcess state changes. Args: new_state (int): Process state number (``QProcess::ProcessState``) """ if new_state == QProcess.Starting: self._logger.msg.emit("\tStarting program <b>{0}</b>".format(self._program)) arg_str = " ".join(self._args) self._logger.msg.emit("\tArguments: <b>{0}</b>".format(arg_str)) elif new_state == QProcess.Running: self._logger.msg_warning.emit("\tExecution in progress...") elif new_state == QProcess.NotRunning: # logging.debug("Process is not running") pass else: self._logger.msg_error.emit("Process is in an unspecified state") logging.error("QProcess unspecified state: %s", new_state) @Slot(int) def on_process_error(self, process_error): """Runs if there is an error in the running QProcess. Args: process_error (int): Process error number (``QProcess::ProcessError``) """ if process_error == QProcess.FailedToStart: self.process_failed = True self.process_failed_to_start = True self._logger.msg_error.emit("Process failed to start") elif process_error == QProcess.Timedout: self.process_failed = True self._logger.msg_error.emit("Timed out") elif process_error == QProcess.Crashed: self.process_failed = True if not self.user_stopped: self._logger.msg_error.emit("Process crashed") elif process_error == QProcess.WriteError: self._logger.msg_error.emit("Process WriteError") elif process_error == QProcess.ReadError: self._logger.msg_error.emit("Process ReadError") elif process_error == QProcess.UnknownError: self._logger.msg_error.emit("Unknown error in process") else: self._logger.msg_error.emit("Unspecified error in process: {0}".format(process_error)) self.teardown_process() def teardown_process(self): """Tears down the QProcess in case a QProcess.ProcessError occurred. Emits execution_finished signal.""" if not self._process: pass else: out = str(self._process.readAllStandardOutput().data(), "utf-8", errors="replace") errout = str(self._process.readAllStandardError().data(), "utf-8", errors="replace") if out is not None: self._logger.msg_proc.emit(out.strip()) if errout is not None: self._logger.msg_proc.emit(errout.strip()) self._process.deleteLater() self._process = None self.execution_finished.emit(-9998) def stop_execution(self): """See base class.""" self.user_stopped = True self.process_failed = True if not self._process: return try: self._process.kill() if not self._process.waitForFinished(5000): self._process.finished.emit(-1, -1) self._process.deleteLater() except Exception as ex: # pylint: disable=broad-except self._logger.msg_error.emit("[{0}] exception when terminating process".format(ex)) logging.exception("Exception in closing QProcess: %s", ex) finally: self._process = None @Slot(int, int) def on_process_finished(self, exit_code, exit_status): """Runs when subprocess has finished. Args: exit_code (int): Return code from external program (only valid for normal exits) exit_status (int): Crash or normal exit (``QProcess::ExitStatus``) """ if not self._process: return if exit_status == QProcess.CrashExit: if not self._silent: self._logger.msg_error.emit("\tProcess crashed") exit_code = -1 elif exit_status == QProcess.NormalExit: pass else: if not self._silent: self._logger.msg_error.emit("Unknown QProcess exit status [{0}]".format(exit_status)) exit_code = -1 if not exit_code == 0: self.process_failed = True if not self.user_stopped: out = str(self._process.readAllStandardOutput().data(), "utf-8", errors="replace") errout = str(self._process.readAllStandardError().data(), "utf-8", errors="replace") if out is not None: if not self._silent: self._logger.msg_proc.emit(out.strip()) else: self.process_output = out.strip() self.process_error = errout.strip() else: self._logger.msg.emit("*** Terminating process ***") # Delete QProcess self._process.deleteLater() self._process = None self.execution_finished.emit(exit_code) @Slot() def on_ready_stdout(self): """Emit data from stdout.""" if not self._process: return self._process.setReadChannel(QProcess.StandardOutput) chunk = self._process.readLine().data() self._out_chunks.append(chunk) if not chunk.endswith(b"\n"): return line = b"".join(self._out_chunks) line = str(line, "unicode_escape", errors="replace").strip() self._logger.msg_proc.emit(line) self._out_chunks.clear() @Slot() def on_ready_stderr(self): """Emit data from stderr.""" if not self._process: return self._process.setReadChannel(QProcess.StandardError) chunk = self._process.readLine().data() self._err_chunks.append(chunk) if not chunk.endswith(b"\n"): return line = b"".join(self._err_chunks) line = str(line, "utf-8", errors="replace").strip() self._logger.msg_proc_error.emit(line) self._err_chunks.clear()
class QProcessExecutionManager(ExecutionManager): """Class to manage tool instance execution using a PySide2 QProcess.""" def __init__(self, logger, program=None, args=None, silent=False, semisilent=False): """Class constructor. Args: logger (LoggerInterface): a logger instance program (str): Path to program to run in the subprocess (e.g. julia.exe) args (list): List of argument for the program (e.g. path to script file) silent (bool): Whether or not to emit logger msg signals """ super().__init__(logger) self._program = program self._args = args self._silent = silent # Do not show Event Log nor Process Log messages self._semisilent = semisilent # Do not show Event Log messages but show Process Log messages self.process_failed = False self.process_failed_to_start = False self._user_stopped = False self._process = QProcess(self) self.process_output = None # stdout when running silent self.error_output = None # stderr when running silent def program(self): """Program getter method.""" return self._program def args(self): """Program argument getter method.""" return self._args # noinspection PyUnresolvedReferences def start_execution(self, workdir=None): """Starts the execution of a command in a QProcess. Args: workdir (str): Work directory """ if workdir is not None: self._process.setWorkingDirectory(workdir) self._process.started.connect(self.process_started) self._process.finished.connect(self.on_process_finished) if not self._silent and not self._semisilent: # Loud self._process.readyReadStandardOutput.connect(self.on_ready_stdout) self._process.readyReadStandardError.connect(self.on_ready_stderr) self._process.error.connect(self.on_process_error) # errorOccurred available in Qt 5.6 self._process.stateChanged.connect(self.on_state_changed) elif self._semisilent: # semi-silent self._process.readyReadStandardOutput.connect(self.on_ready_stdout) self._process.readyReadStandardError.connect(self.on_ready_stderr) self._process.start(self._program, self._args) if not self._process.waitForStarted(msecs=10000): # This blocks until process starts or timeout happens self.process_failed = True self.process_failed_to_start = True self._process.deleteLater() self._process = None self.execution_finished.emit(-9998) def wait_for_process_finished(self, msecs=30000): """Wait for subprocess to finish. Return: True if process finished successfully, False otherwise """ if not self._process: return False if self.process_failed or self.process_failed_to_start: return False if not self._process.waitForFinished(msecs): self.process_failed = True self._process.close() self._process = None return False return True @Slot(name="process_started") def process_started(self): """Run when subprocess has started.""" @Slot("QProcess::ProcessState", name="on_state_changed") def on_state_changed(self, new_state): """Runs when QProcess state changes. Args: new_state (QProcess::ProcessState): Process state number """ if new_state == QProcess.Starting: self._logger.msg.emit("\tStarting program <b>{0}</b>".format(self._program)) arg_str = " ".join(self._args) self._logger.msg.emit("\tArguments: <b>{0}</b>".format(arg_str)) elif new_state == QProcess.Running: self._logger.msg_warning.emit( "\tExecution is in progress. See Process Log for messages " "(stdout&stderr)" ) elif new_state == QProcess.NotRunning: # logging.debug("QProcess is not running") pass else: self._logger.msg_error.emit("Process is in an unspecified state") logging.error("QProcess unspecified state: %s", new_state) @Slot("QProcess::ProcessError", name="'on_process_error") def on_process_error(self, process_error): """Run if there is an error in the running QProcess. Args: process_error (QProcess::ProcessError): Process error number """ if process_error == QProcess.FailedToStart: self.process_failed = True self.process_failed_to_start = True elif process_error == QProcess.Timedout: self.process_failed = True self._logger.msg_error.emit("Timed out") elif process_error == QProcess.Crashed: self.process_failed = True if not self._user_stopped: self._logger.msg_error.emit("Process crashed") elif process_error == QProcess.WriteError: self._logger.msg_error.emit("Process WriteError") elif process_error == QProcess.ReadError: self._logger.msg_error.emit("Process ReadError") elif process_error == QProcess.UnknownError: self._logger.msg_error.emit("Unknown error in process") else: self._logger.msg_error.emit("Unspecified error in process: {0}".format(process_error)) def stop_execution(self): """See base class.""" self._logger.msg_error.emit("Terminating process") self._user_stopped = True self.process_failed = True if not self._process: return try: self._process.terminate() except Exception as ex: # pylint: disable=broad-except self._logger.msg_error.emit("[{0}] exception when terminating process".format(ex)) logging.exception("Exception in closing QProcess: %s", ex) finally: self._process.deleteLater() self._process = None @Slot(int) def on_process_finished(self, exit_code): """Runs when subprocess has finished. Args: exit_code (int): Return code from external program (only valid for normal exits) """ # logging.debug("Error that occurred last: {0}".format(self._process.error())) if not self._process: return exit_status = self._process.exitStatus() # Normal or crash exit if exit_status == QProcess.CrashExit: if not self._silent: self._logger.msg_error.emit("\tProcess crashed") exit_code = -1 elif exit_status == QProcess.NormalExit: pass else: if not self._silent: self._logger.msg_error.emit("Unknown QProcess exit status [{0}]".format(exit_status)) exit_code = -1 if not exit_code == 0: self.process_failed = True if not self._user_stopped: out = str(self._process.readAllStandardOutput().data(), "utf-8") errout = str(self._process.readAllStandardError().data(), "utf-8") if out is not None: if not self._silent: self._logger.msg_proc.emit(out.strip()) else: self.process_output = out.strip() self.error_output = errout.strip() else: self._logger.msg.emit("*** Terminating process ***") # Delete QProcess self._process.deleteLater() self._process = None self.execution_finished.emit(exit_code) @Slot(name="on_ready_stdout") def on_ready_stdout(self): """Emit data from stdout.""" if not self._process: return out = str(self._process.readAllStandardOutput().data(), "utf-8") self._logger.msg_proc.emit(out.strip()) @Slot(name="on_ready_stderr") def on_ready_stderr(self): """Emit data from stderr.""" if not self._process: return err = str(self._process.readAllStandardError().data(), "utf-8") self._logger.msg_proc_error.emit(err.strip())
class QSubProcess(QObject): """Class to handle starting, running, and finishing PySide2 QProcesses.""" subprocess_finished_signal = Signal(int, name="subprocess_finished_signal") def __init__(self, toolbox, program=None, args=None, silent=False): """Class constructor. Args: toolbox (ToolboxUI): Instance of Main UI class. program (str): Path to program to run in the subprocess (e.g. julia.exe) args (list): List of argument for the program (e.g. path to script file) silent (bool): Whether or not to emit toolbox msg signals """ super().__init__() self._toolbox = toolbox self._program = program self._args = args self._silent = silent self.process_failed = False self.process_failed_to_start = False self._user_stopped = False self._process = QProcess(self) self.output = None def program(self): """Program getter method.""" return self._program def args(self): """Program argument getter method.""" return self._args # noinspection PyUnresolvedReferences def start_process(self, workdir=None): """Start the execution of a command in a QProcess. Args: workdir (str): Directory for the script (at least with Julia this is a must) """ if workdir is not None: self._process.setWorkingDirectory(workdir) self._process.started.connect(self.process_started) self._process.finished.connect(self.process_finished) if not self._silent: self._process.readyReadStandardOutput.connect(self.on_ready_stdout) self._process.readyReadStandardError.connect(self.on_ready_stderr) self._process.error.connect( self.on_process_error) # errorOccurred available in Qt 5.6 self._process.stateChanged.connect(self.on_state_changed) # self._toolbox.msg.emit("\tStarting program: <b>{0}</b>".format(self._program)) self._process.start(self._program, self._args) if not self._process.waitForStarted( msecs=10000 ): # This blocks until process starts or timeout happens self.process_failed = True self.process_failed_to_start = True self._process.deleteLater() self._process = None self.subprocess_finished_signal.emit(-9998) def wait_for_finished(self, msecs=30000): """Wait for subprocess to finish. Return: True if process finished successfully, False otherwise """ if not self._process: return False if self.process_failed or self.process_failed_to_start: return False if not self._process.waitForFinished(msecs): self.process_failed = True self._process.close() self._process = None return False return True @Slot(name="process_started") def process_started(self): """Run when subprocess has started.""" pass # self._toolbox.msg.emit("\tSubprocess started...") @Slot("QProcess::ProcessState", name="on_state_changed") def on_state_changed(self, new_state): """Runs when QProcess state changes. Args: new_state (QProcess::ProcessState): Process state number """ if new_state == QProcess.Starting: self._toolbox.msg.emit("\tStarting program <b>{0}</b>".format( self._program)) arg_str = " ".join(self._args) self._toolbox.msg.emit("\tArguments: <b>{0}</b>".format(arg_str)) elif new_state == QProcess.Running: self._toolbox.msg_warning.emit( "\tExecution is in progress. See Process Log for messages " "(stdout&stderr)") elif new_state == QProcess.NotRunning: # logging.debug("QProcess is not running") pass else: self._toolbox.msg_error.emit("Process is in an unspecified state") logging.error("QProcess unspecified state: {0}".format(new_state)) @Slot("QProcess::ProcessError", name="'on_process_error") def on_process_error(self, process_error): """Run if there is an error in the running QProcess. Args: process_error (QProcess::ProcessError): Process error number """ if process_error == QProcess.FailedToStart: # self._toolbox.msg_error.emit("Failed to start") self.process_failed = True self.process_failed_to_start = True elif process_error == QProcess.Timedout: self.process_failed = True self._toolbox.msg_error.emit("Timed out") elif process_error == QProcess.Crashed: self.process_failed = True self._toolbox.msg_error.emit("Process crashed") elif process_error == QProcess.WriteError: self._toolbox.msg_error.emit("Process WriteError") elif process_error == QProcess.ReadError: self._toolbox.msg_error.emit("Process ReadError") elif process_error == QProcess.UnknownError: self._toolbox.msg_error.emit("Unknown error in process") else: self._toolbox.msg_error.emit( "Unspecified error in process: {0}".format(process_error)) def terminate_process(self): """Shutdown simulation in a QProcess.""" self._toolbox.msg_error.emit("<br/>Terminating process") # logging.debug("Terminating QProcess nr.{0}. ProcessState:{1} and ProcessError:{2}" # .format(self._process.processId(), self._process.state(), self._process.error())) self._user_stopped = True self.process_failed = True try: self._process.close() except Exception as ex: self._toolbox.msg_error.emit( "[{0}] exception when terminating process".format(ex)) logging.exception("Exception in closing QProcess: {}".format(ex)) @Slot(int, name="process_finished") def process_finished(self, exit_code): """Run when subprocess has finished. Args: exit_code (int): Return code from external program (only valid for normal exits) """ # logging.debug("Error that occurred last: {0}".format(self._process.error())) exit_status = self._process.exitStatus() # Normal or crash exit if exit_status == QProcess.CrashExit: if not self._silent: self._toolbox.msg_error.emit("\tProcess crashed") exit_code = -1 elif exit_status == QProcess.NormalExit: if not self._silent: self._toolbox.msg.emit("\tProcess finished") else: if not self._silent: self._toolbox.msg_error.emit( "Unknown QProcess exit status [{0}]".format(exit_status)) exit_code = -1 if not exit_code == 0: self.process_failed = True if not self._user_stopped: out = str(self._process.readAllStandardOutput().data(), "utf-8") if out is not None: if not self._silent: self._toolbox.msg_proc.emit(out.strip()) else: self.output = out.strip() else: self._toolbox.msg.emit("*** Terminating process ***") # Delete QProcess self._process.deleteLater() self._process = None self.subprocess_finished_signal.emit(exit_code) @Slot(name="on_ready_stdout") def on_ready_stdout(self): """Emit data from stdout.""" out = str(self._process.readAllStandardOutput().data(), "utf-8") self._toolbox.msg_proc.emit(out.strip()) @Slot(name="on_ready_stderr") def on_ready_stderr(self): """Emit data from stderr.""" err = str(self._process.readAllStandardError().data(), "utf-8") self._toolbox.msg_proc_error.emit(err.strip())
class MainWindow(QDialog): def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle(APP_NAME) self.setWindowIcon(QIcon(get_resource_path(os.path.join('resources', 'noun_Plant.ico')))) self.ui = Ui_Dialog() self.ui.setupUi(self) self.ui.lineEdit_invoiceNumber.setValidator(QIntValidator()) self.ui.progressBar.setMaximum(1) self.proc = QProcess() self.ui.tableWidget_invoiceContent.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.savePath = create_path_it_not_exist(os.path.join(get_config_path(APP_CONFIG_FOLDER), 'save.json')) self.saveData = SaveData(self.savePath) self.outputPath = os.path.join(get_exe_path(), create_path_it_not_exist(os.path.join(get_exe_path(), 'output'))) self.texGenerator = LatexTemplateGenerator(get_resource_path('resources').replace('\\', '/')) self.currentGeneralInfo = GeneralInfo() self.currentClientInfo = ClientInfo() self.currentInvoiceInfo = InvoiceInfo() self.ui.pushButton_saveQuickRecallInvoice.clicked.connect(self.save_invoice_info) self.ui.pushButton_saveQuickRecallClient.clicked.connect(self.save_client_info) self.ui.pushButton_saveQuickRecallGeneral.clicked.connect(self.save_general_info) self.ui.comboBox_quickRecallInvoice.activated.connect(self.on_combo_box_invoice_changed) self.ui.comboBox_quickRecallClient.activated.connect(self.on_combo_box_client_changed) self.ui.comboBox_quickRecallGeneral.activated.connect(self.on_combo_box_general_changed) self.ui.pushButton_generateInvoice.clicked.connect(self.generate_invoice) self.proc.finished.connect(functools.partial(self._handleProcFinished, self.proc)) self.ui.toolButton_add.clicked.connect(self.add_row) self.ui.toolButton_delete.clicked.connect(self.delete_row) self.update_ui() def _handleProcFinished(self, process, exitCode): stdOut = process.readAllStandardOutput() stdErr = process.readAllStandardError() print("Standard Out:") print(stdOut) print("Standard Error:") print(stdErr) if(exitCode == 0): self.success_message('Invoice Generated successfully') self.ui.progressBar.setMaximum(1) def make_pdf(self, input_folder, input_file): if self.proc.isOpen(): print('cancelled running process') self.proc.close() self.proc.setWorkingDirectory(input_folder) self.proc.start("xelatex", [os.path.join(input_folder, input_file)]) self.ui.progressBar.setMaximum(0) def save_data(self): self.save_invoice_info() self.save_client_info() self.save_general_info() if self.saveData.save_client(self.currentClientInfo) and \ self.saveData.save_invoice(self.currentInvoiceInfo) and \ self.saveData.save_general(self.currentGeneralInfo): self.saveData.save_to_file() else: self.warning_message("Invalid invoice formatting\n Check for empty or incorrect fields") def load_data(self): pass def list_client(self): self.ui.comboBox_quickRecallClient.clear() for client in self.saveData.clients: self.ui.comboBox_quickRecallClient.addItem(client.name) def list_general(self): self.ui.comboBox_quickRecallGeneral.clear() for general in self.saveData.generals: self.ui.comboBox_quickRecallGeneral.addItem(general.company_name) pass def list_invoice(self): self.ui.comboBox_quickRecallInvoice.clear() for invoice in self.saveData.invoices: self.ui.comboBox_quickRecallInvoice.addItem(invoice.invoice_number) def generate_invoice(self): print("generating invoice") self.save_data() filename = ''.join(e for e in unidecode.unidecode(self.currentInvoiceInfo.client.name) if e.isalnum()) self.texGenerator.render(SaveData.asflatdict(self.currentInvoiceInfo), create_path_it_not_exist( os.path.join(self.outputPath, self.currentInvoiceInfo.invoice_number, 'Facture_' + self.currentInvoiceInfo.invoice_number + '_' + filename + '.tex'))) self.make_pdf(os.path.join(self.outputPath, self.currentInvoiceInfo.invoice_number), 'Facture_' + self.currentInvoiceInfo.invoice_number + '_' + filename + '.tex') self.update_ui() def recall_general_info(self, company_name): newGeneral = self.saveData.get_general(company_name) self.ui.lineEdit_companyName.setText(newGeneral.company_name) self.ui.lineEdit_firstName.setText(newGeneral.first_name) self.ui.lineEdit_lastName.setText(newGeneral.last_name) self.ui.lineEdit_fullAddress.setText(newGeneral.full_address) self.ui.lineEdit_companySIRET.setText(newGeneral.company_siret) self.ui.lineEdit_companySIREN.setText(newGeneral.company_siren) self.ui.lineEdit_companyAPE.setText(newGeneral.company_ape) self.ui.lineEdit_companyEmail.setText(newGeneral.company_email) self.ui.lineEdit_companyTelephone.setText(newGeneral.company_phone) self.ui.lineEdit_bankIBAN.setText(newGeneral.bank_iban) self.ui.lineEdit_bankBIC.setText(newGeneral.bank_bic) def recall_client_info(self, name): newClient = self.saveData.get_client(name) self.ui.lineEdit_clientName.setText(newClient.name) self.ui.lineEdit_clientAddressFirst.setText(newClient.address_first_line) self.ui.lineEdit_clientAdressSecond.setText(newClient.address_second_line) self.update_infos_from_UI() def recall_invoice_info(self, invoice_number): newInvoice = self.saveData.get_invoice(invoice_number) self.ui.lineEdit_invoiceNumber.setText(newInvoice.invoice_number) self.ui.lineEdit_invoiceDate.setText(newInvoice.invoice_date) self.ui.lineEdit_invoiceName.setText(newInvoice.invoice_name) self.ui.tableWidget_invoiceContent.clearContents() for item in newInvoice.items: row_position = self.ui.tableWidget_invoiceContent.rowCount() self.ui.tableWidget_invoiceContent.insertRow(row_position) self.ui.tableWidget_invoiceContent.setItem(row_position, 0, QTableWidgetItem(str(item.product_name))) self.ui.tableWidget_invoiceContent.setItem(row_position, 1, QTableWidgetItem(str(item.quantity))) self.ui.tableWidget_invoiceContent.setItem(row_position, 2, QTableWidgetItem(str(item.price))) self.recall_client_info(newInvoice.client.name) self.recall_general_info(newInvoice.general.company_name) self.update_infos_from_UI() def on_combo_box_client_changed(self, index): self.recall_client_info(self.ui.comboBox_quickRecallClient.itemText(index)) def on_combo_box_general_changed(self, index): self.recall_general_info(self.ui.comboBox_quickRecallGeneral.itemText(index)) def on_combo_box_invoice_changed(self, index): self.recall_invoice_info(self.ui.comboBox_quickRecallInvoice.itemText(index)) def save_invoice_info(self): self.update_invoice_infos_from_UI() if not self.saveData.save_invoice(self.currentInvoiceInfo): self.warning_message("Couldn't save new Invoice") self.update_ui() def save_general_info(self): self.update_general_infos_from_UI() if not self.saveData.save_general(self.currentGeneralInfo): self.warning_message("Couldn't save new General") self.update_ui() def save_client_info(self): self.update_client_infos_from_UI() if not self.saveData.save_client(self.currentClientInfo): self.warning_message("Couldn't save new Client") self.update_ui() def add_row(self): self.ui.tableWidget_invoiceContent.insertRow(self.ui.tableWidget_invoiceContent.rowCount()) def delete_row(self): self.ui.tableWidget_invoiceContent.removeRow(self.ui.tableWidget_invoiceContent.currentRow()) def ask_validation(self, text, informative_text, title="Validation Dialog"): msgBox = QMessageBox() msgBox.setWindowTitle("hey") msgBox.setText(text) msgBox.setInformativeText(informative_text) msgBox.setStandardButtons(QMessageBox.Apply | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ret = msgBox.exec_() return True if ret == QMessageBox.Apply else False def success_message(self, text): ret = QMessageBox.information(self, self.tr("Success"), self.tr(text), QMessageBox.Ok, QMessageBox.Ok) def warning_message(self, text): ret = QMessageBox.warning(self, self.tr("Warning"), self.tr(text), QMessageBox.Ok, QMessageBox.Ok) def update_client_infos_from_UI(self): currentClientInfo = ClientInfo() currentClientInfo.name = self.ui.lineEdit_clientName.text() currentClientInfo.address_first_line = self.ui.lineEdit_clientAddressFirst.text() currentClientInfo.address_second_line = self.ui.lineEdit_clientAdressSecond.text() self.currentClientInfo = currentClientInfo def update_general_infos_from_UI(self): currentGeneralInfo = GeneralInfo() currentGeneralInfo.company_name = self.ui.lineEdit_companyName.text() currentGeneralInfo.first_name = self.ui.lineEdit_firstName.text() currentGeneralInfo.last_name = self.ui.lineEdit_lastName.text() currentGeneralInfo.full_address = self.ui.lineEdit_fullAddress.text() currentGeneralInfo.company_siret = self.ui.lineEdit_companySIRET.text() currentGeneralInfo.company_siren = self.ui.lineEdit_companySIREN.text() currentGeneralInfo.company_ape = self.ui.lineEdit_companyAPE.text() currentGeneralInfo.company_email = self.ui.lineEdit_companyEmail.text() currentGeneralInfo.company_phone = self.ui.lineEdit_companyTelephone.text() currentGeneralInfo.bank_iban = self.ui.lineEdit_bankIBAN.text() currentGeneralInfo.bank_bic = self.ui.lineEdit_bankBIC.text() self.currentGeneralInfo = currentGeneralInfo def update_invoice_infos_from_UI(self): currentInvoiceInfo = InvoiceInfo() currentInvoiceInfo.invoice_number = self.ui.lineEdit_invoiceNumber.text() currentInvoiceInfo.invoice_date = self.ui.lineEdit_invoiceDate.text() currentInvoiceInfo.invoice_name = self.ui.lineEdit_invoiceName.text() currentInvoiceInfo.general = self.currentGeneralInfo currentInvoiceInfo.client = self.currentClientInfo currentInvoiceInfo.items = [] try: for row in range(self.ui.tableWidget_invoiceContent.rowCount()): newItem = InvoiceItem() newItem.product_name = self.ui.tableWidget_invoiceContent.item(row, 0).text() \ if self.ui.tableWidget_invoiceContent.item(row, 0) is not None else "" newItem.quantity = int(self.ui.tableWidget_invoiceContent.item(row, 1).text()) \ if self.ui.tableWidget_invoiceContent.item(row, 1) is not None else 0 newItem.price = float(self.ui.tableWidget_invoiceContent.item(row, 2).text()) \ if self.ui.tableWidget_invoiceContent.item(row, 2) is not None else 0 currentInvoiceInfo.items.append(newItem) except ValueError: self.warning_message("oops Something went wrong, make sure you entered appropriate values") self.currentInvoiceInfo = currentInvoiceInfo def update_infos_from_UI(self): self.update_general_infos_from_UI() self.update_client_infos_from_UI() self.update_invoice_infos_from_UI() def update_ui(self): self.list_invoice() self.list_client() self.list_general()