from PyQt5.QtCore import QSettings, pyqtSignal, Qt from PyQt5.QtGui import QIcon, QTextBlock, QTextCursor from PyQt5.QtWidgets import QDialog, QLabel,QComboBox, QDialogButtonBox, QFileDialog, QHBoxLayout, QMessageBox, QProgressBar, QPushButton, QTextEdit, QVBoxLayout import subprocess import threading import sys from rscder.utils.icons import IconInstance import os import sys import ai_method.subprcess_python as sp from ai_method import AI_METHOD from misc.main import AlgFrontend import abc class AIMethodDialog(QDialog): stage_end = pyqtSignal(int) stage_log = pyqtSignal(str) ENV = 'base' name = '' stages = [] setting_widget = None def __init__(self, parent = None) -> None: super().__init__(parent) # self.setting_widget:AlgFrontend = setting_widget vlayout = QVBoxLayout() hlayout = QHBoxLayout() select_label = QLabel('模式选择:') self.select_mode = QComboBox() self.select_mode.addItem('----------', 'NoValue') for stage in self.stages: self.select_mode.addItem(stage[1], stage[0]) setting_btn = QPushButton(IconInstance().SELECT, '配置') hlayout.addWidget(select_label) hlayout.addWidget(self.select_mode, 2) hlayout.addWidget(setting_btn) self.setting_args = [] def show_setting(): if self.setting_widget is None: return dialog = QDialog(parent) vlayout = QVBoxLayout() dialog.setLayout(vlayout) widget = self.setting_widget.get_widget(dialog) vlayout.addWidget(widget) dbtn_box = QDialogButtonBox(dialog) dbtn_box.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dbtn_box.button(QDialogButtonBox.Ok).setText('确定') dbtn_box.button(QDialogButtonBox.Cancel).setText('取消') dbtn_box.button(QDialogButtonBox.Ok).clicked.connect(dialog.accept) dbtn_box.button(QDialogButtonBox.Cancel).clicked.connect(dialog.reject) vlayout.addWidget(dbtn_box, 1, Qt.AlignRight) dialog.setMinimumHeight(500) dialog.setMinimumWidth(900) if dialog.exec_() == QDialog.Accepted: self.setting_args = self.setting_widget.get_params(widget) setting_btn.clicked.connect(show_setting) btnbox = QDialogButtonBox(self) btnbox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) btnbox.button(QDialogButtonBox.Ok).setText('确定') btnbox.button(QDialogButtonBox.Cancel).setText('取消') processbar = QProgressBar(self) processbar.setMaximum(1) processbar.setMinimum(0) processbar.setEnabled(False) def run_stage(): self.log = f'开始{self.current_stage}...\n' btnbox.button(QDialogButtonBox.Cancel).setEnabled(True) processbar.setMaximum(0) processbar.setValue(0) processbar.setEnabled(True) q = threading.Thread(target=self.run_stage, args=(self.current_stage,)) q.start() pass btnbox.button(QDialogButtonBox.Cancel).setEnabled(False) btnbox.accepted.connect(run_stage) btnbox.rejected.connect(self._stage_stop) self.processbar = processbar vlayout.addLayout(hlayout) self.detail = QTextEdit(self) vlayout.addWidget(self.detail) vlayout.addWidget(processbar) vlayout.addWidget(btnbox) self.detail.setReadOnly(True) # self.detail.copyAvailable(True) self.detail.setText(f'等待开始...') self.setLayout(vlayout) self.setMinimumHeight(500) self.setMinimumWidth(500) self.setWindowIcon(IconInstance().AI_DETECT) self.setWindowTitle(self.name) self.stage_end.connect(self._stage_end) self.log = f'等待开始...\n' self.stage_log.connect(self._stage_log) self.p = None @property def current_stage(self): if self.select_mode.currentData() == 'NoValue': return None return self.select_mode.currentData() @property def _python_base(self): return os.path.abspath(os.path.join(os.path.dirname(sys.executable), '..', '..')) @property def activate_env(self): script = os.path.join(self._python_base, 'Scripts','activate') if self.ENV == 'base': return script else: return script + ' ' + self.ENV @property def python_path(self): if self.ENV == 'base': return self._python_base return os.path.join(self._python_base, 'envs', self.ENV, 'python.exe') @property def workdir(self): raise NotImplementedError() @abc.abstractmethod def stage_script(self, stage): return None def _stage_log(self, l): self.log += l + '\n' self.detail.setText(self.log) cursor = self.detail.textCursor() # QTextCursor cursor.movePosition(QTextCursor.End) self.detail.setTextCursor(cursor) def closeEvent(self, e) -> None: if self.p is None: return super().closeEvent(e) dialog = QMessageBox(QMessageBox.Warning, '警告', '关闭窗口将停止' + self.current_stage + '!', QMessageBox.Ok | QMessageBox.Cancel) dialog.button(QMessageBox.Ok).clicked.connect(dialog.accepted) dialog.button(QMessageBox.Cancel).clicked.connect(dialog.rejected) dialog.show() r = dialog.exec() # print(r) # print(QMessageBox.Rejected) # print(QMessageBox.Accepted) if r == QMessageBox.Cancel: e.ignore() return return super().closeEvent(e) def _stage_stop(self): if self.p is not None: try: self.stage_log.emit(f'用户停止{self.stage}...') self.p.kill() except: pass def _stage_end(self, c): self.processbar.setMaximum(1) self.processbar.setValue(0) self.processbar.setEnabled(False) self.log += '完成!' self.detail.setText(self.log) def run_stage(self, stage): if stage is None: return ss = self.stage_script(stage) if ss is None: self.stage_log.emit(f'开始{stage}时未发现脚本') self.stage_end.emit(1) return if self.workdir is None: self.stage_log.emit(f'未配置工作目录!') self.stage_end.emit(2) return args = [ss, *self.setting_args] self.p = sp.SubprocessWraper(self.python_path, self.workdir, args, self.activate_env) for line in self.p.run(): self.stage_log.emit(line) self.stage_end.emit(self.p.returncode) from collections import OrderedDict def option_to_gui(parent, options:OrderedDict): for key in options: pass def gui_to_option(widget, options:OrderedDict): pass