def runProcess(command, arguments): process = QProcess() process.start(command, arguments) process.waitForFinished() result = [] for line in str(process.readAllStandardOutput()).split(os.linesep): result.append(line) return result
def testWithArgs(self): '''Connecting a lambda to a signal with arguments''' proc = QProcess() dummy = Dummy() QObject.connect(proc, SIGNAL('finished(int)'), lambda x: setattr(dummy, 'called', x)) proc.start(sys.executable, ['-c', '""']) proc.waitForFinished() self.assertEqual(dummy.called, proc.exitCode())
def testNoArgs(self): '''Connecting a lambda to a signal without arguments''' proc = QProcess() dummy = Dummy() QObject.connect(proc, SIGNAL('started()'), lambda: setattr(dummy, 'called', True)) proc.start(sys.executable, ['-c', '""']) proc.waitForFinished() self.assertTrue(dummy.called)
def testNoArgs(self): '''Connecting a lambda to a signal without arguments''' proc = QProcess() dummy = Dummy() QObject.connect(proc, SIGNAL('started()'), lambda: setattr(dummy, 'called', True)) proc.start(sys.executable, ['-c', '""']) proc.waitForFinished() self.assert_(dummy.called)
def exit(self): self.vivadoProcess.kill() vivado_command_to_kill = "cmd.exe /C Taskkill /IM vivado.exe /F" process = QProcess() process.start(vivado_command_to_kill) process.waitForFinished(5000) tcp_command_to_kill = "cmd.exe /C netstat -ano | find '0.0.0.0:9955'" return
def testWithoutArgs(self): '''Connect QProcess.started() to QTimeLine.togglePaused()''' process = QProcess() timeline = QTimeLine() QObject.connect(process, SIGNAL('finished(int, QProcess::ExitStatus)'), timeline, SLOT('toggleDirection()')) orig_dir = timeline.direction() process.start(sys.executable, ['-c', '"print 42"']) process.waitForFinished() new_dir = timeline.direction() if orig_dir == QTimeLine.Forward: self.assertEqual(new_dir, QTimeLine.Backward) else: self.assertEqual(new_dir, QTimeLine.Forward)
def run_handler(self): self.py_ = G.config.installed_apps[self.pack_name].get('py_') if not self.py_: QMessageBox.warning(self.parent, "提示", "未选择Python解释器") self.act_setting_slot() return if not os.path.exists(self.py_) or not os.path.isfile(self.py_): QMessageBox.warning(self.parent, "提示", f"{self.py_} 不存在") return try: self.entry_, requirement_ = self.get_build() except Exception as e: QMessageBox.warning(self.parent, "提示", str(e)) return ##检测依赖 p = QProcess() p.start(' '.join(([self.py_, "-m", 'pip', "freeze"]))) p.waitForFinished() out = p.readAllStandardOutput().data().decode() output = out.splitlines() with open(requirement_, 'r') as f: requirement = f.read().splitlines() dissatisfy, version_less = diff_pip(output, requirement) if dissatisfy: msgbox = QMessageBox(self.parent) msgbox.setWindowTitle("缺少依赖") msgbox.setText("\n".join(dissatisfy[:15]) + "\n...") yes = msgbox.addButton('立即安装', QMessageBox.AcceptRole) no = msgbox.addButton('稍后', QMessageBox.RejectRole) msgbox.setDefaultButton(yes) reply = msgbox.exec_() if reply == QMessageBox.AcceptRole: self.requirement_ = dissatisfy self.install_handler() return # run TipDialog("正在启动...") cmd = ' '.join([self.py_, self.entry_]) QProcess().startDetached(cmd)
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 Standard(object): """Action download ---|--> install --|--> == run v v \\ cancel cancel uninstall """ # common name = "" # 应用名称 desc = "" # 应用描述 icon = "" # 应用图标 app_url = "" # 应用地址 versions = {} # 应用版本及下载地址 # installed install_version = "" # 选择安装的版本 app_folder = "" # 应用安装路径 py_ = "" # 解释器 (G 中实时获取) entry_ = "" # 启动文件 (build.json实时获取) requirement_ = "" # 缺失依赖 def __init__(self, parent: QWidget, **kwargs): self.cls_name = self.__class__.__name__ self.parent = parent self.action = kwargs.get("action", Actions.DOWNLOAD) self.thread_pool = QThreadPool() self.check(**kwargs) self.div: AppDiv self.div_init() self.count = 0 self.start_time = 0 self.cancel = False self.process = QProcess(self.parent) self.process.readyReadStandardOutput.connect(self.on_readoutput) self.process.readyReadStandardError.connect(self.on_readerror) def check(self, **kwargs): """检查已下载应用参数""" self.app_folder = kwargs.get("app_folder") # 应用安装路径 self.install_version = kwargs.get('install_version') # 应用安装路径 self.app_info(**kwargs) def app_info(self, **kwargs): raise NotImplementedError @property def pack_name(self): """安装后id""" return self.cls_name + "_" + self.install_version.replace('.', '_') @property def ui_name(self): """ui中id""" return self.cls_name + "_app" def _transfer(self, widget, value=None): if widget == 'bar': self.div.job.progressbar_signal.emit(value) elif widget == 'msg': self.div.job.msg_signal.emit(value) elif widget == 'switch': self.div.job.switch_signal.emit() elif widget == 'action': self.div.job.action_signal.emit(value) def before_handle(self): self.action = Actions.CANCEL self.cancel = False self.div.action.setText(Actions.to_zn(self.action)) self._transfer('switch') def _tip(self, msg): self.parent.mainwindow.job.msg_box_signal.emit({"msg": str(msg)}) def div_init(self): self.div = AppDiv(self.parent) self.div.icon.setStyleSheet(f"image: url({self.icon});") self.div.name.setText(self.name) self.div.action.setText(Actions.to_zn(self.action)) self.div.action.clicked.connect(self.action_handler) if self.action == Actions.DOWNLOAD: for i in self.versions.keys(): act = QAction(i, self.parent) setattr(self.div, f"act_{'_'.join([j for j in i if i.isalnum()])}", act) self.div.menu.addAction(act) self.div.menu.triggered[QAction].connect( self.version_action_triggered) self.div.desc.setText(self.desc) self.div.desc.url = self.app_url # 可点击 setattr(self.parent, self.ui_name, self) self.parent.apps_layout.addLayout(self.div.layout) elif self.action == Actions.RUN: act_uninstall = QAction(Actions.to_zn(Actions.UNINSTALL), self.parent) act_setting = QAction("解释器", self.parent) act_upgrade = QAction(Actions.to_zn(Actions.UPGRADE), self.parent) setattr(self.div, f"act_uninstall", act_uninstall) setattr(self.div, f"act_setting", act_setting) setattr(self.div, f"act_upgrade", act_upgrade) self.div.menu.addAction(act_setting) self.div.menu.addAction(act_upgrade) self.div.menu.addAction(act_uninstall) self.div.menu.triggered[QAction].connect( self.menu_action_triggered) self.div.desc.setText(self.install_version) setattr(self.parent, self.pack_name, self) self.parent.installed_layout.addLayout(self.div.layout) def version_action_triggered(self, q): """点击版本号直接下载""" if self.action == Actions.CANCEL: return self.install_version = q.text() self.action_handler() def menu_action_triggered(self, q): """卸载/更新处理""" if self.action == Actions.CANCEL: return act = q.text() if act == "解释器": self.act_setting_slot() elif Actions.to_en(act) == Actions.UNINSTALL: self.uninstall_handler() elif Actions.to_en(act) == Actions.UPGRADE: self.upgrade_handler() def act_setting_slot(self): self.py_manage = PyManageWidget(self.parent, self.pack_name) self.py_manage.show() @before_download def download_handler(self): """ 版本号 下载目录 """ url = self.versions[self.install_version] postfix = os.path.splitext(url)[-1] # .zip self.app_folder = os.path.join(G.config.install_path, self.pack_name) file_temp = self.app_folder + postfix # 压缩文件路径 ## 文件续传 if os.path.exists(file_temp): local_file = os.path.getsize(file_temp) headers = {'Range': 'bytes=%d-' % local_file} mode = 'ab' else: local_file = 0 headers = {} mode = 'wb' # download self._transfer("msg", "获取中...") try: response = requests.get(url, stream=True, headers=headers) response.raise_for_status() except Exception as e: return False content_size = float(response.headers.get('Content-Length', 0)) self._transfer("bar", dict(range=[0, content_size])) # save with open(file_temp, mode) as file: chunk_size = 1024 for data in response.iter_content(chunk_size=chunk_size): if self.cancel or G.pool_done: return False file.write(data) ## self.count += 1 ##show current = chunk_size * self.count + local_file if content_size: self._transfer("bar", dict(value=current)) speed = format_size(current / (time.time() - self.start_time)) self._transfer( "msg", f"{round(current / 1024, 2)}KB/{round(content_size / 1024, 2) or '-'}KB | {speed}/s" ) extract(file_temp) # 解压 return True def on_download_callback(self, res): self._transfer('switch') if res is True: data = { "cls_name": self.cls_name, "install_version": self.install_version, "action": Actions.RUN, "app_folder": self.app_folder, "py_": "" } G.config.installed_apps.update({self.pack_name: data}) G.config.to_file() self.div.add_installed_layout(data) elif res is False: pass self.action = Actions.DOWNLOAD self._transfer("action", Actions.to_zn(self.action)) def get_build(self): """ :return: path 路径 """ path = find_file(self.app_folder, 'build.json') if path: try: with open(path[0], 'r') as f: build = json.load(f) entry = find_file(self.app_folder, build['entry'])[0] requirement = find_file(self.app_folder, build['requirement'])[0] except KeyError: raise Exception('请确保build.json中含有entry和requirement') except IndexError: raise Exception("未找到entry文件或requirement文件") except json.JSONDecodeError: raise Exception("build.json 有错误") return entry, requirement else: raise Exception("未找到文件build.json") @before_install def install_handler(self): """解析 build.json""" for line in self.requirement_: line = line.strip().replace('==', '>=') cmd_ = [self.py_, "-m", "pip", "install", line ] + G.config.get_pypi_source() if self.cancel or G.pool_done: return False self.process.start(" ".join(cmd_)) self.process.waitForFinished() return True def on_readoutput(self): output = self.process.readAllStandardOutput().data().decode() self._transfer("msg", output.replace('\n', '')) def on_readerror(self): error = self.process.readAllStandardError().data().decode() self._tip(error) def on_install_callback(self, res): self._transfer('switch') self.action = Actions.RUN self._transfer("action", Actions.to_zn(self.action)) def run_handler(self): self.py_ = G.config.installed_apps[self.pack_name].get('py_') if not self.py_: QMessageBox.warning(self.parent, "提示", "未选择Python解释器") self.act_setting_slot() return if not os.path.exists(self.py_) or not os.path.isfile(self.py_): QMessageBox.warning(self.parent, "提示", f"{self.py_} 不存在") return try: self.entry_, requirement_ = self.get_build() except Exception as e: QMessageBox.warning(self.parent, "提示", str(e)) return ##检测依赖 p = QProcess() p.start(' '.join(([self.py_, "-m", 'pip', "freeze"]))) p.waitForFinished() out = p.readAllStandardOutput().data().decode() output = out.splitlines() with open(requirement_, 'r') as f: requirement = f.read().splitlines() dissatisfy, version_less = diff_pip(output, requirement) if dissatisfy: msgbox = QMessageBox(self.parent) msgbox.setWindowTitle("缺少依赖") msgbox.setText("\n".join(dissatisfy[:15]) + "\n...") yes = msgbox.addButton('立即安装', QMessageBox.AcceptRole) no = msgbox.addButton('稍后', QMessageBox.RejectRole) msgbox.setDefaultButton(yes) reply = msgbox.exec_() if reply == QMessageBox.AcceptRole: self.requirement_ = dissatisfy self.install_handler() return # run TipDialog("正在启动...") cmd = ' '.join([self.py_, self.entry_]) QProcess().startDetached(cmd) def upgrade_handler(self): pass @before_uninstall def uninstall_handler(self): try: if os.path.exists(self.app_folder) and os.path.isdir( self.app_folder): shutil.rmtree(self.app_folder) except Exception as e: self._tip({"msg": str(e)}) finally: for name, attr in self.div.__dict__.items(): if name not in self.div.not_widget: attr.deleteLater() G.config.installed_apps.pop(self.pack_name, None) G.config.to_file() def cancel_handler(self): self.cancel = True self._transfer("msg", "Releasing...") def action_handler(self): if self.action == Actions.DOWNLOAD: self.download_handler() # self.git_download_handler() elif self.action == Actions.CANCEL: self.cancel_handler() elif self.action == Actions.RUN: self.run_handler()
def runProcess(command, arguments): process = QProcess() process.start(command, arguments) process.waitForFinished() std_output = process.readAllStandardOutput().data().decode('utf-8') return std_output.split('\n')
class NewEnvWidget(QDialog, Ui_NewEnv): """新建虚拟环境 或 导入外部环境""" def __init__(self, interpreter): super(self.__class__, self).__init__() self.setupUi(self) self.setStyleSheet(qss) self.interpreter = interpreter self.job = NewEnvObject() self.job.sig.connect(self.tip_) self.thread_pool = QThreadPool() # rex # btn self.name.textChanged.connect(self.name_change_slot) self.exis_path.textChanged.connect(self.exis_path_change_slot) self.env_radio.clicked.connect(self.env_radio_slot) self.exit_radio.clicked.connect(self.exit_radio_slot) self.path_btn.clicked.connect(self.path_btn_slot) self.exit_path_btn.clicked.connect(self.exit_path_btn_slot) self.ok_btn.clicked.connect(self.ok_btn_slot) self.cancel_btn.clicked.connect(self.close) self.ready_action() def ready_action(self): self.env_radio_slot() self.name_change_slot() self.load_py() self.path.setText(venv_path) def load_py(self): for k, v in G.config.python_path.items(): self.base_py_list.addItem(k) def name_change_slot(self): name = self.name.text() if name.strip() and name not in G.config.python_path: self.name.setStyleSheet("color:green") self.ok_btn.setEnabled(True) else: self.name.setStyleSheet("color:red") self.ok_btn.setDisabled(True) def env_radio_slot(self): self.exis_path.setDisabled(True) self.exit_path_btn.setDisabled(True) # self.path.setEnabled(True) self.path_btn.setEnabled(True) self.base_py_list.setEnabled(True) def exit_radio_slot(self): self.path.setDisabled(True) self.path_btn.setDisabled(True) self.base_py_list.setDisabled(True) # self.exis_path.setEnabled(True) self.exit_path_btn.setEnabled(True) def path_btn_slot(self): path = QFileDialog.getExistingDirectory(self, "新建虚拟环境至", '/') self.path.setText(path) def exit_path_btn_slot(self): path, _ = QFileDialog.getOpenFileName(self, "选择Python解释器", '/', 'Python Interpreter (*.exe)') if py_path_check(path): self.exis_path.setText(path) def exis_path_change_slot(self): path = self.exis_path.text() if py_path_check(path): self.exis_path.setText(path) else: self.exis_path.setText("") def ok_btn_slot(self): name = self.name.text() if self.env_radio.isChecked(): path = self.path.text() py_ = G.config.python_path[self.base_py_list.currentText()] vir_path = os.path.join(path, name) self.create_env(py_, vir_path, name) if self.exit_radio.isChecked(): path = self.exis_path.text() G.config.python_path.update({name: path}) G.config.to_file() self.close() @Slot(str) def tip_(self, msg): QMessageBox.information(self, "提示", msg) self.close() def readout_slot(self): output = self.p.readAllStandardOutput().data().decode() self.infobox.sig.msg.emit(output) def finished_slot(self): if self.infobox_flag: self.infobox.close() G.config.to_file() self.job.sig.emit("创建完成") def create_env(self, py_, vir_path, name): self.infobox = ProgressMsgDialog(msg="准备中...") self.infobox.show() self.infobox.raise_() self.infobox_flag = False self.p = QProcess() self.p.readyReadStandardOutput.connect(self.readout_slot) self.p.finished.connect(self.finished_slot) virtualenv_ = "virtualenv" img_ = G.config.get_pypi_source() cmd = " ".join([py_, "-m", 'pip', 'install', virtualenv_] + img_) self.p.start(cmd) self.p.waitForFinished() # 开始新建venv self.infobox.sig.msg.emit('新建虚拟环境中...') cmd = " ".join([py_, "-m", virtualenv_, "--no-site-packages", vir_path]) self.p.start(cmd) self.infobox_flag = True # record py_path = join_path(vir_path, 'Scripts', 'python.exe') G.config.python_path.update({name: py_path}) def closeEvent(self, event): self.interpreter.load_py() event.accept() def window_cleanup(self): self.close()
class MediaVideo(Media): def __init__(self, media, parent_widget): super(MediaVideo, self).__init__(media, parent_widget) self.widget = QWidget(parent_widget) self.process = QProcess(self.widget) self.process.setObjectName('%s-process' % self.objectName()) self.std_out = [] self.errors = [] self.stopping = False self.mute = False self.widget.setGeometry(media['geometry']) self.connect(self.process, SIGNAL('error()'), self.process_error) self.connect(self.process, SIGNAL('finished()'), self.process_finished) self.connect(self.process, SIGNAL('started()'), self.process_started) self.set_default_widget_prop() self.stop_timer = QTimer(self) self.stop_timer.setSingleShot(True) self.stop_timer.setInterval(1000) self.stop_timer.timeout.connect(self.process_timeout) #---- kong ---- for RPi self.rect = media['geometry'] #---- @Slot() def process_timeout(self): os.kill(self.process.pid(), signal.SIGTERM) self.stopping = False if not self.is_started(): self.started_signal.emit() super(MediaVideo, self).stop() @Slot(object) def process_error(self, err): print('---- process error ----') self.errors.append(err) self.stop() @Slot() def process_finished(self): self.stop() @Slot() def process_started(self): self.stop_timer.stop() if float(self.duration) > 0: self.play_timer.setInterval(int(float(self.duration) * 1000)) self.play_timer.start() self.started_signal.emit() pass @Slot() def play(self): self.finished = 0 self.widget.show() self.widget.raise_() uri = self.options['uri'] path = f'content/{uri}' #---- kong ---- for RPi left, top, right, bottom = self.rect.getCoords() rect = f'{left},{top},{right},{bottom}' args = [ '--win', rect, '--no-osd', '--layer', self.zindex, path ] self.process.start('omxplayer.bin', args) self.stop_timer.start() #---- @Slot() def stop(self, delete_widget=False): #---- kong ---- for RPi if not self.widget: return False if self.stopping or self.is_finished(): return False self.stop_timer.start() self.stopping = True if self.process.state() == QProcess.ProcessState.Running: self.process.write(b'q') self.process.waitForFinished() self.process.close() super(MediaVideo, self).stop(delete_widget) self.stopping = False self.stop_timer.stop() return True
class WebMediaView(MediaView): def __init__(self, media, parent): super(WebMediaView, self).__init__(media, parent) self.widget = QWidget(parent) self.process = QProcess(self.widget) self.process.setObjectName('%s-process' % self.objectName()) self.std_out = [] self.errors = [] self.stopping = False self.mute = False self.widget.setGeometry(media['geometry']) self.connect(self.process, SIGNAL('error()'), self.process_error) self.connect(self.process, SIGNAL('finished()'), self.process_finished) self.connect(self.process, SIGNAL('started()'), self.process_started) self.set_default_widget_prop() self.stop_timer = QTimer(self) self.stop_timer.setSingleShot(True) self.stop_timer.setInterval(1000) self.stop_timer.timeout.connect(self.process_timeout) self.rect = self.widget.geometry() @Slot() def process_timeout(self): os.kill(self.process.pid(), signal.SIGTERM) self.stopping = False if not self.is_started(): self.started_signal.emit() super(WebMediaView, self).stop() @Slot(object) def process_error(self, err): print('---- process error ----') self.errors.append(err) self.stop() @Slot() def process_finished(self): self.stop() @Slot() def process_started(self): self.stop_timer.stop() if float(self.duration) > 0: self.play_timer.setInterval(int(float(self.duration) * 1000)) self.play_timer.start() self.started_signal.emit() pass @Slot() def play(self): self.finished = 0 self.widget.show() self.widget.raise_() #---- kong ---- url = self.options['uri'] args = [ str(self.rect.left()), str(self.rect.top()), str(self.rect.width()), str(self.rect.height()), QUrl.fromPercentEncoding(QByteArray(url.encode('utf-8'))) ] #self.process.start('dist/web.exe', args) # for windows #self.process.start('./dist/web', args) # for RPi self.stop_timer.start() #---- @Slot() def stop(self, delete_widget=False): #---- kong ---- if not self.widget: return False if self.stopping or self.is_finished(): return False self.stop_timer.start() self.stopping = True if self.process.state() == QProcess.ProcessState.Running: #---- kill process ---- self.process.terminate() # for windows self.process.kill() # for linux #os.system('pkill web') # for RPi #---- self.process.waitForFinished() self.process.close() super(WebMediaView, self).stop(delete_widget) self.stopping = False self.stop_timer.stop() return True
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 MediaText(Media): def __init__(self, media, parent_widget): super(MediaText, self).__init__(media, parent_widget) self.widget = QWidget(parent_widget) self.process = QProcess(self.widget) self.process.setObjectName('%s-process' % self.objectName()) self.std_out = [] self.errors = [] self.stopping = False self.mute = False self.widget.setGeometry(media['geometry']) self.connect(self.process, SIGNAL('error()'), self.process_error) self.connect(self.process, SIGNAL('finished()'), self.process_finished) self.connect(self.process, SIGNAL('started()'), self.process_started) self.set_default_widget_prop() self.stop_timer = QTimer(self) self.stop_timer.setSingleShot(True) self.stop_timer.setInterval(1000) self.stop_timer.timeout.connect(self.process_timeout) self.rect = self.widget.geometry() @Slot() def process_timeout(self): os.kill(self.process.pid(), signal.SIGTERM) self.stopping = False if not self.is_started(): self.started_signal.emit() super(MediaText, self).stop() @Slot(object) def process_error(self, err): print('---- process error ----') self.errors.append(err) self.stop() @Slot() def process_finished(self): self.stop() @Slot() def process_started(self): self.stop_timer.stop() if float(self.duration) > 0: self.play_timer.setInterval(int(float(self.duration) * 1000)) self.play_timer.start() self.started_signal.emit() pass @Slot() def play(self): self.finished = 0 self.widget.show() self.widget.raise_() #---- kong ---- path = f'file:///home/pi/rdtone/urd/content/{self.layout_id}_{self.region_id}_{self.id}.html' print(path) l = str(self.rect.left()) t = str(self.rect.top()) w = str(self.rect.width()) h = str(self.rect.height()) s = f'--window-size={w},{h}' p = f'--window-position={l},{t}' args = [ '--kiosk', s, p, path #l, t, w, h, path ] self.process.start('chromium-browser', args) #self.process.start('./xWeb', args) self.stop_timer.start() #---- @Slot() def stop(self, delete_widget=False): #---- kong ---- if not self.widget: return False if self.stopping or self.is_finished(): return False self.stop_timer.start() self.stopping = True if self.process.state() == QProcess.ProcessState.Running: #---- kill process ---- os.system('pkill chromium') #os.system('pkill xWeb') #---- self.process.waitForFinished() self.process.close() super(MediaText, self).stop(delete_widget) self.stopping = False self.stop_timer.stop() return True