class ConvertFMT(QDialog, Ui_ConverFMT, object): exception_signal = pyqtSignal(str) # 定义所有类都可以使用的信号 progressSig = pyqtSignal(int) # 控制进度条 startButtonStatusSig = pyqtSignal(list) unalignedSig = pyqtSignal(list) ##弹出识别输入文件的信号 auto_popSig = pyqtSignal(QDialog) def __init__( self, workPath=None, focusSig=None, autoFiles=None, parent=None): super(ConvertFMT, self).__init__(parent) self.parent = parent self.factory = Factory() self.thisPath = self.factory.thisPath self.workPath = workPath self.focusSig = focusSig self.autoFiles = autoFiles self.setupUi(self) # 保存设置 self.convertFmt_settings = QSettings( self.thisPath + '/settings/convertFmt_settings.ini', QSettings.IniFormat) # File only, no fallback to registry or or. self.convertFmt_settings.setFallbacksEnabled(False) # 开始装载样式表 with open(self.thisPath + os.sep + 'style.qss', encoding="utf-8", errors='ignore') as f: self.qss_file = f.read() self.setStyleSheet(self.qss_file) # 恢复用户的设置 self.guiRestore() self.exception_signal.connect(self.popupException) self.startButtonStatusSig.connect(self.factory.ctrl_startButton_status) self.progressSig.connect(self.runProgress) self.comboBox_4.installEventFilter(self) self.comboBox_4.lineEdit().autoDetectSig.connect(self.popupAutoDec) #自动识别可用的输入 self.unalignedSig.connect(self.popupUnaligns) # 给开始按钮添加菜单 menu = QMenu(self) menu.setToolTipsVisible(True) self.work_action = QAction(QIcon(":/picture/resourses/work.png"), "", menu) self.work_action.triggered.connect(lambda: self.factory.swithWorkPath(self.work_action, parent=self)) self.dir_action = QAction(QIcon(":/picture/resourses/folder.png"), "Output Dir: ", menu) self.dir_action.triggered.connect(lambda: self.factory.set_direct_dir(self.dir_action, self)) menu.addAction(self.work_action) menu.addAction(self.dir_action) self.pushButton.toolButton.setMenu(menu) self.pushButton.toolButton.menu().installEventFilter(self) self.factory.swithWorkPath(self.work_action, init=True, parent=self) # 初始化一下 ## brief demo country = self.factory.path_settings.value("country", "UK") url = "http://phylosuite.jushengwu.com/dongzhang0725.github.io/documentation/#5-8-1-Brief-example" if \ country == "China" else "https://dongzhang0725.github.io/dongzhang0725.github.io/documentation/#5-8-1-Brief-example" self.label_2.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(url))) ##自动弹出识别文件窗口 self.auto_popSig.connect(self.popupAutoDecSub) @pyqtSlot() def on_pushButton_clicked(self): """ execute program """ AllItems = self.comboBox_4.fetchListsText() if AllItems: self.dict_args = {} self.dict_args["exception_signal"] = self.exception_signal self.dict_args["unaligned_signal"] = self.unalignedSig self.dict_args["progressSig"] = self.progressSig self.dict_args["files"] = AllItems self.dict_args["workPath"] = self.workPath self.output_dir_name = self.factory.fetch_output_dir_name(self.dir_action) self.dict_args["export_path"] = self.factory.creat_dirs(self.workPath + os.sep + "convertFmt_results" + os.sep + self.output_dir_name) self.dict_args["export_phylip"] = self.checkBox.isChecked() self.dict_args["export_nex"] = self.checkBox_2.isChecked() self.dict_args["export_nexi"] = self.checkBox_3.isChecked() self.dict_args["export_axt"] = self.checkBox_13.isChecked() self.dict_args["export_paml"] = self.checkBox_5.isChecked() self.dict_args["export_fas"] = self.checkBox_12.isChecked() self.dict_args["export_stat"] = self.checkBox_9.isChecked() if True not in list(self.dict_args.values()): QMessageBox.critical( self, "Convert Sequence Format", "<p style='line-height:25px; height:25px'>Please select output format(s) first!</p>") self.checkBox.setChecked(True) return ok = self.factory.remove_dir(self.dict_args["export_path"], parent=self) if not ok: #提醒是否删除旧结果,如果用户取消,就不执行 return self.worker = WorkThread(self.run_command, parent=self) self.worker.start() else: QMessageBox.critical( self, "Convert Sequence Format", "<p style='line-height:25px; height:25px'>Please input files first!</p>") @pyqtSlot() def on_pushButton_3_clicked(self): """ open files """ files = QFileDialog.getOpenFileNames( self, "Input Files", filter="Supported Format(*.fas *.fasta *.phy *.phylip *.nex *.nxs *.nexus);;") if files[0]: self.input(files[0]) def run_command(self): try: # 先清空文件夹 time_start = datetime.datetime.now() self.startButtonStatusSig.emit( [self.pushButton, self.progressBar, "start", self.dict_args["export_path"], self.qss_file, self]) convertFmt = Convertfmt(**self.dict_args) convertFmt.exec_() if convertFmt.error_message: self.exception_signal.emit(convertFmt.error_message) # 激发这个信号 self.startButtonStatusSig.emit( [self.pushButton, self.progressBar, "except", self.dict_args["export_path"], self.qss_file, self]) elif convertFmt.unaligns: self.unalignedSig.emit(convertFmt.unaligns) self.startButtonStatusSig.emit( [self.pushButton, self.progressBar, "except", self.dict_args["export_path"], self.qss_file, self]) else: self.startButtonStatusSig.emit( [self.pushButton, self.progressBar, "stop", self.dict_args["export_path"], self.qss_file, self]) self.focusSig.emit(self.dict_args["export_path"]) time_end = datetime.datetime.now() self.time_used_des = "Start at: %s\nFinish at: %s\nTotal time used: %s\n\n" % (str(time_start), str(time_end), str(time_end - time_start)) with open(self.dict_args["export_path"] + os.sep + "summary.txt", "w", encoding="utf-8") as f: f.write("If you use PhyloSuite, please cite:\nZhang, D., F. Gao, I. Jakovlić, H. Zou, J. Zhang, W.X. Li, and G.T. Wang, PhyloSuite: An integrated and scalable desktop platform for streamlined molecular sequence data management and evolutionary phylogenetics studies. Molecular Ecology Resources, 2020. 20(1): p. 348–355. DOI: 10.1111/1755-0998.13096.\n\n" + self.time_used_des) except BaseException: self.exceptionInfo = ''.join( traceback.format_exception( *sys.exc_info())) # 捕获报错内容,只能在这里捕获,没有报错的地方无法捕获 self.exception_signal.emit(self.exceptionInfo) # 激发这个信号 self.startButtonStatusSig.emit( [self.pushButton, self.progressBar, "except", self.dict_args["export_path"], self.qss_file, self]) def guiSave(self): # Save geometry self.convertFmt_settings.setValue('size', self.size()) # self.convertFmt_settings.setValue('pos', self.pos()) for name, obj in inspect.getmembers(self): # if type(obj) is QComboBox: # this works similar to isinstance, but # missed some field... not sure why? if isinstance(obj, QCheckBox): state = obj.isChecked() self.convertFmt_settings.setValue(name, state) if isinstance(obj, QRadioButton): state = obj.isChecked() self.convertFmt_settings.setValue(name, state) def guiRestore(self): # Restore geometry size = self.factory.judgeWindowSize(self.convertFmt_settings, 561, 384) self.resize(size) self.factory.centerWindow(self) # self.move(self.convertFmt_settings.value('pos', QPoint(875, 254))) for name, obj in inspect.getmembers(self): if isinstance(obj, QComboBox): if self.autoFiles: self.input(self.autoFiles) else: self.input([]) if isinstance(obj, QCheckBox): value = self.convertFmt_settings.value( name, "true") # get stored value from registry obj.setChecked( self.factory.str2bool(value)) # restore checkbox if isinstance(obj, QRadioButton): value = self.convertFmt_settings.value( name, "true") # get stored value from registry obj.setChecked( self.factory.str2bool(value)) # restore checkbox def input(self, list_inputs): self.comboBox_4.refreshInputs(list_inputs) def runProgress(self, num): oldValue = self.progressBar.value() done_int = int(num) if done_int > oldValue: self.progressBar.setProperty("value", done_int) QCoreApplication.processEvents() def popupException(self, exception): msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setText( 'The program encountered an unforeseen problem, please report the bug at <a href="https://github.com/dongzhang0725/PhyloSuite/issues">https://github.com/dongzhang0725/PhyloSuite/issues</a> or send an email with the detailed traceback to [email protected]') msg.setWindowTitle("Error") msg.setDetailedText(exception) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def closeEvent(self, event): self.guiSave() def eventFilter(self, obj, event): # modifiers = QApplication.keyboardModifiers() if isinstance( obj, QComboBox): if event.type() == QEvent.DragEnter: if event.mimeData().hasUrls(): # must accept the dragEnterEvent or else the dropEvent # can't occur !!! event.accept() return True if event.type() == QEvent.Drop: files = [u.toLocalFile() for u in event.mimeData().urls()] files = [i for i in files if os.path.splitext(i)[1].upper() in [".FAS", ".FASTA", ".PHY", ".PHYLIP", ".NEX", ".NXS", ".NEXUS"]] self.input(files) if (event.type() == QEvent.Show) and (obj == self.pushButton.toolButton.menu()): if re.search(r"\d+_\d+_\d+\-\d+_\d+_\d+", self.dir_action.text()) or self.dir_action.text() == "Output Dir: ": self.factory.sync_dir(self.dir_action) ##同步文件夹名字 menu_x_pos = self.pushButton.toolButton.menu().pos().x() menu_width = self.pushButton.toolButton.menu().size().width() button_width = self.pushButton.toolButton.size().width() pos = QPoint(menu_x_pos - menu_width + button_width, self.pushButton.toolButton.menu().pos().y()) self.pushButton.toolButton.menu().move(pos) return True return super(ConvertFMT, self).eventFilter(obj, event) # 0 def popupUnaligns(self, message): msg = QMessageBox(self) msg.setIcon(QMessageBox.Warning) msg.setText( "<p style='line-height:25px; height:25px'>Unaligned sequences found, see details</p>") msg.setWindowTitle("Warning") msg.setDetailedText("Unaligned sequences: " + ",".join(message)) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def popupAutoDec(self, init=False): self.init = init self.factory.popUpAutoDetect("format conversion", self.workPath, self.auto_popSig, self) def popupAutoDecSub(self, popupUI): if not popupUI: if not self.init: QMessageBox.warning( self, "Warning", "<p style='line-height:25px; height:25px'>No available file detected!</p>") return if not self.init: popupUI.checkBox.setVisible(False) if popupUI.exec_() == QDialog.Accepted: widget = popupUI.listWidget_framless.itemWidget(popupUI.listWidget_framless.selectedItems()[0]) autoInputs = widget.autoInputs self.input(autoInputs)
class DrawRSCUfig(QDialog, Ui_RSCUfig, object): exception_signal = pyqtSignal(str) # 定义所有类都可以使用的信号 warning_signal = pyqtSignal(str) progressSig = pyqtSignal(int) # 控制进度条 startButtonStatusSig = pyqtSignal(list) def __init__(self, autoInputs=None, workPath=None, focusSig=None, RscriptPath=None, parent=None): super(DrawRSCUfig, self).__init__(parent) self.parent = parent self.factory = Factory() self.thisPath = self.factory.thisPath self.workPath = workPath self.focusSig = focusSig self.autoInputs = autoInputs self.RscriptPath = RscriptPath self.setupUi(self) # 保存设置 self.DrawRSCUfig_settings = QSettings( self.thisPath + '/settings/DrawRSCUfig_settings.ini', QSettings.IniFormat) # File only, no fallback to registry or or. self.DrawRSCUfig_settings.setFallbacksEnabled(False) # 开始装载样式表 with open(self.thisPath + os.sep + 'style.qss', encoding="utf-8", errors='ignore') as f: self.qss_file = f.read() self.setStyleSheet(self.qss_file) # 恢复用户的设置 self.guiRestore() self.exception_signal.connect(self.popupException) self.warning_signal.connect(self.popupWarning) self.startButtonStatusSig.connect(self.factory.ctrl_startButton_status) self.progressSig.connect(self.runProgress) self.listWidget_3.installEventFilter(self) # 给开始按钮添加菜单 menu = QMenu(self) menu.setToolTipsVisible(True) self.work_action = QAction(QIcon(":/picture/resourses/work.png"), "", menu) self.work_action.triggered.connect( lambda: self.factory.swithWorkPath(self.work_action, parent=self)) self.dir_action = QAction(QIcon(":/picture/resourses/folder.png"), "Output Dir: ", menu) self.dir_action.triggered.connect( lambda: self.factory.set_direct_dir(self.dir_action, self)) menu.addAction(self.work_action) menu.addAction(self.dir_action) self.pushButton.toolButton.setMenu(menu) self.pushButton.toolButton.menu().installEventFilter(self) self.factory.swithWorkPath(self.work_action, init=True, parent=self) # 初始化一下 ## brief demo country = self.factory.path_settings.value("country", "UK") url = "http://phylosuite.jushengwu.com/dongzhang0725.github.io/documentation/#5-14-3-1-Brief-example" if \ country == "China" else "https://dongzhang0725.github.io/dongzhang0725.github.io/documentation/#5-14-3-1-Brief-example" self.label_2.clicked.connect( lambda: QDesktopServices.openUrl(QUrl(url))) @pyqtSlot() def on_pushButton_3_clicked(self): """ open files """ fileNames = QFileDialog.getOpenFileNames(self, "Input Files", filter="CSV Format(*.csv);;") if fileNames[0]: dict_inputs = OrderedDict() for i in fileNames[0]: dict_inputs[i] = os.path.splitext(os.path.basename(i))[0] self.inputListWidget_3(dict_inputs) @pyqtSlot() def on_toolButton_T_clicked(self): ''' delete ''' listItems = self.listWidget_3.selectedItems() if not listItems: return for item in listItems: self.listWidget_3.takeItem(self.listWidget_3.row(item)) @pyqtSlot() def on_pushButton_2_clicked(self): ''' cancel ''' # ctypes.windll.kernel32.TerminateThread( # @UndefinedVariable # self.worker.handle, 0) self.close() @pyqtSlot() def on_pushButton_clicked(self): """ execute program """ dict_inputs = OrderedDict() for i in range(self.listWidget_3.count()): dict_inputs[self.listWidget_3.item( i).toolTip()] = self.listWidget_3.item(i).text() if dict_inputs: # 有数据才执行 self.dict_args = {} self.output_dir_name = self.factory.fetch_output_dir_name( self.dir_action) self.exportPath = self.factory.creat_dirs(self.workPath + os.sep + "RSCUfig_results" + os.sep + self.output_dir_name) self.dict_args["exportPath"] = self.exportPath self.dict_args["dict_files_title"] = dict_inputs self.dict_args["exception_signal"] = self.exception_signal self.dict_args["progressSig"] = self.progressSig xItems = [ self.listWidget_2.item(i).text() for i in range(self.listWidget_2.count()) ] self.dict_args["Order of x-axis"] = xItems colorItems = [ i.text() for i in [ self.pushButton_color, self.pushButton_color_2, self.pushButton_color_3, self.pushButton_color_4 ] ] self.dict_args["Color of stacks"] = colorItems self.dict_args["Figure height"] = self.spinBox_5.value() self.dict_args["Figure width"] = self.spinBox_6.value() self.dict_args["height proportion"] = self.doubleSpinBox_2.value() self.dict_args["ylim"] = self.doubleSpinBox_3.value() ok = self.factory.remove_dir(self.exportPath, parent=self) if not ok: #提醒是否删除旧结果,如果用户取消,就不执行 return self.worker = WorkThread(self.run_command, parent=self) self.worker.start() else: QMessageBox.critical( self, "Draw RSCU figure", "<p style='line-height:25px; height:25px'>Please input files first!</p>" ) def run_command(self): try: # 清空文件夹,放在这里方便统一报错 time_start = datetime.datetime.now() self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "start", self.exportPath, self.qss_file, self ]) rscriptPath = self.rscu2fig() # subprocess.call([self.RscriptPath, "--vanilla", rscriptPath], shell=True) subprocess.call("%s --vanilla %s" % (self.RscriptPath, rscriptPath), shell=True) self.progressSig.emit(100) if not os.path.exists(self.RSCUpath): self.warning_signal.emit( "No RSCU figure generated! The R packages \"ggplot2\" or \"ggpubr\" may not be installed properly. You may:<br>" " <span style='font-weight:600'>1</span>. Install \"ggplot2\" and \"ggpubr\" manually, restart PhyloSuite and try again<br>" " <span style='font-weight:600'>2</span>. Execute \"rscu_scripts.r\" script: <span style='font-weight:600; color:#ff0000;'>Rscript rscu_scripts.r</span><br>" " <span style='font-weight:600'>3</span>. Copy the content of \"rscu_scripts.r\" and paste it into Rstudio or Rgui to execute" ) self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.exportPath, self.qss_file, self ]) else: self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "stop", self.exportPath, self.qss_file, self ]) if os.path.exists(self.exportPath + os.sep + "Rplots.pdf"): os.remove(self.exportPath + os.sep + "Rplots.pdf") self.focusSig.emit(self.exportPath) time_end = datetime.datetime.now() self.time_used_des = "Start at: %s\nFinish at: %s\nTotal time used: %s\n\n" % ( str(time_start), str(time_end), str(time_end - time_start)) with open(self.exportPath + os.sep + "summary.txt", "w", encoding="utf-8") as f: f.write( "If you use PhyloSuite, please cite:\nZhang, D., F. Gao, I. Jakovlić, H. Zou, J. Zhang, W.X. Li, and G.T. Wang, PhyloSuite: An integrated and scalable desktop platform for streamlined molecular sequence data management and evolutionary phylogenetics studies. Molecular Ecology Resources, 2020. 20(1): p. 348–355. DOI: 10.1111/1755-0998.13096.\n\n" + self.time_used_des) except BaseException: self.exceptionInfo = ''.join( traceback.format_exception( *sys.exc_info())) # 捕获报错内容,只能在这里捕获,没有报错的地方无法捕获 self.exception_signal.emit(self.exceptionInfo) # 激发这个信号 self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.exportPath, self.qss_file, self ]) def guiSave(self): # Save geometry self.DrawRSCUfig_settings.setValue('size', self.size()) # self.DrawRSCUfig_settings.setValue('pos', self.pos()) for name, obj in inspect.getmembers(self): # if type(obj) is QComboBox: # this works similar to isinstance, but # missed some field... not sure why? if isinstance(obj, QListWidget): if name == "listWidget_3": dict_inputs = OrderedDict() for i in range(obj.count()): dict_inputs[obj.item(i).toolTip()] = obj.item(i).text() self.DrawRSCUfig_settings.setValue(name, dict_inputs) else: allItems = [obj.item(i).text() for i in range(obj.count())] self.DrawRSCUfig_settings.setValue(name, allItems) if isinstance(obj, QSpinBox): value = obj.value() self.DrawRSCUfig_settings.setValue(name, value) if isinstance(obj, QDoubleSpinBox): value = obj.value() self.DrawRSCUfig_settings.setValue(name, value) if isinstance(obj, QPushButton): if name in [ "pushButton_color", "pushButton_color_2", "pushButton_color_3", "pushButton_color_4" ]: color = obj.palette().color(1) self.DrawRSCUfig_settings.setValue(name, color.name()) def guiRestore(self): # Restore geometry self.resize( self.factory.judgeWindowSize(self.DrawRSCUfig_settings, 746, 566)) self.factory.centerWindow(self) # self.move(self.DrawRSCUfig_settings.value('pos', QPoint(875, 254))) for name, obj in inspect.getmembers(self): if isinstance(obj, QListWidget): if name == "listWidget_2": ini_list = [ "Gln", "His", "Asn", "Pro", "Thr", "Leu1", "Glu", "Met", "Arg", "Tyr", "Asp", "Lys", "Ala", "Ile", "Ser1", "Ser2", "Leu2", "Cys", "Trp", "Val", "Gly", "Phe" ] values = self.DrawRSCUfig_settings.value(name, ini_list) self.inputListWidget(values, obj) elif name == "listWidget_3": ini_dict_input = OrderedDict() dict_inputs = self.DrawRSCUfig_settings.value( name, ini_dict_input) if self.autoInputs: dict_inputs = OrderedDict() for i in self.autoInputs: dict_inputs[i] = os.path.splitext( os.path.basename(i))[0] self.inputListWidget_3(dict_inputs) if isinstance(obj, QSpinBox): value = self.DrawRSCUfig_settings.value(name, 8) obj.setValue(int(value)) if isinstance(obj, QDoubleSpinBox): value = self.DrawRSCUfig_settings.value(name, None) if value: obj.setValue(float(value)) if isinstance(obj, QPushButton): if obj in [ self.pushButton_color, self.pushButton_color_2, self.pushButton_color_3, self.pushButton_color_4 ]: dict_ini_colors = { "pushButton_color": "#6598C9", "pushButton_color_2": "#CB4A28", "pushButton_color_3": "#9AC664", "pushButton_color_4": "#7F5499" } ini_color = dict_ini_colors[name] color = self.DrawRSCUfig_settings.value(name, ini_color) obj.setStyleSheet("background-color:%s" % color) obj.setText(color) obj.clicked.connect(self.changePbColor) def runProgress(self, num): oldValue = self.progressBar.value() done_int = int(num) if done_int > oldValue: self.progressBar.setProperty("value", done_int) QCoreApplication.processEvents() def popupException(self, exception): rgx = re.compile(r'Permission.+?[\'\"](.+\.pdf)[\'\"]') if rgx.search(exception): pdffile = rgx.search(exception).group(1) reply = QMessageBox.critical( self, "Draw RSCU figure", "<p style='line-height:25px; height:25px'>Please close 'pdf' file first!</p>", QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes and platform.system().lower( ) == "windows": os.startfile(pdffile) else: msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setText( 'The program encountered an unforeseen problem, please report the bug at <a href="https://github.com/dongzhang0725/PhyloSuite/issues">https://github.com/dongzhang0725/PhyloSuite/issues</a> or send an email with the detailed traceback to [email protected]' ) msg.setWindowTitle("Error") msg.setDetailedText(exception) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def closeEvent(self, event): self.guiSave() def inputListWidget(self, items, listwidget): listwidget.clear() for num, i in enumerate(items): item = QListWidgetItem(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) listwidget.addItem(item) def inputListWidget_3(self, dict_items): if not dict_items: return self.listWidget_3.clear() for num, i in enumerate(dict_items): if os.path.exists(i): item = QListWidgetItem(dict_items[i]) item.setToolTip(i) item.setFlags(item.flags() | Qt.ItemIsEditable) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) self.listWidget_3.addItem(item) def rscu2fig(self): script = '''# auto install missing packages list.of.packages <- c("ggplot2", "ggpubr") new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])] if(length(new.packages)) install.packages(new.packages, repos="http://cran.us.r-project.org") library("ggplot2") scaleFUN <- function(x) sprintf("%.2f", x) #可以设置坐标轴显示的小数点位数,为了与图注保持一致\n''' allfig = [] # [1,2,3,4] sums = len(self.dict_args["dict_files_title"]) for num, i in enumerate(self.dict_args["dict_files_title"]): self.Order_name = self.factory.int2word(num + 1).strip().replace( " ", "_") self.rscu_file = os.path.normpath(i).replace("\\", "/") self.latin_name = self.dict_args["dict_files_title"][i] self.spe_number = str(num + 1) allfig.append(self.spe_number) self.Xaxis = '"' + '","'.join( self.dict_args["Order of x-axis"]) + '"' self.ylim = "%.1f" % self.dict_args["ylim"] # 生成RSCU堆积条形图代码 script_i = ''' {self.Order_name} <- read.table("{self.rscu_file}",header = TRUE,sep=",") f{self.Order_name} <- factor({self.Order_name}$Fill, levels = unique(rev({self.Order_name}$Fill))) #填充的颜色的factor #自定义横坐标顺序 x{self.Order_name} <- factor({self.Order_name}$AA, levels=c({self.Xaxis})) #横坐标 y{self.Order_name} <- {self.Order_name}$RSCU #Y值 z{self.Order_name} <- {self.Order_name}$Equality #图注的Y值 l{self.Order_name} <- {self.Order_name}$Codon #图注打标签 p{self.spe_number} <- ggplot(data = {self.Order_name}, mapping = aes(x = x{self.Order_name}, y = y{self.Order_name}, fill = f{self.Order_name},width = .7)) + geom_bar(stat = 'identity', position = 'stack')+ ggtitle("{self.latin_name}")+theme(axis.title.x=element_blank(),axis.title.y=element_text(size=10),legend.position="none",panel.background=element_blank(),panel.grid.minor=element_blank(),plot.background=element_blank(),axis.line.x = element_line(color="black", size = 0.5),axis.line.y = element_line(color="black", size = 0.5),axis.text.x=element_blank(), plot.title=element_text(family="sans",face="italic",hjust=0,size=10)) + scale_fill_manual("legend", values = c("1" = "{self.dict_args[Color of stacks][0]}", "2" = "{self.dict_args[Color of stacks][1]}", "3" = "{self.dict_args[Color of stacks][2]}", "4" = "{self.dict_args[Color of stacks][3]}"))+geom_text(mapping = aes(label = factor({self.Order_name}$aaRatio)), size = 2.5, vjust=-.2,position = position_stack()) + ylim(0,{self.ylim})+ylab("RSCU")\n'''.format( self=self) if num == (len(self.dict_args["dict_files_title"]) - 1): #最后一个要添加横坐标 script_i = script_i.replace("axis.text.x=element_blank(),", "") script += script_i if num == 0: bottom = ''' p <- ggplot(data = {self.Order_name}, mapping = aes(x = x{self.Order_name}, y = z{self.Order_name}, fill = f{self.Order_name},width = .9)) + geom_bar(stat = 'identity', position = 'stack') + geom_text(mapping = aes(label = l{self.Order_name}), size = 2.4, colour = 'white', position = position_stack(vjust=.5))+theme(axis.text.x=element_blank(),axis.text.y=element_blank(),axis.ticks=element_blank(),axis.title.x=element_blank(),axis.title.y=element_blank(),legend.position="none",panel.background=element_blank(),panel.border=element_blank(),panel.grid.major=element_blank(),panel.grid.minor=element_blank(),plot.background=element_blank(),axis.line.y = element_blank()) + scale_fill_manual("legend", values = c("1" = "{self.dict_args[Color of stacks][0]}", "2" = "{self.dict_args[Color of stacks][1]}", "3" = "{self.dict_args[Color of stacks][2]}", "4" = "{self.dict_args[Color of stacks][3]}"))\n'''.format( self=self) self.progressSig.emit((num + 1) * 50 / sums) script += bottom allfig.append("") self.allfignum = "p" + ",p".join(allfig) self.str_matrix = ",".join( ["1"] * len(self.dict_args["dict_files_title"]) + ["%.2f" % (1 / self.dict_args["height proportion"])]) # (1,1,1,0.5) self.nrow = str(len(self.dict_args["dict_files_title"]) + 1) self.RSCUpath = os.path.normpath(self.exportPath + os.sep + "RSCU.pdf").replace("\\", "/") script += ''' library("ggpubr") pall <- ggarrange({self.allfignum}, heights=c({self.str_matrix}), ncol=1, nrow={self.nrow}, align ="v") pdf("{self.RSCUpath}",width={self.dict_args[Figure width]},height={self.dict_args[Figure height]}) ## 如果觉得比例不合适,可以适当调整width和height的大小。 pall dev.off() '''.format(self=self) scriptPath = self.exportPath + os.sep + "rscu_scripts.r" with open(scriptPath, "w", encoding="utf-8") as f: f.write(script) self.progressSig.emit(60) return scriptPath def changePbColor(self): button = self.sender() ini_color = button.palette().color(1) color = QColorDialog.getColor(QColor(ini_color), self) if color.isValid(): button.setText(color.name()) button.setStyleSheet("background-color:%s" % color.name()) def eventFilter(self, obj, event): # modifiers = QApplication.keyboardModifiers() if isinstance(obj, QListWidget): if event.type() == QEvent.DragEnter: if event.mimeData().hasUrls(): # must accept the dragEnterEvent or else the dropEvent # can't occur !!! event.accept() return True if event.type() == QEvent.Drop: files = [u.toLocalFile() for u in event.mimeData().urls()] dict_inputs = {} for i in files: dict_inputs[i] = os.path.splitext(os.path.basename(i))[0] self.inputListWidget_3(dict_inputs) if (event.type() == QEvent.Show) and (obj == self.pushButton.toolButton.menu()): if re.search(r"\d+_\d+_\d+\-\d+_\d+_\d+", self.dir_action.text() ) or self.dir_action.text() == "Output Dir: ": self.factory.sync_dir(self.dir_action) ##同步文件夹名字 menu_x_pos = self.pushButton.toolButton.menu().pos().x() menu_width = self.pushButton.toolButton.menu().size().width() button_width = self.pushButton.toolButton.size().width() pos = QPoint(menu_x_pos - menu_width + button_width, self.pushButton.toolButton.menu().pos().y()) self.pushButton.toolButton.menu().move(pos) return True # 其他情况会返回系统默认的事件处理方法。 return super(DrawRSCUfig, self).eventFilter(obj, event) # 0 def popupWarning(self, text): QMessageBox.warning( self, "Warning", "<p style='line-height:25px; height:25px'>%s</p>" % text)
class GbEditor(QDialog, Ui_GBeditor, object): progressSig = pyqtSignal(int) # 控制进度条 findSig = pyqtSignal() predict_signal = pyqtSignal(str) exception_signal = pyqtSignal(str) def __init__(self, nmlgb): self.dict_args = nmlgb.dict_args super(GbEditor, self).__init__(self.dict_args["parent"]) # self.thisPath = os.path.dirname(os.path.realpath(__file__)) self.factory = Factory() self.thisPath = self.factory.thisPath # 保存设置 self.gbEditor_settings = QSettings( self.thisPath + '/settings/gbEditor_settings.ini', QSettings.IniFormat, parent=self) # File only, no fallback to registry or or. self.gbEditor_settings.setFallbacksEnabled(False) self.settings_ini = QSettings(self.thisPath + '/settings/setting_settings.ini', QSettings.IniFormat) self.settings_ini.setFallbacksEnabled(False) self.setupUi(self) # self.splitter.setStretchFactor(1, 7) self.allcontent = nmlgb.allContent self.errors = nmlgb.errors self.warings = nmlgb.warnings self.unRecognisetRNA = nmlgb.unRecognisetRNA self.dict_replace = nmlgb.dict_replace self.workPath = self.dict_args["outpath"] self.showOn() # 信号和槽 self.progressSig.connect(self.normProgress) self.findSig.connect(self.popupFindOver) self.predict_signal.connect(self.reANNT_validate) self.exception_signal.connect(lambda x: self.factory.popupException(self, x)) # 开始装载样式表 with open(self.thisPath + os.sep + 'style.qss', encoding="utf-8", errors='ignore') as f: self.qss_file = f.read() self.setStyleSheet(self.qss_file) self.installEventFilter(self) self.guiRestore() self.checkBox.toggled.connect(self.askRemoveMisc) self.removeMISC = False def showOn(self): self.tableWidget.verticalHeader().setVisible(False) self.textBrowser.setHtml(self.allcontent) self.tableWidget.setColumnCount(3) self.tableWidget.setHorizontalHeaderLabels( ['Type', 'Indices', 'Description']) error_list = [str(i + 1) for i in range(len(self.errors))] warning_list = [str(i + 1) for i in range(len(self.warings))] combo_box_options = [error_list, warning_list] data1 = [ '%d Errors' % len(error_list), '%d Warnings' % len(warning_list)] self.combo_errors = QComboBox() self.combo_warnings = QComboBox() self.combo_errors.activated.connect(self.skip_description) self.combo_warnings.activated.connect(self.skip_description) self.combo_box_widget = [self.combo_errors, self.combo_warnings] self.combo_box_data = [self.errors, self.warings] self.tableWidget.setRowCount(2) def setColortoRow(table, rowIndex, color): # 颜色 for j in range(table.columnCount()): if j != 1: itemWid = table.item(rowIndex, j) if itemWid: itemWid.setBackground(color) colors = [QColor("red"), QColor("blue")] for row in range(2): item1 = QTableWidgetItem(data1[row]) item1.setForeground(QColor("white")) self.tableWidget.setItem(row, 0, item1) for t in combo_box_options[row]: self.combo_box_widget[row].addItem(t) self.tableWidget.setCellWidget( row, 1, self.combo_box_widget[row]) if self.combo_box_data[row] != []: # label = QLabel(self.combo_box_data[row][0][1], self) # setColortoRow(self.tableWidget, row, colors[row]) # self.tableWidget.setItem(row, 2, QTableWidgetItem("")) ##必须设置一个空去item,才能设置背景色 # self.tableWidget.setCellWidget( # row, 2, label) widget = QWidget(self) hlayout = QHBoxLayout(widget) hlayout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(50, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) hlayout.addItem(spacerItem) toolButton_2 = QToolButton(self) self.factory.highlightWidgets(toolButton_2) toolButton_2.setToolTip("Configure identifiers") # toolButton_2.setAutoRaise(True) toolButton_2.clicked.connect(self.openExtractSet) toolButton_2.setIcon(QIcon(":/picture/resourses/cog.png")) hlayout.addWidget(toolButton_2) self.tableWidget.setCellWidget(row, 2, widget) # print(self.combo_box_data[row][0][1]) # if not "identifiers" in self.combo_box_data[row][0][1]: # # print("no identifiers") # self.tableWidget.cellWidget(row, 2).setVisible(False) ##设置item item2 = QTableWidgetItem(self.combo_box_data[row][0][1]) item2.setForeground(QColor("white")) self.tableWidget.setItem(row, 2, item2) setColortoRow(self.tableWidget, row, colors[row]) else: # 当没有错误时 item2 = QTableWidgetItem( "No %s to display" % data1[row].split(" ")[1]) item2.setForeground(QColor("white")) self.tableWidget.setItem(row, 2, item2) setColortoRow(self.tableWidget, row, QColor("green")) # self.tableWidget.resizeColumnsToContents() ##模拟点击 # QTimer.singleShot(100, lambda: [self.tableWidget.cellWidget(0, 1).activated.emit(0), # self.tableWidget.cellWidget(1, 1).activated.emit(0)]) def skip_description(self, index): current_comb_box = self.sender() rowIndex = self.combo_box_widget.index(current_comb_box) # 0行是error,1行是waring content_data = self.combo_box_data[rowIndex] # description item = QTableWidgetItem(content_data[index][1]) item.setForeground(QColor("white")) color = QColor("red") if rowIndex == 0 else QColor("blue") widget = self.tableWidget.cellWidget(rowIndex, 2) if "identifiers" in content_data[index][1]: widget.setVisible(True) else: widget.setVisible(False) self.tableWidget.setItem(rowIndex, 2, item) # 保持颜色 self.tableWidget.item(rowIndex, 2).setBackground(color) # 跳转到指定位置 f = Find( parent=self.textBrowser, target=content_data[index][0], sig=self.findSig) @pyqtSlot() def on_pushButton_clicked(self): # text = self.textBrowser.toPlainText() f = Find(parent=self.textBrowser) f.show() @pyqtSlot() def on_pushButton_2_clicked(self, predict=False): ''' validate, predict即预测过后执行这个 ''' currentContent = self.textBrowser.toPlainText() totalID = currentContent.count("//") # 进度条 self.progressDialog = self.factory.myProgressDialog( "Please Wait", "validating...", parent=self) ##替换掉misc_feature的内容 if self.removeMISC: rgx_miscfeature = re.compile(r"(?sm) misc_feature.+?(?= \w|^\w)") currentContent = rgx_miscfeature.sub("", currentContent) self.progressDialog.show() self.dict_args["MarkNCR"] = self.checkBox.isChecked() self.dict_args["ncrLenth"] = self.spinBox.value() self.dict_args["progressSig"] = self.progressSig self.dict_args["gbContents"] = currentContent self.dict_args["outpath"] = self.workPath self.dict_args["totalID"] = totalID nmlgb = Normalize_MT(**self.dict_args) self.progressDialog.close() self.allcontent = nmlgb.allContent self.errors = nmlgb.errors self.warings = nmlgb.warnings self.showOn() QMessageBox.information( self, "GenBank file editor", "<p style='line-height:25px; height:25px'>Validation complete!!</p>") # @pyqtSlot() # def on_pushButton_6_clicked(self): # ''' # save # ''' # currentContent = self.textBrowser.toPlainText() # gbManager = GbManager(self.workPath, parent=self) # gbManager.addRefinedContent(currentContent) # gbManager.close() # QMessageBox.information(self, # "GenBank file editor", "<p style='line-height:25px; height:25px'>File saved succesfully!</p>") @pyqtSlot() def on_pushButton_7_clicked(self): ''' PREDICT TRNA ''' if self.unRecognisetRNA: currentContent = self.textBrowser.toPlainText() self.Lg_ReANNT = Lg_ReANNT( self.unRecognisetRNA, currentContent, self.predict_signal, parent=self) # 添加最大化按钮 self.Lg_ReANNT.setWindowFlags(self.Lg_ReANNT.windowFlags() | Qt.WindowMinMaxButtonsHint) self.Lg_ReANNT.show() # self.textBrowser.setText(self.unRecognisetRNA) else: QMessageBox.information(self, "GenBank file editor", "<p style='line-height:25px; height:25px'>No tRNA(s) needing predict</p>") def normProgress(self, num): oldValue = self.progressDialog.value() done_int = int(num) if done_int > oldValue: self.progressDialog.setProperty("value", done_int) QCoreApplication.processEvents() if done_int == 100: self.progressDialog.close() def popupFindOver(self): reply = QMessageBox.information( self, "GenBank file editor", 'This item has changed, please click "validate" button to validate changes', QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes: self.on_pushButton_2_clicked() def eventFilter(self, obj, event): modifiers = QApplication.keyboardModifiers() if event.type() == QEvent.KeyPress: # 首先得判断type if (modifiers == Qt.ControlModifier) and (event.key() == Qt.Key_S): self.on_pushButton_6_clicked() return True if (modifiers == Qt.ControlModifier) and (event.key() == Qt.Key_F): self.on_pushButton_clicked() return True return super(GbEditor, self).eventFilter(obj, event) def guiSave(self): # Save geometry self.gbEditor_settings.setValue('size', self.size()) # self.gbEditor_settings.setValue('pos', self.pos()) def guiRestore(self): # Restore geometry self.resize(self.factory.judgeWindowSize(self.gbEditor_settings, 807, 612)) self.factory.centerWindow(self) # self.move(self.gbEditor_settings.value('pos', QPoint(875, 254))) def closeEvent(self, event): self.guiSave() def reANNT_validate(self, gbContent): totalID = gbContent.count("//") # 进度条 self.progressDialog = self.factory.myProgressDialog( "Please Wait", "validating...", parent=self) self.progressDialog.show() self.dict_args["progressSig"] = self.progressSig self.dict_args["gbContents"] = gbContent self.dict_args["outpath"] = self.workPath self.dict_args["totalID"] = totalID nmlgb = Normalize_MT(**self.dict_args) self.progressDialog.close() self.allcontent = nmlgb.allContent self.errors = nmlgb.errors self.warings = nmlgb.warnings self.showOn() QMessageBox.information( self, "GenBank file editor", "<p style='line-height:25px; height:25px'>Validation complete!!</p>") def openExtractSet(self): """ GenBank file extract settings """ self.extract_setting = ExtractSettings(self) self.extract_setting.closeSig.connect(self.saveExtractSet) # 添加最大化按钮 self.extract_setting.setWindowFlags(self.extract_setting.windowFlags() | Qt.WindowMinMaxButtonsHint) self.extract_setting.exec_() def saveExtractSet(self, dict_gbExtract_set): dict_settings = dict_gbExtract_set[list(dict_gbExtract_set.keys())[0]] changed = False for i in self.dict_args: if i in dict_settings and (self.dict_args[i] != dict_settings[i]): changed = True self.dict_args[i] = dict_settings[i] if changed: self.on_pushButton_2_clicked() def askRemoveMisc(self, bool_): if bool_: reply = QMessageBox.question( self, "Confirmation", "<p style='line-height:25px; height:25px'>Would you like to remove the \"misc_feature\" annotation before using \"Validate\" button?</p>", QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes: self.removeMISC = True
class CompareTable(QDialog, Ui_compareTable, object): exception_signal = pyqtSignal(str) # 定义所有类都可以使用的信号 progressSig = pyqtSignal(int) # 控制进度条 startButtonStatusSig = pyqtSignal(list) def __init__(self, autoInputs=None, workPath=None, focusSig=None, MAFFTpath=None, parent=None): super(CompareTable, self).__init__(parent) self.parent = parent self.factory = Factory() self.thisPath = self.factory.thisPath self.workPath = workPath self.mafft_exe = MAFFTpath self.setupUi(self) self.autoInputs = autoInputs self.focusSig = focusSig # 保存设置 self.CompareTable_settings = QSettings( self.thisPath + '/settings/CompareTable_settings.ini', QSettings.IniFormat) # File only, no fallback to registry or or. self.CompareTable_settings.setFallbacksEnabled(False) # 开始装载样式表 with open(self.thisPath + os.sep + 'style.qss', encoding="utf-8", errors='ignore') as f: self.qss_file = f.read() self.setStyleSheet(self.qss_file) # 恢复用户的设置 self.guiRestore() self.exception_signal.connect(self.popupException) self.startButtonStatusSig.connect(self.factory.ctrl_startButton_status) self.progressSig.connect(self.runProgress) self.listWidget.installEventFilter(self) # 给开始按钮添加菜单 menu = QMenu(self) menu.setToolTipsVisible(True) self.work_action = QAction(QIcon(":/picture/resourses/work.png"), "", menu) self.work_action.triggered.connect( lambda: self.factory.swithWorkPath(self.work_action, parent=self)) self.dir_action = QAction(QIcon(":/picture/resourses/folder.png"), "Output Dir: ", menu) self.dir_action.triggered.connect( lambda: self.factory.set_direct_dir(self.dir_action, self)) menu.addAction(self.work_action) menu.addAction(self.dir_action) self.pushButton.toolButton.setMenu(menu) self.pushButton.toolButton.menu().installEventFilter(self) self.factory.swithWorkPath(self.work_action, init=True, parent=self) # 初始化一下 ## brief demo country = self.factory.path_settings.value("country", "UK") url = "http://phylosuite.jushengwu.com/dongzhang0725.github.io/documentation/#5-14-2-1-Brief-example" if \ country == "China" else "https://dongzhang0725.github.io/dongzhang0725.github.io/documentation/#5-14-2-1-Brief-example" self.label_2.clicked.connect( lambda: QDesktopServices.openUrl(QUrl(url))) @pyqtSlot() def on_pushButton_clicked(self): """ execute program """ self.list_files = [ self.listWidget.item(i).toolTip() for i in range(self.listWidget.count()) ] if len(self.list_files) >= 2: # 有数据才执行 self.headerRows = self.spinBox.value() self.output_dir_name = self.factory.fetch_output_dir_name( self.dir_action) self.exportPath = self.factory.creat_dirs(self.workPath + os.sep + "comp_tbl_results" + os.sep + self.output_dir_name) self.ispairwiseAlign = self.checkBox.isChecked() ok = self.factory.remove_dir(self.exportPath, parent=self) if not ok: #提醒是否删除旧结果,如果用户取消,就不执行 return self.worker = WorkThread(self.run_command, parent=self) self.worker.start() else: QMessageBox.critical( self, "Compare table", "<p style='line-height:25px; height:25px'>Please input at least two files!</p>" ) @pyqtSlot() def on_pushButton_3_clicked(self): """ open files """ fileNames = QFileDialog.getOpenFileNames(self, "Input Files", filter="CSV Format(*.csv);;") if fileNames[0]: self.input(fileNames[0]) @pyqtSlot() def on_toolButton_T_clicked(self): ''' delete ''' listItems = self.listWidget.selectedItems() if not listItems: return for item in listItems: self.listWidget.takeItem(self.listWidget.row(item)) @pyqtSlot() def on_pushButton_2_clicked(self): ''' cancel ''' # ctypes.windll.kernel32.TerminateThread( # @UndefinedVariable # self.worker.handle, 0) self.close() def run_command(self): try: #清空文件夹,放在这里方便统一报错 time_start = datetime.datetime.now() # raise BaseException self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "start", self.exportPath, self.qss_file, self ]) self.gatherTable = GatherTable(self.list_files, self.ispairwiseAlign, self.exportPath, self.progressSig, self.mafft_exe, self.headerRows) self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "stop", self.exportPath, self.qss_file, self ]) self.focusSig.emit(self.exportPath) time_end = datetime.datetime.now() description = """The sequences were pairwise aligned with MAFFT (Katoh and Standley, 2013) first, then the genetic distances (identity) among sequences were calculated with the “DistanceCalculator” function in Biopython (C**k, et al., 2009) using the “identity” model.""" mafft_ref = "Katoh, K., Standley, D.M., 2013. MAFFT multiple sequence alignment software version 7: improvements in performance and usability. Mol. Biol. Evol. 30, 772-780." biopython_ref = "C**k, P.J., Antao, T., Chang, J.T., Chapman, B.A., Cox, C.J., Dalke, A., Friedberg, I., Hamelryck, T., Kauff, F., Wilczynski, B., et al. (2009). Biopython: freely available Python tools for computational molecular biology and bioinformatics. Bioinformatics 25, 1422-1423." self.time_used_des = "Start at: %s\nFinish at: %s\nTotal time used: %s\n\n" % ( str(time_start), str(time_end), str(time_end - time_start)) ps_cite = "If you use PhyloSuite, please cite:\nZhang, D., F. Gao, I. Jakovlić, H. Zou, J. Zhang, W.X. Li, and G.T. Wang, PhyloSuite: An integrated and scalable desktop platform for streamlined molecular sequence data management and evolutionary phylogenetics studies. Molecular Ecology Resources, 2020. 20(1): p. 348–355. DOI: 10.1111/1755-0998.13096.\n\n" text = ps_cite + self.time_used_des if not self.checkBox.isChecked( ) else description + "\n\n" + ps_cite + "If you use MAFFT, please cite:\n%s\n\nIf you use Biopython, please cite:\n%s\n\n" % ( mafft_ref, biopython_ref) + self.time_used_des with open(self.exportPath + os.sep + "summary.txt", "w", encoding="utf-8") as f: f.write(text) except BaseException: self.exceptionInfo = ''.join( traceback.format_exception( *sys.exc_info())) # 捕获报错内容,只能在这里捕获,没有报错的地方无法捕获 self.exception_signal.emit(self.exceptionInfo) # 激发这个信号 self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.exportPath, self.qss_file, self ]) def guiSave(self): # Save geometry self.CompareTable_settings.setValue('size', self.size()) # self.CompareTable_settings.setValue('pos', self.pos()) for name, obj in inspect.getmembers(self): # if type(obj) is QComboBox: # this works similar to isinstance, but # missed some field... not sure why? if isinstance(obj, QListWidget): allItems = [obj.item(i).toolTip() for i in range(obj.count())] self.CompareTable_settings.setValue(name, allItems) if isinstance(obj, QCheckBox): state = obj.isChecked() self.CompareTable_settings.setValue(name, state) def guiRestore(self): # Restore geometry self.resize( self.factory.judgeWindowSize(self.CompareTable_settings, 652, 483)) self.factory.centerWindow(self) # self.move(self.CompareTable_settings.value('pos', QPoint(875, 254))) for name, obj in inspect.getmembers(self): if isinstance(obj, QListWidget): if self.autoInputs: self.input(self.autoInputs) else: values = self.CompareTable_settings.value(name, []) if values: self.input(values) if isinstance(obj, QCheckBox): value = self.CompareTable_settings.value( name, "true") # get stored value from registry obj.setChecked( self.factory.str2bool(value)) # restore checkbox def runProgress(self, num): oldValue = self.progressBar.value() done_int = int(num) if done_int > oldValue: self.progressBar.setProperty("value", done_int) QCoreApplication.processEvents() def popupException(self, exception): rgx = re.compile(r'Permission.+?[\'\"](.+\.csv)[\'\"]') if rgx.search(exception): csvfile = rgx.search(exception).group(1) reply = QMessageBox.critical( self, "Compare table", "<p style='line-height:25px; height:25px'>Please close '%s' file first!</p>" % os.path.basename(csvfile), QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes and platform.system().lower( ) == "windows": os.startfile(csvfile) else: msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setText( 'The program encountered an unforeseen problem, please report the bug at <a href="https://github.com/dongzhang0725/PhyloSuite/issues">https://github.com/dongzhang0725/PhyloSuite/issues</a> or send an email with the detailed traceback to [email protected]' ) msg.setWindowTitle("Error") msg.setDetailedText(exception) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def closeEvent(self, event): self.guiSave() def eventFilter(self, obj, event): # modifiers = QApplication.keyboardModifiers() if isinstance(obj, QListWidget): if event.type() == QEvent.DragEnter: if event.mimeData().hasUrls(): # must accept the dragEnterEvent or else the dropEvent # can't occur !!! event.accept() return True if event.type() == QEvent.Drop: files = [u.toLocalFile() for u in event.mimeData().urls()] self.input(files) if (event.type() == QEvent.Show) and (obj == self.pushButton.toolButton.menu()): if re.search(r"\d+_\d+_\d+\-\d+_\d+_\d+", self.dir_action.text() ) or self.dir_action.text() == "Output Dir: ": self.factory.sync_dir(self.dir_action) ##同步文件夹名字 menu_x_pos = self.pushButton.toolButton.menu().pos().x() menu_width = self.pushButton.toolButton.menu().size().width() button_width = self.pushButton.toolButton.size().width() pos = QPoint(menu_x_pos - menu_width + button_width, self.pushButton.toolButton.menu().pos().y()) self.pushButton.toolButton.menu().move(pos) return True # 其他情况会返回系统默认的事件处理方法。 return super(CompareTable, self).eventFilter(obj, event) # 0 def input(self, files): self.listWidget.clear() for num, i in enumerate(files): if not os.path.exists(i): continue item = QListWidgetItem(os.path.basename(i)) item.setToolTip(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) self.listWidget.addItem(item)
class ExtractGB(QDialog, Ui_Extractor, object): exception_signal = pyqtSignal(str) # 定义所有类都可以使用的信号 progressSig = pyqtSignal(int) # 控制进度条 threadFinished = pyqtSignal() startButtonStatusSig = pyqtSignal(list) def __init__( self, gb_files=None, list_names=None, workPath=None, totleID=None, clearFolderSig=None, focusSig=None, parent=None): super(ExtractGB, self).__init__(parent) self.parent = parent self.function_name = "Extraction" self.setupUi(self) self.factory = Factory() self.thisPath = self.factory.thisPath self.gb_files = gb_files self.workPath = workPath self.totleID = totleID self.clearFolderSig = clearFolderSig self.list_names = list_names self.focusSig = focusSig self.installEventFilter(self) self.dict_icon = { "rectangle": ":/itol_domain/resourses/itol/re.png", "horizontal hexagon": ":/itol_domain/resourses/itol/hh.png", "vertical hexagon": ":/itol_domain/resourses/itol/hv.png", "ellipse": ":/itol_domain/resourses/itol/el.png", "rhombus (diamond)": ":/itol_domain/resourses/itol/di.png", "right pointing triangle": ":/itol_domain/resourses/itol/tr.png", "left pointing triangle": ":/itol_domain/resourses/itol/tl.png", "left pointing pentagram": ":/itol_domain/resourses/itol/pl.png", "right pointing pentagram": ":/itol_domain/resourses/itol/pr.png", "up pointing pentagram": ":/itol_domain/resourses/itol/pu.png", "down pointing pentagram": ":/itol_domain/resourses/itol/pd.png", "octagon": ":/itol_domain/resourses/itol/oc.png", "rectangle (gap)": ":/itol_domain/resourses/itol/gp.png"} self.dict_shape = { "rectangle": "RE", "horizontal hexagon": "HH", "vertical hexagon": "HV", "ellipse": "EL", "rhombus (diamond)": "DI", "right pointing triangle": "TR", "left pointing triangle": "TL", "left pointing pentagram": "PL", "right pointing pentagram": "PR", "up pointing pentagram": "PU", "down pointing pentagram": "PD", "octagon": "OC", "rectangle (gap)": "GP"} self.showOn() self.extractGB_settings = QSettings( self.thisPath + '/settings/extractGB_settings.ini', QSettings.IniFormat) # File only, no fallback to registry or or. self.extractGB_settings.setFallbacksEnabled(False) self.settings_ini = QSettings(self.thisPath + '/settings/setting_settings.ini', QSettings.IniFormat) self.settings_ini.setFallbacksEnabled(False) # 保存主界面设置 self.data_settings = QSettings( self.factory.workPlaceSettingPath + os.sep + 'data_settings.ini', QSettings.IniFormat) # File only, no fallback to registry or or. self.data_settings.setFallbacksEnabled(False) # 恢复用户的设置 self.guiRestore() # 开始装载样式表 with open(self.thisPath + os.sep + 'style.qss', encoding="utf-8", errors='ignore') as f: self.qss_file = f.read() self.setStyleSheet(self.qss_file) self.progressSig.connect(self.runProgress) self.exception_signal.connect(self.popupException) self.startButtonStatusSig.connect(self.factory.ctrl_startButton_status) self.tableWidget_2.installEventFilter(self) self.table_popMenu = QMenu(self) self.Copy = QAction("Copy", self, statusTip="Copy color(s)", shortcut="Ctrl+C", triggered=self.copyColor) self.Cut = QAction("Cut", self, statusTip="Cut color(s)", shortcut="Ctrl+X", triggered=self.cutColor) self.Paste = QAction("Paste", self, statusTip="Paste color(s)", shortcut="Ctrl+V", triggered=self.pasteColor) self.remove = QAction("Delete", self, shortcut=QKeySequence.Delete, statusTip="Remove color(s)", triggered=self.on_pushButton_9_clicked) self.table_popMenu.addAction(self.Copy) self.table_popMenu.addAction(self.Cut) self.table_popMenu.addAction(self.Paste) self.table_popMenu.addAction(self.remove) self.tableWidget_2.setContextMenuPolicy(Qt.CustomContextMenu) self.tableWidget_2.customContextMenuRequested.connect(lambda x: self.table_popMenu.exec_(QCursor.pos())) self.comboBox_6.activated[str].connect(self.switchSeqType) # self.checkBox.toggled.connect(self.judgeCodonW) self.switchSeqType(self.comboBox_6.currentText()) # 给开始按钮添加菜单 menu = QMenu(self) menu.setToolTipsVisible(True) self.work_action = QAction(QIcon(":/picture/resourses/work.png"), "", menu) self.work_action.triggered.connect(lambda: self.factory.swithWorkPath(self.work_action, parent=self)) self.dir_action = QAction(QIcon(":/picture/resourses/folder.png"), "Output Dir: ", menu) self.dir_action.triggered.connect(lambda: self.factory.set_direct_dir(self.dir_action, self)) menu.addAction(self.work_action) menu.addAction(self.dir_action) self.pushButton_2.toolButton.setMenu(menu) self.pushButton_2.toolButton.menu().installEventFilter(self) self.factory.swithWorkPath(self.work_action, init=True, parent=self) # 初始化一下 ## brief demo country = self.factory.path_settings.value("country", "UK") url = "http://phylosuite.jushengwu.com/dongzhang0725.github.io/documentation/#5-1-1-Brief-example" if \ country == "China" else "https://dongzhang0725.github.io/dongzhang0725.github.io/documentation/#5-1-1-Brief-example" self.label_3.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(url))) @pyqtSlot() def on_pushButton_2_clicked(self): """ execute program """ self.dict_args = {} self.dict_args["progressSig"] = self.progressSig self.dict_args["gb_files"] = self.gb_files self.dict_args["workPath"] = self.workPath # 创建输出文件夹 self.output_dir_name = self.factory.fetch_output_dir_name(self.dir_action) self.exportPath = self.factory.creat_dirs(self.workPath + os.sep + "extract_results" + os.sep + self.output_dir_name) self.dict_args["exportPath"] = self.exportPath self.dict_args["codon"] = str( self.comboBox_9.currentText()).split(" ")[0] self.dict_args["Name Type"] = [self.comboBox_4.itemText(i) for i in range(self.comboBox_4.count()) if self.comboBox_4.model().item(i).checkState() == Qt.Checked] # self.dict_args["Gene Number"] = str( # self.comboBox_8.currentText()) # bool self.dict_args["if ignore duplicated"] = True #self.checkBox_2.isChecked() self.dict_args["if statistics"] = True #self.checkBox_3.isChecked() self.dict_args["if itol"] = self.groupBox_4.isChecked() self.dict_args["totleID"] = self.totleID self.dict_args["seq type"] = str(self.comboBox_6.currentText()) for row, row_text in enumerate( ["atp", "nad", "cytb", "cox", "rRNA", "tRNA", "NCR"]): for column, column_text in enumerate( ["checked", "colour", "length", "shape"]): if column == 0: self.dict_args[row_text + column_text] = True if self.tableWidget.item( row, column).checkState() == Qt.Checked else False # bool elif column == 1: self.dict_args[row_text + column_text] = self.tableWidget.item(row, column).text() elif column == 2: self.dict_args[row_text + column_text] = self.tableWidget.item(row, column).text() elif column == 3: shape_combo = self.tableWidget.cellWidget(row, column) shape = shape_combo.currentText() self.dict_args[row_text + column_text] = self.dict_shape[shape] # 转为缩写形式 self.dict_args["gene interval"] = self.doubleSpinBox.value() self.dict_args["included_lineages"] = [self.comboBox_7.itemText(i) for i in range(self.comboBox_7.count()) if self.comboBox_7.model().item(i).checkState() == Qt.Checked] # {"Family":["red", "green", ""]} dict_color_array = self.getLineageColor() for i in dict_color_array: while "" in dict_color_array[i]: # 删除空行 dict_color_array[i].remove("") self.dict_args["lineage color"] = dict_color_array ##提取的设置 dict_extract_settings = copy.deepcopy(self.dict_gbExtract_set[self.comboBox_6.currentText()]) #提取所有的话,记得先判断有没有那个键 extract_all_features = dict_extract_settings.pop("extract all features") if "extract all features" \ in dict_extract_settings else False self.dict_args["extract_intergenic_regions"] = dict_extract_settings.pop("extract intergenic regions") if "extract intergenic regions" \ in dict_extract_settings else True self.dict_args["extract_overlapping_regions"] = dict_extract_settings.pop("extract overlapping regions") if "extract overlapping regions" \ in dict_extract_settings else True self.dict_args["intergenic_regions_threshold"] = dict_extract_settings.pop("intergenic regions threshold") if "intergenic regions threshold" \ in dict_extract_settings else 200 self.dict_args["overlapping_regions_threshold"] = dict_extract_settings.pop( "overlapping regions threshold") if "overlapping regions threshold" \ in dict_extract_settings else 1 self.dict_args["features"] = dict_extract_settings.pop("Features to be extracted") if not extract_all_features else "All" # self.dict_args["features"] = "All" if self.extract_all_features else self.dict_args["features"] name_unify = dict_extract_settings.pop("Names unification") self.dict_args["replace"] = {i[0]: i[1] for i in name_unify} self.dict_args["extract_list_gene"] = dict_extract_settings.pop("extract listed gene") if "extract listed gene" \ in dict_extract_settings else False self.dict_args["qualifiers"] = dict_extract_settings ###只剩下qualifier的设置 self.dict_args["extract_entire_seq"] = self.radioButton.isChecked() self.dict_args["entire_seq_name"] = self.lineEdit.text() if self.lineEdit.text() else "sequence" self.dict_args["cal_codon_bias"] = False # self.checkBox.isChecked() self.dict_args["start_gene_with"] = "cox1" if not self.lineEdit_2.text() else self.lineEdit_2.text() ok = self.factory.remove_dir(self.dict_args["exportPath"], parent=self) if not ok: return # 提醒是否删除旧结果,如果用户取消,就不执行 self.worker = WorkThread(self.run_command, parent=self) self.worker.start() def run_command(self): try: # 清空文件夹再生成结果,放在这里执行好统一管理报错 # self.clearFolderSig.emit(self.dict_args["exportPath"]) time_start = datetime.datetime.now() self.startButtonStatusSig.emit( [self.pushButton_2, self.progressBar, "start", self.dict_args["exportPath"], self.qss_file, self]) if (self.dict_args["seq type"] == "Mitogenome") and (not self.dict_args["extract_entire_seq"]): # 如果是整个序列提取,也不能用这个 extract = GBextract_MT(**self.dict_args) extract._exec() else: extract = GBextract(**self.dict_args) extract._exec() # extract = GBextract(**self.dict_args) if extract.Error_ID: stopStatus = extract.Error_ID elif extract.source_feature_IDs: stopStatus = "extract_no_feature%s"%", ".join(extract.source_feature_IDs) else: stopStatus = "stop" self.startButtonStatusSig.emit( [self.pushButton_2, self.progressBar, stopStatus, self.dict_args["exportPath"], self.qss_file, self]) self.focusSig.emit(self.dict_args["exportPath"]) time_end = datetime.datetime.now() self.time_used_des = "Start at: %s\nFinish at: %s\nTotal time used: %s\n\n" % (str(time_start), str(time_end), str(time_end - time_start)) with open(self.dict_args["exportPath"] + os.sep + "summary.txt", "w", encoding="utf-8") as f: f.write("If you use PhyloSuite, please cite:\nZhang, D., F. Gao, I. Jakovlić, H. Zou, J. Zhang, W.X. Li, " "and G.T. Wang, PhyloSuite: An integrated and scalable desktop platform for streamlined molecular " "sequence data management and evolutionary phylogenetics studies. Molecular Ecology Resources, " "2020. 20(1): p. 348–355. DOI: 10.1111/1755-0998.13096.\n\n" + self.time_used_des + "For the summary of this extraction, please see \"overview.csv\"") except BaseException: self.exceptionInfo = ''.join( traceback.format_exception( *sys.exc_info())) # 捕获报错内容,只能在这里捕获,没有报错的地方无法捕获 self.exception_signal.emit(self.exceptionInfo) # 激发这个信号 self.startButtonStatusSig.emit( [self.pushButton_2, self.progressBar, "except", self.dict_args["exportPath"], self.qss_file, self]) @pyqtSlot() def on_pushButton_clicked(self): reply = QMessageBox.question( self, "Confirmation", "<p style='line-height:25px; height:25px'>Extracter is still running, terminate it?</p>", QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes: if hasattr(self, "worker"): try: self.worker.stopWork() except: pass self.close() @pyqtSlot() def on_pushButton_6_clicked(self): """ Add row """ currentRows = self.tableWidget_2.rowCount() self.tableWidget_2.setRowCount(currentRows + 1) for column in range(self.tableWidget_2.columnCount()): item = QTableWidgetItem("") item.setToolTip("Double click to set colors") self.tableWidget_2.setItem(currentRows, column, item) @pyqtSlot() def on_pushButton_8_clicked(self): """ delete row """ selecteditems = self.tableWidget_2.selectedItems() rows = sorted(set([i.row() for i in selecteditems]), reverse=True) for row in rows: self.tableWidget_2.removeRow(row) @pyqtSlot() def on_pushButton_9_clicked(self): """ delete cell """ selecteditems = self.tableWidget_2.selectedItems() if selecteditems: for i in selecteditems: i.setText("") i.setBackground(QColor('transparent')) @pyqtSlot() def on_toolButton_3_clicked(self): """ GenBank file extract settings """ self.extract_setting = ExtractSettings(self) self.extract_setting.closeSig.connect(self.displaySettings) # 添加最大化按钮 self.extract_setting.setWindowFlags(self.extract_setting.windowFlags() | Qt.WindowMinMaxButtonsHint) self.extract_setting.exec_() @pyqtSlot() def on_pushButton_10_clicked(self): """ GenBank file lineage recognization settings """ self.setting = Setting(self) self.setting.display_table(self.setting.listWidget.item(0)) self.setting.closeSig.connect(self.updateLineageCombo) self.setting.closeSig.connect(self.updateLineageTable) self.setting.setWindowFlags(self.setting.windowFlags() | Qt.WindowMinMaxButtonsHint) self.setting.exec_() def showOn(self): list_color = [ "#ffff33", "#99ffff", "#ff9999", "#6699ff", "#DAA520", "#ccff00", "#bfbfbf"] self.tableWidget.itemClicked.connect(self.handleItemClicked) for row in range(self.tableWidget.rowCount()): # 2列颜色 (必须初始化一个item,不然恢复界面设置会出错) item2 = QTableWidgetItem(list_color[row]) item2.setBackground(QColor(list_color[row])) self.tableWidget.setItem(row, 1, item2) model = QStandardItemModel() for i in self.dict_icon: item = QStandardItem(i) item.setIcon(QIcon(self.dict_icon[i])) font = item.font() font.setPointSize(13) item.setFont(font) model.appendRow(item) comb_box = MyComboBox(self) comb_box.setModel(model) # 改变icon大小 view = comb_box.view() view.setIconSize(QSize(38, 38)) self.tableWidget.setCellWidget(row, 3, comb_box) self.tableWidget.resizeColumnsToContents() self.tableWidget.verticalHeader().setVisible(False) def handleItemClicked(self, item): if item.column() == 1: color = QColorDialog.getColor(QColor(item.text()), self) if color.isValid(): item.setText(color.name()) item.setBackground(color) self.tableWidget.clearSelection() def handleItemClicked_2(self, item): color = QColorDialog.getColor(QColor(item.text()), self) if color.isValid(): item.setText(color.name()) item.setBackground(color) self.tableWidget_2.clearSelection() def guiSave(self): # Save geometry self.extractGB_settings.setValue('size', self.size()) # self.extractGB_settings.setValue('pos', self.pos()) for name, obj in inspect.getmembers(self): # if type(obj) is QComboBox: # this works similar to isinstance, but # missed some field... not sure why? if isinstance(obj, QComboBox): # save combobox selection to registry if name in ["comboBox_7", "comboBox_4"]: dict_state = {} for i in range(obj.count()): text = obj.itemText(i) state = 2 if obj.model().item(i).checkState() == Qt.Checked else 0 dict_state[text] = state self.extractGB_settings.setValue(name, dict_state) elif name != "comboBox_5": text = obj.currentText() if text: allItems = [ obj.itemText(i) for i in range(obj.count())] allItems.remove(text) sortItems = [text] + allItems self.extractGB_settings.setValue(name, sortItems) if isinstance(obj, QGroupBox): state = obj.isChecked() self.extractGB_settings.setValue(name, state) if isinstance(obj, QCheckBox): state = obj.isChecked() self.extractGB_settings.setValue(name, state) if isinstance(obj, QRadioButton): state = obj.isChecked() self.extractGB_settings.setValue(name, state) if isinstance(obj, QTableWidget): if name == "tableWidget": array = [] # 每一行存:checked, color, length, index_text for row in range(obj.rowCount()): checked = "true" if obj.item( row, 0).checkState() == Qt.Checked else "false" colour = obj.item(row, 1).text() length = obj.item(row, 2).text() shape_combo = obj.cellWidget(row, 3) shape = shape_combo.currentText() array.append([checked, colour, length, shape]) self.extractGB_settings.setValue(name, array) elif name == "tableWidget_2": dict_color_array = self.getLineageColor() self.extractGB_settings.setValue(name, dict_color_array) if isinstance(obj, QTabWidget): index = obj.currentIndex() self.extractGB_settings.setValue(name, index) if isinstance(obj, QDoubleSpinBox): value = obj.value() self.extractGB_settings.setValue(name, value) if isinstance(obj, QLineEdit): text = obj.text() self.extractGB_settings.setValue(name, text) def guiRestore(self): # Restore geometry self.resize(self.factory.judgeWindowSize(self.extractGB_settings, 756, 661)) self.factory.centerWindow(self) # self.move(self.extractGB_settings.value('pos', QPoint(875, 254))) for name, obj in inspect.getmembers(self): if isinstance(obj, QComboBox): if name == "comboBox_5": # 展示输入 model = obj.model() obj.clear() for num, i in enumerate(self.list_names): item = QStandardItem(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) model.appendRow(item) self.changeLable() elif name == "comboBox_6": self.displaySettings() elif name == "comboBox_7": self.updateLineageCombo() elif name == "comboBox_4": key = re.sub(r"/|\\", "_", self.workPath) + "_availableInfo" value = self.data_settings.value( key, None) if value: source_keys = value[3][1] else: source_keys = None items = ["Organism", "ID", "Name", "Length", "Description", "Date"] + source_keys \ if source_keys else ["Organism", "ID", "Name", "Length"] ini_state = {}.fromkeys(items, 0) # 2代表选中 ini_state["Organism"] = 2 ini_state["Name"] = 2 dict_name_state = self.extractGB_settings.value("comboBox_4", ini_state) if type(dict_name_state) != dict: dict_name_state = ini_state self.comboBox_4.sep = "_" model = self.comboBox_4.model() self.comboBox_4.clear() for num, text in enumerate(items): item = QStandardItem(str(text)) state = dict_name_state[text] if text in dict_name_state else ini_state[text] # 测试一下 item.setCheckState(int(state)) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) model.appendRow(item) self.comboBox_4.setTopText() self.comboBox_4.view().pressed.connect(self.judgeName) else: allItems = [obj.itemText(i) for i in range(obj.count())] values = self.extractGB_settings.value(name, allItems) model = obj.model() obj.clear() for num, i in enumerate(values): item = QStandardItem(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) model.appendRow(item) if isinstance(obj, QGroupBox): # 如果没有name,就设置为True state = self.extractGB_settings.value(name, "true") obj.setChecked(self.factory.str2bool(state)) if isinstance(obj, QCheckBox): value = self.extractGB_settings.value( name, "no setting") # get stored value from registry if value != "no setting": obj.setChecked( self.factory.str2bool(value)) # restore checkbox if isinstance(obj, QRadioButton): value = self.extractGB_settings.value( name, "first") # get stored value from registry if value != "first": obj.setChecked( self.factory.str2bool(value)) # restore checkbox if isinstance(obj, QTableWidget): if name == "tableWidget": # 每一行存:checked, color, length, index_text ini_array = [ [ 'true', '#ffff33', '25', 'rectangle'], [ 'true', '#99ffff', '25', 'rectangle'], [ 'true', '#ff9999', '30', 'rectangle'], [ 'true', '#6699ff', '25', 'rectangle'], [ 'true', '#DAA520', '18', 'rectangle'], [ 'true', '#ccff00', '15', 'rectangle'], [ 'false', '#bfbfbf', '15', 'ellipse']] array = self.extractGB_settings.value(name, ini_array) for row in range(obj.rowCount()): ifChecked = Qt.Checked if array[row][ 0] == "true" else Qt.Unchecked obj.item(row, 0).setCheckState(ifChecked) obj.item(row, 1).setText(array[row][1]) obj.item(row, 1).setBackground(QColor(array[row][1])) obj.item(row, 2).setText(array[row][2]) shape = array[row][3] shape_combo = obj.cellWidget(row, 3) shape_combo_index = shape_combo.findText(shape) if shape_combo_index == -1: # add to list if not found shape_combo.insertItems(0, [value]) index = shape_combo.findText(value) shape_combo.setCurrentIndex(index) else: # preselect a combobox value by index shape_combo.setCurrentIndex(shape_combo_index) elif name == "tableWidget_2": self.updateLineageTable() if isinstance(obj, QTabWidget): index = self.extractGB_settings.value(name, 0) obj.setCurrentIndex(int(index)) if isinstance(obj, QDoubleSpinBox): value = self.extractGB_settings.value(name, 1.0) obj.setValue(float(value)) if isinstance(obj, QLineEdit): text = self.extractGB_settings.value(name, "first") if text != "first": obj.setText(text) def popupException(self, exception): rgx = re.compile(r'Permission.+?[\'\"](.+\.csv)[\'\"]') if rgx.search(exception): csvfile = rgx.search(exception).group(1) reply = QMessageBox.critical( self, "Extract sequence", "<p style='line-height:25px; height:25px'>Please close '%s' file first!</p>"%os.path.basename(csvfile), QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes and platform.system().lower() == "windows": os.startfile(csvfile) elif "Permission" in exception: reply = QMessageBox.critical( self, "Extract sequence", "<p style='line-height:25px; height:25px'>Error happened, please close the window and try again!</p>", QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes: self.close() else: msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setText( 'The program encountered an unforeseen problem, please report the bug at <a href="https://github.com/dongzhang0725/PhyloSuite/issues">https://github.com/dongzhang0725/PhyloSuite/issues</a> or send an email with the detailed traceback to [email protected]') msg.setWindowTitle("Error") msg.setDetailedText(exception) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def closeEvent(self, event): self.guiSave() def runProgress(self, num): oldValue = self.progressBar.value() done_int = int(num) if done_int > oldValue: self.progressBar.setProperty("value", done_int) QCoreApplication.processEvents() def eventFilter(self, obj, event): name = obj.objectName() if isinstance(obj, QTableWidget): if event.type() == QEvent.KeyPress: # 首先得判断type modifiers = QApplication.keyboardModifiers() if name == "tableWidget_2": if event.key() == Qt.Key_Delete: self.on_pushButton_9_clicked() return True if (modifiers == Qt.ControlModifier) and ( event.key() == Qt.Key_C): self.copyColor() return True if (modifiers == Qt.ControlModifier) and ( event.key() == Qt.Key_X): self.cutColor() return True if (modifiers == Qt.ControlModifier) and ( event.key() == Qt.Key_V): self.pasteColor() return True if (event.type() == QEvent.Show) and (obj == self.pushButton_2.toolButton.menu()): if re.search(r"\d+_\d+_\d+\-\d+_\d+_\d+", self.dir_action.text()) or self.dir_action.text() == "Output Dir: ": self.factory.sync_dir(self.dir_action) ##同步文件夹名字 menu_x_pos = self.pushButton_2.toolButton.menu().pos().x() menu_width = self.pushButton_2.toolButton.menu().size().width() button_width = self.pushButton_2.toolButton.size().width() pos = QPoint(menu_x_pos - menu_width + button_width, self.pushButton_2.toolButton.menu().pos().y()) self.pushButton_2.toolButton.menu().move(pos) return True return super(ExtractGB, self).eventFilter(obj, event) def changeLable(self): count = str(self.comboBox_5.count()) self.label_4.setText("Inputs (" + count + "):") def getLineageColor(self): dict_color_array = OrderedDict() columnNum = self.tableWidget_2.columnCount() rowNum = self.tableWidget_2.rowCount() for column in range(columnNum): headerText = self.tableWidget_2.horizontalHeaderItem(column).text() list_columnText = [] for row in range(rowNum): if self.tableWidget_2.item(row, column): list_columnText.append( self.tableWidget_2.item(row, column).text()) dict_color_array[headerText] = list_columnText return dict_color_array def copyColor(self): selecteditems = self.tableWidget_2.selectedItems() if selecteditems: list_selItem_text = [i.text() for i in selecteditems] QApplication.clipboard().setText("\t".join(list_selItem_text)) def cutColor(self): selecteditems = self.tableWidget_2.selectedItems() list_selItem_text = [] if selecteditems: for i in selecteditems: list_selItem_text.append(i.text()) i.setText("") i.setBackground(QColor('transparent')) QApplication.clipboard().setText("\t".join(list_selItem_text)) def pasteColor(self): colors = QApplication.clipboard().text() list_colors = re.split(r"\s+|,", colors) while "" in list_colors: list_colors.remove("") selecteditems = self.tableWidget_2.selectedItems() for num, i in enumerate(selecteditems): if (num+1) <= len(list_colors): color = list_colors[num] qcolor = QColor(color) if not qcolor.isValid(): continue i.setText(color) i.setBackground(qcolor) def displaySettings(self): ###提取的设置 self.GenBankExtract_settings = QSettings( self.thisPath + '/settings/GenBankExtract_settings.ini', QSettings.IniFormat) # File only, no fallback to registry or or. self.GenBankExtract_settings.setFallbacksEnabled(False) self.dict_gbExtract_set = self.GenBankExtract_settings.value("set_version") # self.extract_list_gene = self.factory.str2bool(self.GenBankExtract_settings.value("extract listed gene", # "false")) # self.extract_all_features = self.factory.str2bool(self.GenBankExtract_settings.value("extract all features", # # "false")) if self.dict_gbExtract_set: self.allVersions = list(self.dict_gbExtract_set.keys()) model = self.comboBox_6.model() self.comboBox_6.clear() for num, i in enumerate(self.allVersions + [">>>More<<<"]): item = QStandardItem(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) model.appendRow(item) self.switchSeqType(self.comboBox_6.currentText()) def switchSeqType(self, text): ###保存version顺序 if text == ">>>More<<<": country = self.factory.path_settings.value("country", "UK") url = "http://phylosuite.jushengwu.com/dongzhang0725.github.io/PhyloSuite-demo/customize_extraction/" if \ country == "China" else "https://dongzhang0725.github.io/dongzhang0725.github.io/PhyloSuite-demo/customize_extraction/" QDesktopServices.openUrl(QUrl(url)) self.comboBox_6.setCurrentIndex(0) return if text != self.allVersions[0]: if text in self.allVersions: list_now_versions = copy.deepcopy(self.allVersions) list_now_versions.remove(text) reorder_list = [text] + list_now_versions self.dict_gbExtract_set = OrderedDict((i, self.dict_gbExtract_set[i]) for i in reorder_list) self.GenBankExtract_settings.setValue('set_version', self.dict_gbExtract_set) ###控制itol if text != "Mitogenome": targetIndex = "null" for index in range(self.tabWidget.count()): if self.tabWidget.tabText(index) == "Gene order display": targetIndex = index if targetIndex != "null": self.hiddenIndex = targetIndex self.hiddenWidget = self.tabWidget.widget(self.hiddenIndex) self.hiddenTabText = self.tabWidget.tabText(self.hiddenIndex) self.tabWidget.removeTab(self.hiddenIndex) self.hiddenFlag = True else: if hasattr(self, "hiddenFlag") and self.hiddenFlag: self.tabWidget.insertTab(self.hiddenIndex, self.hiddenWidget, self.hiddenTabText) self.hiddenFlag = False self.tabWidget.setCurrentIndex(0) def updateLineageTable(self): # tableWidget_2 self.tableWidget_2.itemDoubleClicked.connect(self.handleItemClicked_2) header, array = self.factory.getCurrentTaxSetData() countColumn = len(header) self.tableWidget_2.setColumnCount(countColumn) ini_dict_array = OrderedDict() for i in header: ini_dict_array[i] = [""] * 6 familyColor = [ "#81C7D6", "#FF0033", "#6A00D1", "#49BF4E", "#AA538B", "#FF99CC"] if "Family" in ini_dict_array: ini_dict_array["Family"] = familyColor # ini_dict_array[ # "Family"] = familyColor if "Family" in ini_dict_array else ini_dict_array["Family"] self.tableWidget_2.setHorizontalHeaderLabels( header) dict_array = self.extractGB_settings.value( "tableWidget_2", ini_dict_array) maxRow = len(max(list(dict_array.values()), key=len)) self.tableWidget_2.setRowCount(maxRow) if len(ini_dict_array) > len(dict_array): # 如果用户添加了lineage list_differ = list( set(ini_dict_array.keys()).difference(set(dict_array.keys()))) for i in list_differ: # 加上这个lineage dict_array[i] = [""] * maxRow for column, columnText in enumerate(header): for row, rowText in enumerate(dict_array[columnText]): item = QTableWidgetItem(rowText) item.setToolTip("Double click to set colors") color = QColor(rowText) if color.isValid(): item.setBackground(color) self.tableWidget_2.setItem(row, column, item) def updateLineageCombo(self): header, array = self.factory.getCurrentTaxSetData() ini_state = {}.fromkeys(header, 2) # 2代表选中 dict_lng_state = self.extractGB_settings.value("comboBox_7", ini_state) model = self.comboBox_7.model() self.comboBox_7.clear() for num, text in enumerate(header): item = QStandardItem(text) state = dict_lng_state[text] if text in dict_lng_state else ini_state[text] # 测试一下 item.setCheckState(int(state)) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) model.appendRow(item) self.comboBox_7.setTopText() def judgeName(self): if not set(["Organism", "ID", "Name"]).intersection(set(self.comboBox_4.topText.split("_"))): QMessageBox.information( self, "Information", "<p style='line-height:25px; height:25px'>At least one of the following should be retained: \"Organism\", \"ID\", \"Name\"</p>") self.comboBox_4.item.setCheckState(Qt.Checked) self.comboBox_4.setTopText() def judgeCodonW(self, bool_): if bool_: pass
class TrimAl(QDialog, Ui_trimAl, object): exception_signal = pyqtSignal(str) # 定义所有类都可以使用的信号 progressSig = pyqtSignal(int) # 控制进度条 startButtonStatusSig = pyqtSignal(list) logGuiSig = pyqtSignal(str) trimAl_exception = pyqtSignal(str) workflow_progress = pyqtSignal(int) workflow_finished = pyqtSignal(str) # 用于输入文件后判断用 ui_closeSig = pyqtSignal(str) # 用于flowchart自动popup combobox等操作 showSig = pyqtSignal(QDialog) closeSig = pyqtSignal(str, str) ##弹出识别输入文件的信号 auto_popSig = pyqtSignal(QDialog) def __init__(self, workPath=None, TApath=None, autoInputs=None, focusSig=None, workflow=None, parent=None): super(TrimAl, self).__init__(parent) self.parent = parent self.function_name = "trimAl" self.workflow = workflow self.factory = Factory() self.thisPath = self.factory.thisPath self.workPath = workPath self.focusSig = focusSig self.TApath = TApath self.autoInputs = autoInputs self.setupUi(self) # 保存设置 if not workflow: self.trimAl_settings = QSettings( self.thisPath + '/settings/trimAl_settings.ini', QSettings.IniFormat) else: self.trimAl_settings = QSettings( self.thisPath + '/settings/workflow_settings.ini', QSettings.IniFormat) self.trimAl_settings.beginGroup("Workflow") self.trimAl_settings.beginGroup("temporary") self.trimAl_settings.beginGroup('trimAl') # File only, no fallback to registry or or. self.trimAl_settings.setFallbacksEnabled(False) # 开始装载样式表 with open(self.thisPath + os.sep + 'style.qss', encoding="utf-8", errors='ignore') as f: self.qss_file = f.read() self.setStyleSheet(self.qss_file) # 恢复用户的设置 self.guiRestore() self.exception_signal.connect(self.popupException) self.startButtonStatusSig.connect(self.factory.ctrl_startButton_status) self.progressSig.connect(self.runProgress) self.logGuiSig.connect(self.addText2Log) self.trimAl_exception.connect(self.popup_trimAl_exception) self.comboBox_4.lineEdit().autoDetectSig.connect( self.popupAutoDec) # 自动识别可用的输入 self.comboBox_4.installEventFilter(self) self.lineEdit_2.installEventFilter(self) self.lineEdit_3.installEventFilter(self) self.log_gui = self.gui4Log() # stat output file name self.lineEdit_2.setLineEditNoChange(True) self.lineEdit_3.setLineEditNoChange(True) self.lineEdit_2.deleteFile.clicked.connect( self.clear_lineEdit) # 删除了内容,也要把tooltip删掉 self.lineEdit_3.deleteFile.clicked.connect( self.clear_lineEdit) # 删除了内容,也要把tooltip删掉 # 选框联动 self.checkBox_2.toggled.connect(self.switchCheckBox1) self.checkBox_5.toggled.connect(self.switchCheckBox1) # self.groupBox_5.toggled.connect(lambda bool_: self.groupBox_6.setChecked(not bool_)) # self.groupBox_6.toggled.connect(lambda bool_: self.groupBox_5.setChecked(not bool_)) self.checkBox_3.toggled.connect(self.switchCheckBox2) self.checkBox_6.toggled.connect(self.switchCheckBox2) self.checkBox_7.toggled.connect(self.switchCheckBox2) self.checkBox_8.toggled.connect(self.switchCheckBox2) self.checkBox_9.toggled.connect(self.judgeInput) self.radioButton.toggled.connect(self.Un_checkBox_9) self.comboBox_2.currentTextChanged.connect(self.judgeComboBox_2) self.comboBox.currentTextChanged.connect(self.judgeFormats) self.radioButton.toggled.connect(self.controlRefineText) self.interrupt = False # 给开始按钮添加菜单 menu = QMenu(self) menu.setToolTipsVisible(True) action = QAction(QIcon(":/picture/resourses/terminal-512.png"), "View | Edit command", menu, triggered=self.showCMD) self.work_action = QAction(QIcon(":/picture/resourses/work.png"), "", menu) self.work_action.triggered.connect( lambda: self.factory.swithWorkPath(self.work_action, parent=self)) self.dir_action = QAction(QIcon(":/picture/resourses/folder.png"), "Output Dir: ", menu) self.dir_action.triggered.connect( lambda: self.factory.set_direct_dir(self.dir_action, self)) menu.addAction(action) menu.addAction(self.work_action) menu.addAction(self.dir_action) self.pushButton.toolButton.setMenu(menu) self.pushButton.toolButton.menu().installEventFilter(self) self.factory.swithWorkPath(self.work_action, init=True, parent=self) # 初始化一下 ## brief demo country = self.factory.path_settings.value("country", "UK") url = "http://phylosuite.jushengwu.com/dongzhang0725.github.io/documentation/#5-4-1-Brief-example" if \ country == "China" else "https://dongzhang0725.github.io/dongzhang0725.github.io/documentation/#5-4-1-Brief-example" self.label_7.clicked.connect( lambda: QDesktopServices.openUrl(QUrl(url))) ##自动弹出识别文件窗口 self.auto_popSig.connect(self.popupAutoDecSub) @pyqtSlot() def on_pushButton_clicked(self): """ execute program """ self.command = self.fetchCommands() if self.command: self.interrupt = False self.error_has_shown = False #保证只报一次错 self.list_pids = [] self.queue = multiprocessing.Queue() thread = int(self.comboBox_6.currentText()) thread = thread if len( self.dict_args["inputFiles"]) > thread else len( self.dict_args["inputFiles"]) thread = 1 if not self.dict_args[ "inputFiles"] else thread # compare的情况 self.pool = multiprocessing.Pool(processes=thread, initializer=pool_init, initargs=(self.queue, )) # Check for progress periodically self.timer = QTimer() self.timer.timeout.connect(self.updateProcess) self.timer.start(1) self.worker = WorkThread(self.run_command, parent=self) self.worker.start() @pyqtSlot() def on_pushButton_9_clicked(self): """ show log """ self.log_gui.show() @pyqtSlot() def on_pushButton_3_clicked(self): """ alignment file """ fileNames = QFileDialog.getOpenFileNames(self, "Input alignment file") if fileNames[0]: self.input(fileNames[0]) @pyqtSlot() def on_pushButton_4_clicked(self): """ set file for comparison """ fileName = QFileDialog.getOpenFileName( self, "Input file containing alignment path to compare") if fileName[0]: base = os.path.basename(fileName[0]) self.lineEdit_2.setText(base) self.lineEdit_2.setToolTip(fileName[0]) @pyqtSlot() def on_pushButton_22_clicked(self): """ matrix file """ fileName = QFileDialog.getOpenFileName(self, "Input matrix file") if fileName[0]: base = os.path.basename(fileName[0]) self.lineEdit_3.setText(base) self.lineEdit_3.setToolTip(fileName[0]) @pyqtSlot() def on_pushButton_2_clicked(self, quiet=False): """ Stop """ if self.isRunning(): if (not self.workflow) and (not quiet): reply = QMessageBox.question( self, "Confirmation", "<p style='line-height:25px; height:25px'>trimAl is still running, terminate it?</p>", QMessageBox.Yes, QMessageBox.Cancel) else: reply = QMessageBox.Yes if reply == QMessageBox.Yes: try: self.worker.stopWork() self.pool.terminate( ) # Terminate all processes in the Pool ## 删除subprocess if platform.system().lower() == "windows": for pid in self.list_pids: os.popen('taskkill /F /T /PID %s' % pid) else: for pid in self.list_pids: os.killpg(os.getpgid(pid), signal.SIGTERM) self.pool = None self.interrupt = True except: self.pool = None self.interrupt = True if (not self.workflow) and (not quiet): QMessageBox.information( self, "trimAl", "<p style='line-height:25px; height:25px'>Program has been terminated!</p>" ) self.startButtonStatusSig.emit([ self.pushButton, [self.progressBar], "except", self.dict_args["exportPath"], self.qss_file, self ]) def run_command(self): try: # 清空文件夹,放在这里方便统一报错 time_start = datetime.datetime.now() self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "start", self.dict_args["exportPath"], self.qss_file, self ]) ##进度条用 # self.dict_file_progress = {os.path.basename(file): 0 for file in self.dict_args["seq_files"]} if self.radioButton.isChecked(): async_results = [ self.pool.apply_async(run, args=(self.dict_args, self.command, file)) for file in self.dict_args["inputFiles"] ] self.totalFileNum = len(self.dict_args["inputFiles"]) else: async_results = [ self.pool.apply_async(run, args=(self.dict_args, self.command, self.dict_args["compareFile"])) ] self.totalFileNum = 1 self.finishedFileNum = 0 #进度条用 self.pool.close() # 关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成 map(ApplyResult.wait, async_results) lst_results = [r.get() for r in async_results] # 判断比对是否成功 trimAl_results = glob.glob(self.exportPath + os.sep + "*_trimAl.fas") empty_files = [ os.path.basename(file) for file in trimAl_results if os.stat(file).st_size == 0 ] has_error = False if not trimAl_results or empty_files: has_error = True log = self.textEdit_log.toPlainText() list_commands = re.findall(r"Command: (.+)\n", log) last_cmd = list_commands[-1] if list_commands else "" self.trimAl_exception.emit( "trimAl execute failed, click <span style=\"color:red\">Show log</span> to see details!" "<br>You can also copy this command to terminal to debug: %s" % last_cmd) if not has_error: self.renameSequence() #修剪后序列的名字被,需要改回来 time_end = datetime.datetime.now() self.time_used = str(time_end - time_start) self.time_used_des = "Start at: %s\nFinish at: %s\nTotal time used: %s\n\n" % ( str(time_start), str(time_end), self.time_used) with open(self.exportPath + os.sep + "summary.txt", "w", encoding="utf-8") as f: f.write( self.description + "\n\nIf you use PhyloSuite, please cite:\nZhang, D., F. Gao, I. Jakovlić, H. Zou, J. Zhang, W.X. Li, and G.T. Wang, PhyloSuite: An integrated and scalable desktop platform for streamlined molecular sequence data management and evolutionary phylogenetics studies. Molecular Ecology Resources, 2020. 20(1): p. 348–355. DOI: 10.1111/1755-0998.13096.\n" "If you use trimAl, please cite:\n" + self.reference + "\n\n" + self.time_used_des) if (not self.interrupt) and (not has_error): self.pool = None self.interrupt = False if self.workflow: # work flow跑的 self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "workflow stop", self.exportPath, self.qss_file, self ]) self.workflow_finished.emit("finished") return self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "stop", self.exportPath, self.qss_file, self ]) self.focusSig.emit(self.exportPath) else: self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.exportPath, self.qss_file, self ]) self.pool = None self.interrupt = False except BaseException: self.exceptionInfo = ''.join( traceback.format_exception( *sys.exc_info())) # 捕获报错内容,只能在这里捕获,没有报错的地方无法捕获 self.exception_signal.emit(self.exceptionInfo) # 激发这个信号 self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.dict_args["exportPath"], self.qss_file, self ]) self.pool = None self.interrupt = False def guiSave(self): # Save geometry self.trimAl_settings.setValue('size', self.size()) # self.trimAl_settings.setValue('pos', self.pos()) for name, obj in inspect.getmembers(self): # if type(obj) is QComboBox: # this works similar to isinstance, but # missed some field... not sure why? if isinstance(obj, QComboBox): # save combobox selection to registry index = obj.currentIndex() self.trimAl_settings.setValue(name, index) elif isinstance(obj, QCheckBox): state = obj.isChecked() self.trimAl_settings.setValue(name, state) elif isinstance(obj, QRadioButton): state = obj.isChecked() self.trimAl_settings.setValue(name, state) elif isinstance(obj, QDoubleSpinBox): float_ = obj.value() self.trimAl_settings.setValue(name, float_) elif isinstance(obj, QLineEdit): value = obj.text() self.trimAl_settings.setValue(name, value) elif isinstance(obj, QTabWidget): index = obj.currentIndex() self.trimAl_settings.setValue(name, index) def guiRestore(self): # Restore geometry self.resize( self.factory.judgeWindowSize(self.trimAl_settings, 748, 594)) self.factory.centerWindow(self) # self.move(self.trimAl_settings.value('pos', QPoint(875, 254))) for name, obj in inspect.getmembers(self): if isinstance(obj, QComboBox): if name == "comboBox_6": cpu_num = multiprocessing.cpu_count() list_cpu = [str(i + 1) for i in range(cpu_num)] index = self.trimAl_settings.value(name, "0") model = obj.model() obj.clear() for num, i in enumerate(list_cpu): item = QStandardItem(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) model.appendRow(item) obj.setCurrentIndex(int(index)) elif name == "comboBox_4": self.input(self.autoInputs) else: allItems = [obj.itemText(i) for i in range(obj.count())] index = self.trimAl_settings.value(name, "0") model = obj.model() obj.clear() for num, i in enumerate(allItems): item = QStandardItem(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) item.setToolTip(i) model.appendRow(item) obj.setCurrentIndex(int(index)) elif isinstance(obj, QCheckBox): value = self.trimAl_settings.value( name, "no setting") # get stored value from registry if value != "no setting": obj.setChecked( self.factory.str2bool(value)) # restore checkbox elif isinstance(obj, QRadioButton): value = self.trimAl_settings.value( name, "no setting") # get stored value from registry if value != "no setting": obj.setChecked( self.factory.str2bool(value)) # restore checkbox elif isinstance(obj, QDoubleSpinBox): ini_float_ = obj.value() float_ = self.trimAl_settings.value(name, ini_float_) obj.setValue(float(float_)) elif isinstance(obj, QLineEdit): if name not in ["lineEdit_3", "lineEdit_2"]: value = self.trimAl_settings.value( name, "") # get stored value from registry if value: obj.setText(value) # restore checkbox elif isinstance(obj, QTabWidget): index = self.trimAl_settings.value(name, 0) obj.setCurrentIndex(int(index)) def runProgress(self, num): oldValue = self.progressBar.value() done_int = int(num) if done_int > oldValue: self.progressBar.setProperty("value", done_int) QCoreApplication.processEvents() def popupException(self, exception): msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setText( 'The program encountered an unforeseen problem, please report the bug at <a href="https://github.com/dongzhang0725/PhyloSuite/issues">https://github.com/dongzhang0725/PhyloSuite/issues</a> or send an email with the detailed traceback to [email protected]' ) msg.setWindowTitle("Error") msg.setDetailedText(exception) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def closeEvent(self, event): self.guiSave() self.log_gui.close() # 关闭子窗口 self.closeSig.emit("trimAl", self.fetchWorkflowSetting()) # 断开showSig和closeSig的槽函数连接 try: self.showSig.disconnect() except: pass try: self.closeSig.disconnect() except: pass if self.workflow: self.ui_closeSig.emit("trimAl") # 自动跑的时候不杀掉程序 return if self.isRunning(): reply = QMessageBox.question( self, "trimAl", "<p style='line-height:25px; height:25px'>trimAl is still running, terminate it?</p>", QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes: try: self.worker.stopWork() self.pool.terminate( ) # Terminate all processes in the Pool ## 删除subprocess if platform.system().lower() == "windows": for pid in self.list_pids: os.popen('taskkill /F /T /PID %s' % pid) else: for pid in self.list_pids: os.killpg(os.getpgid(pid), signal.SIGTERM) self.pool = None self.interrupt = True except: self.pool = None self.interrupt = True else: event.ignore() def showEvent(self, event): QTimer.singleShot(100, lambda: self.showSig.emit(self)) def eventFilter(self, obj, event): # modifiers = QApplication.keyboardModifiers() name = obj.objectName() if isinstance(obj, QComboBox): if event.type() == QEvent.DragEnter: if event.mimeData().hasUrls(): # must accept the dragEnterEvent or else the dropEvent # can't occur !!! event.accept() return True if event.type() == QEvent.Drop: files = [u.toLocalFile() for u in event.mimeData().urls()] self.input(files) if isinstance(obj, QLineEdit): if event.type() == QEvent.DragEnter: if event.mimeData().hasUrls(): # must accept the dragEnterEvent or else the dropEvent # can't occur !!! event.accept() return True if event.type() == QEvent.Drop: files = [u.toLocalFile() for u in event.mimeData().urls()] if name == "lineEdit_2": base = os.path.basename(files[0]) self.lineEdit_2.setText(base) self.lineEdit_2.setToolTip(files[0]) elif name == "lineEdit_3": base = os.path.basename(files[0]) self.lineEdit_3.setText(base) self.lineEdit_3.setToolTip(files[0]) if (event.type() == QEvent.Show) and (obj == self.pushButton.toolButton.menu()): if re.search(r"\d+_\d+_\d+\-\d+_\d+_\d+", self.dir_action.text() ) or self.dir_action.text() == "Output Dir: ": self.factory.sync_dir(self.dir_action) ##同步文件夹名字 menu_x_pos = self.pushButton.toolButton.menu().pos().x() menu_width = self.pushButton.toolButton.menu().size().width() button_width = self.pushButton.toolButton.size().width() pos = QPoint(menu_x_pos - menu_width + button_width, self.pushButton.toolButton.menu().pos().y()) self.pushButton.toolButton.menu().move(pos) return True # return QMainWindow.eventFilter(self, obj, event) # # 其他情况会返回系统默认的事件处理方法。 return super(TrimAl, self).eventFilter(obj, event) # 0 def gui4Log(self): dialog = QDialog(self) dialog.resize(800, 500) dialog.setWindowTitle("Log") gridLayout = QGridLayout(dialog) horizontalLayout_2 = QHBoxLayout() label = QLabel(dialog) label.setText("Log of trimAl:") horizontalLayout_2.addWidget(label) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) horizontalLayout_2.addItem(spacerItem) toolButton = QToolButton(dialog) icon2 = QIcon() icon2.addPixmap( QPixmap( ":/picture/resourses/interface-controls-text-wrap-512.png")) toolButton.setIcon(icon2) toolButton.setCheckable(True) toolButton.setToolTip("Use Wraps") toolButton.clicked.connect(self.setWordWrap) toolButton.setChecked(True) horizontalLayout_2.addWidget(toolButton) pushButton = QPushButton("Save to file", dialog) icon = QIcon() icon.addPixmap(QPixmap(":/picture/resourses/Save-icon.png")) pushButton.setIcon(icon) pushButton_2 = QPushButton("Close", dialog) icon = QIcon() icon.addPixmap(QPixmap(":/picture/resourses/if_Delete_1493279.png")) pushButton_2.setIcon(icon) self.textEdit_log = QTextEdit(dialog) self.textEdit_log.setReadOnly(True) gridLayout.addLayout(horizontalLayout_2, 0, 0, 1, 2) gridLayout.addWidget(self.textEdit_log, 1, 0, 1, 2) gridLayout.addWidget(pushButton, 2, 0, 1, 1) gridLayout.addWidget(pushButton_2, 2, 1, 1, 1) pushButton.clicked.connect(self.save_log_to_file) pushButton_2.clicked.connect(dialog.close) dialog.setWindowFlags(dialog.windowFlags() | Qt.WindowMinMaxButtonsHint) return dialog def addText2Log(self, text): if re.search(r"\w+", text): self.textEdit_log.append(text) with open(self.exportPath + os.sep + "PhyloSuite_TrimAl_.log", "a", errors='ignore') as f: f.write(text + "\n") def save_log_to_file(self): content = self.textEdit_log.toPlainText() fileName = QFileDialog.getSaveFileName(self, "trimAl", "log", "text Format(*.txt)") if fileName[0]: with open(fileName[0], "w", encoding="utf-8") as f: f.write(content) def setWordWrap(self): button = self.sender() if button.isChecked(): button.setChecked(True) self.textEdit_log.setLineWrapMode(QTextEdit.WidgetWidth) else: button.setChecked(False) self.textEdit_log.setLineWrapMode(QTextEdit.NoWrap) def input(self, list_items=None): if list_items: self.comboBox_4.refreshInputs(list_items) else: self.comboBox_4.refreshInputs([]) def showCMD(self): """ show command """ self.command = self.fetchCommands() if self.command: dialog = QDialog(self) dialog.resize(600, 200) dialog.setWindowTitle("Command") gridLayout = QGridLayout(dialog) label = QLabel(dialog) label.setText("Current Command:") pushButton = QPushButton("Save and run", dialog) icon = QIcon() icon.addPixmap(QPixmap(":/picture/resourses/Save-icon.png")) pushButton.setIcon(icon) pushButton_2 = QPushButton("Close", dialog) icon = QIcon() icon.addPixmap( QPixmap(":/picture/resourses/if_Delete_1493279.png")) pushButton_2.setIcon(icon) self.textEdit_cmd = QTextEdit(dialog) self.textEdit_cmd.setText(self.command) self.textEdit_cmd.textChanged.connect(self.judgeCmdText) gridLayout.addWidget(label, 0, 0, 1, 2) gridLayout.addWidget(self.textEdit_cmd, 1, 0, 1, 2) gridLayout.addWidget(pushButton, 2, 0, 1, 1) gridLayout.addWidget(pushButton_2, 2, 1, 1, 1) pushButton.clicked.connect(lambda: [ self.run_with_CMD(self.textEdit_cmd.toPlainText()), dialog.close() ]) pushButton_2.clicked.connect(dialog.close) dialog.setWindowFlags(dialog.windowFlags() | Qt.WindowMinMaxButtonsHint) dialog.exec_() def clear_lineEdit(self): sender = self.sender() lineEdit = sender.parent() lineEdit.setText("") lineEdit.setToolTip("") def fetchCommands(self): if (self.radioButton.isChecked() and self.comboBox_4.count()) or \ (self.radioButton_2.isChecked() and self.lineEdit_2.toolTip()): self.interrupt = False self.error_has_shown = False self.dict_args = {} self.dict_args["workPath"] = self.workPath self.output_dir_name = self.factory.fetch_output_dir_name( self.dir_action) self.exportPath = self.factory.creat_dirs(self.workPath + \ os.sep + "trimAl_results" + os.sep + self.output_dir_name) self.dict_args["exportPath"] = self.exportPath ok = self.factory.remove_dir(self.exportPath, parent=self) if not ok: # 提醒是否删除旧结果,如果用户取消,就不执行 return if self.tabWidget.tabText( self.tabWidget.currentIndex()) == "Automated Trimming": # 自动模式 method = self.getAutoMethod() self.dict_args["autoMethod"] = " -%s" % method self.dict_args["cons"] = "" self.dict_args["gt"] = "" self.dict_args["st"] = "" self.dict_args["ct"] = "" self.dict_args["w"] = "" self.dict_args["gw"] = "" self.dict_args["sw"] = "" self.dict_args["cw"] = "" else: # 人工模式 self.dict_args["autoMethod"] = "" self.dict_args[ "cons"] = " -cons %.1f" % self.doubleSpinBox.value() self.dict_args[ "gt"] = " -gt %.1f" % self.doubleSpinBox_2.value() self.dict_args[ "st"] = " -st %.1f" % self.doubleSpinBox_3.value() self.dict_args[ "ct"] = " -ct %.1f" % self.doubleSpinBox_4.value( ) if self.checkBox_9.isChecked() else "" self.dict_args["w"] = " -w %.1f" % self.doubleSpinBox_7.value( ) if self.checkBox_3.isChecked() else "" self.dict_args[ "gw"] = " -gw %.1f" % self.doubleSpinBox_6.value( ) if self.checkBox_6.isChecked() else "" self.dict_args[ "sw"] = " -sw %.1f" % self.doubleSpinBox_5.value( ) if self.checkBox_7.isChecked() else "" self.dict_args[ "cw"] = " -cw %.1f" % self.doubleSpinBox_8.value( ) if self.checkBox_8.isChecked() else "" self.dict_args[ "complementary"] = " -complementary" if self.checkBox.isChecked( ) else "" self.dict_args[ "colnumbering"] = " -colnumbering" if self.checkBox_2.isChecked( ) else "" html_file_name = self.lineEdit_4.text() if self.lineEdit_4.text( ) else "summary.html" self.dict_args["htmlout"] = " -htmlout \"%s\"" % ( self.dict_args["exportPath"] + os.sep + "$fileBase$_" + html_file_name) if self.checkBox_4.isChecked() else "" self.dict_args["stat"] = " -%s" % self.comboBox_2.currentText( ).split(":")[0] if self.checkBox_5.isChecked() else "" self.dict_args["outFormat"] = " -%s" % self.comboBox.currentText() # 输出文件后缀 dict_suffix = { "fasta": ".fas", "phylip": ".phy", "phylip3.2": ".phy", "nexus": ".nex", "mega": ".meg", "clustal": ".clw", "nbrf": ".nbrf" } self.dict_args["suffix"] = dict_suffix[self.comboBox.currentText()] self.dict_args["trimAl"] = self.TApath ##输入文件 if self.radioButton.isChecked(): self.dict_args["compareFile"] = "" self.dict_args["inputFiles"] = self.comboBox_4.fetchListsText() command = "\"{trimAl}\" -in alignment{autoMethod}{cons}{gt}{st}{ct}" \ "{w}{gw}{sw}{cw}{complementary}{colnumbering}{htmlout}" \ "{outFormat} -out outputFile{stat}".format(**self.dict_args) else: # compare self.dict_args["inputFiles"] = [] self.dict_args["compareFile"] = self.lineEdit_2.toolTip() command = "\"{trimAl}\" -compareset setFile{autoMethod}{cons}{gt}{st}{ct}" \ "{w}{gw}{sw}{cw}{complementary}{colnumbering}{htmlout}{outFormat}" \ " -out outputFile{stat}".format(**self.dict_args) self.reference = "Capella-Gutierrez S, Silla-Martinez JM, Gabaldon T. 2009. trimAl: a tool for automated" \ " alignment trimming in large-scale phylogenetic analyses. Bioinformatics. 25: 1972-1973. " \ "doi: 10.1093/bioinformatics/btp348." cmd_used = "{autoMethod}{cons}{gt}{st}{ct}{w}{gw}{sw}{cw}".format( **self.dict_args).strip() self.description = "Gap sites were removed with trimAl (Capella‐Gutiérrez et al., 2009) using \"%s\" command." % cmd_used self.textEdit_log.clear() # 清空 return command else: if self.radioButton.isChecked(): QMessageBox.critical( self, "trimAl", "<p style='line-height:25px; height:25px'>Please input files to \"Input\" box first!</p>" ) else: QMessageBox.critical( self, "trimAl", "<p style='line-height:25px; height:25px'>Please input files to \"Compare set\" box first!</p>" ) def isRunning(self): '''判断程序是否运行,依赖进程是否存在来判断''' return hasattr(self, "pool") and self.pool and not self.interrupt def run_with_CMD(self, cmd): self.command = cmd if self.command: self.interrupt = False self.error_has_shown = False self.list_pids = [] self.queue = multiprocessing.Queue() thread = int(self.comboBox_6.currentText()) thread = thread if len( self.dict_args["inputFiles"]) > thread else len( self.dict_args["inputFiles"]) thread = 1 if not self.dict_args[ "inputFiles"] else thread # compare的情况 self.pool = multiprocessing.Pool(processes=thread, initializer=pool_init, initargs=(self.queue, )) # # Check for progress periodically self.timer = QTimer() self.timer.timeout.connect(self.updateProcess) self.timer.start(1) self.worker = WorkThread(self.run_command, parent=self) self.worker.start() def judgeCmdText(self): text = self.textEdit_cmd.toPlainText() if self.radioButton.isChecked(): if " -in alignment" not in text: QMessageBox.information( self, "trimAl", "<p style='line-height:25px; height:25px'>\"-in alignment\" cannot be changed!</p>" ) self.textEdit_cmd.undo() else: if " -compareset setFile" not in text: QMessageBox.information( self, "trimAl", "<p style='line-height:25px; height:25px'>\"-compareset setFile\" cannot be changed!</p>" ) self.textEdit_cmd.undo() def updateProcess(self): if self.queue.empty(): return info = self.queue.get() if info[0] == "log": message = info[1] self.logGuiSig.emit(message) elif info[0] == "prog": self.finishedFileNum += 1 if not self.interrupt: self.workflow_progress.emit(self.finishedFileNum * 95 / self.totalFileNum) self.progressSig.emit(self.finishedFileNum * 95 / self.totalFileNum) elif info[0] == "popen": self.list_pids.append(info[1]) elif info[0] == "error": self.on_pushButton_2_clicked(quiet=True) #杀掉进程 self.trimAl_exception.emit( "Error happened! Click <span style='font-weight:600; color:#ff0000;'>Show log</span> to see detail!" ) self.error_has_shown = True elif info[0] == "popen finished": if info[1] in self.list_pids: self.list_pids.remove(info[1]) def popup_trimAl_exception(self, text): if not self.error_has_shown: QMessageBox.critical( self, "trimAl", "<p style='line-height:25px; height:25px'>%s</p>" % text) if "Show log" in text: self.on_pushButton_9_clicked() def judgeComboBox_2(self, text): if text in ["sfc: Print compare values for columns in the selected alignment from compare files method.", "sft: Print accumulated compare values count for the selected alignment from compare files method."] \ and self.radioButton.isChecked(): QMessageBox.information( self, "trimAl", "<p style='line-height:25px; height:25px'>This option should be used in combination with \"Compare set\".</p>" ) self.comboBox_2.setCurrentIndex(0) def switchCheckBox1(self, bool_): checkbox = self.sender() if (checkbox == self.checkBox_2) and bool_ and self.checkBox_5.isChecked(): self.checkBox_5.setChecked(False) if (checkbox == self.checkBox_5) and bool_ and self.checkBox_2.isChecked(): self.checkBox_2.setChecked(False) def switchCheckBox2(self, bool_): checkbox = self.sender() if checkbox == self.checkBox_3: if bool_: for i in [self.checkBox_6, self.checkBox_7, self.checkBox_8]: i.setChecked(not bool_) else: if bool_: self.checkBox_3.setChecked(not bool_) def judgeInput(self, bool_): if bool_ and (not self.radioButton_2.isChecked()): QMessageBox.information( self, "trimAl", "<p style='line-height:25px; height:25px'>This option should be used in combination with \"Compare set\".</p>" ) self.checkBox_9.setChecked(False) def Un_checkBox_9(self, bool_): if bool_: self.checkBox_9.setChecked(not bool_) def popupAutoDec(self, init=False): self.init = init self.factory.popUpAutoDetect("trimAl", self.workPath, self.auto_popSig, self) def popupAutoDecSub(self, popupUI): if not popupUI: if not self.init: QMessageBox.warning( self, "Warning", "<p style='line-height:25px; height:25px'>No available file detected!</p>" ) return if not self.init: popupUI.checkBox.setVisible(False) if popupUI.exec_() == QDialog.Accepted: widget = popupUI.listWidget_framless.itemWidget( popupUI.listWidget_framless.selectedItems()[0]) autoInputs = widget.autoInputs self.input(autoInputs) def judgeFormats(self, text): if text != "fasta": QMessageBox.warning( self, "HmmCleaner", "<p style='line-height:25px; height:25px'>\"%s\" format cannot be used by the downstream programs " "(e.g. concatenation), please select fasta format if you are going to use " "this result for other functions.</p>" % text) def renameSequence(self): trimedFiles = glob.glob(self.exportPath + os.sep + "*_trimAl.fas") if trimedFiles: for num, file in enumerate(trimedFiles): with open(file, encoding="utf-8", errors="ignore") as f: content = f.read() with open(file, "w", encoding="utf-8") as f1: f1.write(re.sub(r"(>.+?) \d+ bp", "\\1", content)) self.progressSig.emit(95 + (5 * (num + 1) / len(trimedFiles))) self.workflow_progress.emit(95 + (5 * (num + 1) / len(trimedFiles))) def fetchWorkflowSetting(self): '''* Alignment Mode * Code table(if codon mode) * strategy * export format''' settings = '''<p class="title">***trimAl***</p>''' trim_strategy = self.tabWidget.tabText(self.tabWidget.currentIndex()) settings += '<p>Trimming strategy: <a href="self.trimAl_exe' \ ' factory.highlightWidgets(x.tabWidget.tabBar())">%s</a></p>' % trim_strategy if trim_strategy == "Automated Trimming": # settings += "|--Automated Trimming--|" strategy = self.getAutoMethod() settings += '<p>Automated method: <a href="self.trimAl_exe ' \ 'factory.highlightWidgets(x.radioButton_10,x.radioButton_11,x.radioButton_12,' \ 'x.radioButton_13,x.radioButton_14,x.radioButton_15)">%s</a></p>' % strategy else: # settings += "|--Manual Trimming--|" cons = self.doubleSpinBox.value() settings += '<p>Minimum percentage of positions to conserve [0-100]: <a href="self.trimAl_exe ' \ 'doubleSpinBox.setFocus() doubleSpinBox.selectAll()' \ ' factory.highlightWidgets(x.doubleSpinBox)">%s</a></p>' % cons gaps = self.doubleSpinBox_2.value() settings += '<p>Gap threshold, fraction of positions without gaps in a column [0-1]: <a href="self.trimAl_exe ' \ 'doubleSpinBox_2.setFocus() doubleSpinBox_2.selectAll()' \ ' factory.highlightWidgets(x.doubleSpinBox_2)">%s</a></p>' % gaps ss = self.doubleSpinBox_3.value() settings += '<p>Similarity threshold, minimum level of residue similarity within a column [0-1]: <a href="self.trimAl_exe ' \ 'doubleSpinBox_3.setFocus() doubleSpinBox_3.selectAll()' \ ' factory.highlightWidgets(x.doubleSpinBox_3)">%s</a></p>' % ss if self.checkBox_3.isChecked(): w = self.doubleSpinBox_7.value() settings += '<p>General window size, applied to all stats: <a href="self.trimAl_exe ' \ 'doubleSpinBox_7.setFocus() doubleSpinBox_7.selectAll()' \ ' factory.highlightWidgets(x.doubleSpinBox_7)">%s</a></p>' % w if self.checkBox_6.isChecked(): gw = self.doubleSpinBox_6.value() settings += '<p>Window size applied to Gaps: <a href="self.trimAl_exe ' \ 'doubleSpinBox_6.setFocus() doubleSpinBox_6.selectAll()' \ ' factory.highlightWidgets(x.doubleSpinBox_6)">%s</a></p>' % gw if self.checkBox_7.isChecked(): sw = self.doubleSpinBox_5.value() settings += '<p>Window size applied to Similarity: <a href="self.trimAl_exe ' \ 'doubleSpinBox_5.setFocus() doubleSpinBox_5.selectAll()' \ ' factory.highlightWidgets(x.doubleSpinBox_5)">%s</a></p>' % sw if self.checkBox_8.isChecked(): cw = self.doubleSpinBox_8.value() settings += '<p>Window size applied to Consistency: <a href="self.trimAl_exe ' \ 'doubleSpinBox_8.setFocus() doubleSpinBox_8.selectAll()' \ ' factory.highlightWidgets(x.doubleSpinBox_8)">%s</a></p>' % cw thread = self.comboBox_6.currentText() settings += '<p>Thread: <a href="self.trimAl_exe comboBox_6.showPopup()' \ ' factory.highlightWidgets(x.comboBox_6)">%s</a></p>' % thread return settings def isFileIn(self): return self.comboBox_4.count() def controlRefineText(self, bool_): if bool_: if not self.comboBox_4.count(): self.comboBox_4.lineEdit().switchColor( "No input files (Try to drag your file(s) and drop here)") else: self.comboBox_4.lineEdit().switchColor() else: self.comboBox_4.lineEdit().switchColor() def getAutoMethod(self): widgets = (self.gridLayout_8.itemAt(i).widget() for i in range(self.gridLayout_8.count())) checkedBox = [ widget for widget in widgets if isinstance(widget, QRadioButton) and widget.isChecked() ][0] return checkedBox.text()
class Lg_addFiles(QDialog, Ui_addFile, object): closeGUI_signal = pyqtSignal() inputSig = pyqtSignal(list, list, bool, str) inputContentSig = pyqtSignal(str, str) exception_signal = pyqtSignal(str) progressDiologSig = pyqtSignal(int) fastaDownloadFinishedSig = pyqtSignal(str) inputFasSig = pyqtSignal(str, str) def __init__(self, exportPath=None, parent=None): super(Lg_addFiles, self).__init__(parent) self.factory = Factory() self.parent = parent self.thisPath = self.factory.thisPath self.exportPath = exportPath # 保存设置 self.addFiles_settings = QSettings( self.thisPath + '/settings/addFiles_settings.ini', QSettings.IniFormat) self.closeGUI_signal.connect(self.close) self.progressDiologSig.connect(self.runProgressDialog) # self.close() 不能在信号槽里面 self.fastaDownloadFinishedSig.connect(self.parent.setTreeViewFocus) # 开始装载样式表 with open(self.thisPath + os.sep + 'style.qss', encoding="utf-8", errors='ignore') as f: self.qss_file = f.read() self.setStyleSheet(self.qss_file) self.setupUi(self) self.guiRestore() self.exception_signal.connect(self.popupException) self.interrupt = False country = self.factory.path_settings.value("country", "UK") url = "http://phylosuite.jushengwu.com/dongzhang0725.github.io/documentation/#4-3-1-1-Brief-example" if \ country == "China" else "https://dongzhang0725.github.io/dongzhang0725.github.io/documentation/#4-3-1-1-Brief-example" self.label_3.clicked.connect(lambda : QDesktopServices.openUrl(QUrl(url))) def fetSeqFromNCBI(self, id_array): batch_size = 20 count = len(id_array) download_contents = "" for start in range(0, count, batch_size): if self.interrupt: return end = min(count, start + batch_size) print("Going to download record %i to %i" % (start + 1, end)) if (start + batch_size) > count: batch_size = count - start Entrez.email = self.email if self.email else "*****@*****.**" fetch_handle = Entrez.efetch(db="nucleotide", rettype=self.rettype, retmode="text", retstart=start, retmax=batch_size, id=id_array) download_contents += fetch_handle.read() self.progressDiologSig.emit(end * 100 / count) if self.rettype == "gb": self.inputContentSig.emit( download_contents, self.outputPath) else: with open(self.outputPath + os.sep + self.fasta_file_name, "w", encoding="utf-8") as f: f.write(download_contents) self.fastaDownloadFinishedSig.emit(self.outputPath) # result_handle = Entrez.efetch( # db="nucleotide", rettype="gb", id=id_array, retmode="text") # # with open(self.exportPath + os.sep + "new.gb", "w", encoding="utf-8") as f2: # # f2.write(result_handle.read()) # self.inputContentSig.emit( # result_handle.read(), []) @pyqtSlot() def on_pushButton_clicked(self): self.outputPath = self.fetchOutputPath() if self.parent.isWorkFolder(self.outputPath, mode="gb"): files = QFileDialog.getOpenFileNames( self, "Input GenBank Files", filter="GenBank and Fasta Format (*.gb *.gbk *.gbf *.gp *.gbff *.fas *.fasta);;") if files[0]: list_gbs = [] list_fas = [] for i in files[0]: if os.path.splitext(i)[1].upper() in [".GB", ".GBK", ".GP", ".GBF", ".GBFF"]: list_gbs.append(i) elif os.path.splitext(i)[1].upper() in [".FAS", ".FASTA"]: list_fas.append(i) if list_gbs: self.inputSig.emit(list_gbs, [], False, self.outputPath) if list_fas: fasContent = "" for i in list_fas: with open(i, encoding="utf-8", errors='ignore') as f: fasContent += f.read() + "\n" self.inputFasSig.emit(fasContent, self.outputPath) self.close() self.deleteLater() del self else: files = QFileDialog.getOpenFileNames( self, "Input Files", filter="Supported Format (*.docx *.doc *.odt *.docm *.dotx *.dotm *.dot " "*.fas *.fasta *.phy *.phylip *.nex *.nxs *.nexus);;") if files[0]: self.inputSig.emit([], files[0], False, self.outputPath) self.close() self.deleteLater() del self @pyqtSlot() def on_pushButton_2_clicked(self): # download self.interrupt = False text_content = self.plainTextEdit.toPlainText() if text_content: self.outputPath = self.fetchOutputPath() self.rettype = "gb" if self.parent.isWorkFolder(self.outputPath, mode="gb") else "fasta" if self.rettype == "fasta": name, ok = QInputDialog.getText( self, 'Set output file name', 'Output file name:', text="sequence.fas") if ok: self.fasta_file_name = name + ".fas" if "." not in name else name if os.path.exists(self.outputPath + os.sep + self.fasta_file_name): reply = QMessageBox.question( self, "Concatenate sequence", "<p style='line-height:25px; height:25px'>The file exists, replace it?</p>", QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Cancel: return else: QMessageBox.information( self, "Information", "<p style='line-height:25px; height:25px'>Download canceled!</p>") return self.downloadState("start") self.progressDialog = self.factory.myProgressDialog( "Please Wait", "Downloading...", parent=self) self.progressDialog.show() self.id_array = re.split(r"\s|,", text_content) while "" in self.id_array: self.id_array.remove("") self.email = self.lineEdit.text() self.worker_download = WorkThread(self.run_command, parent=self) self.progressDialog.canceled.connect(lambda: [setattr(self, "interrupt", True), self.worker_download.stopWork(), self.progressDialog.close(), self.downloadState("stop")]) self.worker_download.start() else: QMessageBox.information( self, "Information", "<p style='line-height:25px; height:25px'>Please input ID(s) first</p>") @pyqtSlot() def on_pushButton_8_clicked(self): #search in NCBI self.close() self.parent.on_SerhNCBI_triggered() @pyqtSlot() def on_toolButton_clicked(self): info = '''To make use of NCBI's E-utilities, NCBI requires you to specify your email address with each request.<br> In case of excessive usage of the E-utilities, NCBI will attempt to contact a user at the email address provided before blocking access to the E-utilities.''' QMessageBox.information( self, "Information", "<p style='line-height:25px; height:25px'>%s</p>"%info) def run_command(self): try: self.fetSeqFromNCBI(self.id_array) self.closeGUI_signal.emit() except BaseException: self.exceptionInfo = ''.join( traceback.format_exception( *sys.exc_info())) # 捕获报错内容,只能在这里捕获,没有报错的地方无法捕获 self.exception_signal.emit(self.exceptionInfo) # self.popupException(self.exceptionInfo) # 激发这个信号 def popupException(self, exception): msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setText( 'The program encountered an unforeseen problem, please report the bug at <a href="https://github.com/dongzhang0725/PhyloSuite/issues">https://github.com/dongzhang0725/PhyloSuite/issues</a> or send an email with the detailed traceback to [email protected]') msg.setWindowTitle("Error") msg.setDetailedText(exception) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() self.pushButton_2.setEnabled(True) self.pushButton_2.setStyleSheet(self.qss_file) self.pushButton_2.setText("Start") def guiSave(self): # Save geometry self.addFiles_settings.setValue('size', self.size()) # self.addFiles_settings.setValue('pos', self.pos()) # for name, obj in inspect.getmembers(self): # if type(obj) is QComboBox: # this works similar to isinstance, but # missed some field... not sure why? # if isinstance(obj, QTextBrowser): # # save combobox selection to registry # htmlText = obj.toHtml() # self.addFiles_settings.setValue(name, htmlText) def guiRestore(self): # Restore geometry self.resize(self.factory.judgeWindowSize(self.addFiles_settings, 620, 500)) self.factory.centerWindow(self) # self.move(self.addFiles_settings.value('pos', QPoint(875, 254))) for name, obj in inspect.getmembers(self): if isinstance(obj, QComboBox): if name == "comboBox": allItems = self.factory.fetchAllWorkFolders(self.exportPath) model = obj.model() obj.clear() for num, i in enumerate(allItems): if self.parent.isWorkFolder(i, mode="gb"): text = "\"%s\" in GenBank_File (GenBank format)"%os.path.basename(i) else: text = "\"%s\" in Other_File" % os.path.basename(i) item = QStandardItem(text) item.setToolTip(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) model.appendRow(item) if self.parent.isWorkFolder(self.exportPath, mode="gb"): obj.setCurrentText("\"%s\" in GenBank_File (GenBank format)"%os.path.basename(self.exportPath)) else: obj.setCurrentText("\"%s\" in Other_File" % os.path.basename(self.exportPath)) def closeEvent(self, event): self.interrupt = True if hasattr(self, "worker_download"): self.worker_download.stopWork() if hasattr(self, "progressDialog"): self.progressDialog.close() self.guiSave() def runProgressDialog(self, num): oldValue = self.progressDialog.value() done_int = int(num) if done_int > oldValue: self.progressDialog.setProperty("value", done_int) QCoreApplication.processEvents() if done_int == 100: self.progressDialog.close() def fetchOutputPath(self): index = self.comboBox.currentIndex() return self.comboBox.itemData(index, role=Qt.ToolTipRole) def downloadState(self, state): if state == "start": self.pushButton_2.setEnabled(False) # 使之失效 self.pushButton_2.setStyleSheet( 'QPushButton {color: red; background-color: rgb(219, 217, 217)}') self.pushButton_2.setText("Downloading...") elif state == "stop": self.pushButton_2.setText("Download") self.pushButton_2.setStyleSheet(self.qss_file) self.pushButton_2.setEnabled(True)
class Matrix(QDialog, Ui_Matrix, object): exception_signal = pyqtSignal(str) # 定义所有类都可以使用的信号 # warning_signal = pyqtSignal(dict) progressSig = pyqtSignal(int) # 控制进度条 startButtonStatusSig = pyqtSignal(list) unalignedSig = pyqtSignal(list) workflow_progress = pyqtSignal(int) workflow_finished = pyqtSignal(str) ##用于flowchart自动popup combobox等操作 showSig = pyqtSignal(QDialog) closeSig = pyqtSignal(str, str) ##用于输入文件后判断用 ui_closeSig = pyqtSignal(str) ##弹出识别输入文件的信号 auto_popSig = pyqtSignal(QDialog) def __init__(self, files=None, workPath=None, focusSig=None, workflow=False, parent=None): super(Matrix, self).__init__(parent) self.parent = parent self.function_name = "Concatenation" self.factory = Factory() self.thisPath = self.factory.thisPath # 保存设置 if not workflow: self.concatenate_settings = QSettings( self.thisPath + '/settings/concatenate_settings.ini', QSettings.IniFormat) else: self.concatenate_settings = QSettings( self.thisPath + '/settings/workflow_settings.ini', QSettings.IniFormat) self.concatenate_settings.beginGroup("Workflow") self.concatenate_settings.beginGroup("temporary") self.concatenate_settings.beginGroup('Concatenate Sequence') # File only, no fallback to registry or or. self.concatenate_settings.setFallbacksEnabled(False) self.setupUi(self) self.files = files self.workPath = workPath self.focusSig = focusSig if focusSig else pyqtSignal( str) # 为了方便workflow self.workflow = workflow # 恢复用户的设置 self.guiRestore() # 开始装载样式表 with open(self.thisPath + os.sep + 'style.qss', encoding="utf-8", errors='ignore') as f: self.qss_file = f.read() self.setStyleSheet(self.qss_file) self.groupBox_top_line.setStyleSheet('''QGroupBox{ border-bottom:none; border-right:none; border-left:none; } QGroupBox::title{ subcontrol-origin: margin; subcontrol-position: top left; }''') self.unalignedSig.connect(self.unaligned) self.exception_signal.connect(self.popupException) # self.warning_signal.connect(self.popupWarning) self.startButtonStatusSig.connect(self.factory.ctrl_startButton_status) self.progressSig.connect(self.runProgress) self.comboBox_4.installEventFilter(self) self.comboBox_4.lineEdit().autoDetectSig.connect( self.popupAutoDec) #自动识别可用的输入 ##设置拖拽排序 self.comboBox_4.view().setDragEnabled(True) self.comboBox_4.view().setDragDropMode(QAbstractItemView.InternalMove) self.comboBox_4.view().setDefaultDropAction(Qt.MoveAction) # self.comboBox_4.view().setSelectionMode(QAbstractItemView.MultiSelection) self.comboBox_4.view().installEventFilter(self) self.checkBox_6.toggled.connect( lambda: self.input(self.comboBox_4.fetchListsText())) # 给开始按钮添加菜单 menu = QMenu(self) menu.setToolTipsVisible(True) self.work_action = QAction(QIcon(":/picture/resourses/work.png"), "", menu) self.work_action.triggered.connect( lambda: self.factory.swithWorkPath(self.work_action, parent=self)) self.dir_action = QAction(QIcon(":/picture/resourses/folder.png"), "Output Dir: ", menu) self.dir_action.triggered.connect( lambda: self.factory.set_direct_dir(self.dir_action, self)) menu.addAction(self.work_action) menu.addAction(self.dir_action) self.pushButton.toolButton.setMenu(menu) self.pushButton.toolButton.menu().installEventFilter(self) self.factory.swithWorkPath(self.work_action, init=True, parent=self) # 初始化一下 ## brief demo country = self.factory.path_settings.value("country", "UK") url = "http://phylosuite.jushengwu.com/dongzhang0725.github.io/documentation/#5-7-1-Brief-example" if \ country == "China" else "https://dongzhang0725.github.io/dongzhang0725.github.io/documentation/#5-7-1-Brief-example" self.label_2.clicked.connect( lambda: QDesktopServices.openUrl(QUrl(url))) ##自动弹出识别文件窗口 self.auto_popSig.connect(self.popupAutoDecSub) @pyqtSlot() def on_pushButton_clicked(self): """ execute program """ if self.isFileIn(): self.dict_args = {} self.dict_args["parent"] = self self.dict_args["exception_signal"] = self.exception_signal # self.dict_args["warning_signal"] = self.warning_signal self.dict_args["unaligned_signal"] = self.unalignedSig self.dict_args["progressSig"] = self.progressSig self.dict_args["workflow_progress"] = self.workflow_progress self.dict_args["files"] = self.comboBox_4.fetchListsText() self.dict_args["workPath"] = self.workPath self.output_dir_name = self.factory.fetch_output_dir_name( self.dir_action) self.exportPath = self.factory.creat_dirs(self.workPath + os.sep + "concatenate_results" + os.sep + self.output_dir_name) self.dict_args["exportPath"] = self.exportPath self.dict_args["export_phylip"] = self.checkBox.isChecked() self.dict_args["export_nex"] = self.checkBox_2.isChecked() self.dict_args["export_nexi"] = self.checkBox_3.isChecked() self.dict_args["export_nexig"] = self.checkBox_4.isChecked() self.dict_args["export_axt"] = self.checkBox_13.isChecked() self.dict_args["export_paml"] = self.checkBox_5.isChecked() self.dict_args["export_fas"] = self.checkBox_12.isChecked() self.dict_args["export_stat"] = self.checkBox_9.isChecked() self.dict_args["export_name"] = self.lineEdit.text( ) if self.lineEdit.text() else "concatenation" if self.groupBox_top_line.isChecked(): self.dict_args["draw_linear"] = True self.dict_args["fig_height"] = self.spinBox_5.value() self.dict_args["fig_width"] = self.spinBox_6.value() self.dict_args["label_size"] = self.spinBox_7.value() self.dict_args["Label_angle"] = self.spinBox_8.value() self.dict_args["Label_position"] = self.comboBox.currentText() self.dict_args["Label_color"] = self.pushButton_color.text() else: self.dict_args["draw_linear"] = False if True not in list(self.dict_args.values()): QMessageBox.critical( self, "Concatenate sequence", "<p style='line-height:25px; height:25px'>Please select output format(s) first!</p>" ) self.checkBox.setChecked(True) return ##描述,方便worfflow使用 self.description = "" self.reference = "" ok = self.factory.remove_dir(self.dict_args["exportPath"], parent=self) if not ok: #提醒是否删除旧结果,如果用户取消,就不执行 return self.worker = WorkThread(self.run_command, parent=self) self.worker.start() else: QMessageBox.critical( self, "Concatenate sequence", "<p style='line-height:25px; height:25px'>Please input files first!</p>" ) def run_command(self): try: # 先清空文件夹 time_start = datetime.datetime.now() self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "start", self.dict_args["exportPath"], self.qss_file, self ]) self.seqMatrix = Seq_matrix(**self.dict_args) if not self.seqMatrix.ok: self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.dict_args["exportPath"], self.qss_file, self ]) self.seqMatrix.interrupt = True return if "draw_linear" in self.dict_args and self.dict_args[ "draw_linear"]: self.dict_args[ "partition_file"] = self.seqMatrix.partition_detail Partition2fig(**self.dict_args) time_end = datetime.datetime.now() self.time_used = str(time_end - time_start) self.time_used_des = "Start at: %s\nFinish at: %s\nTotal time used: %s\n\n" % ( str(time_start), str(time_end), self.time_used) with open(self.dict_args["exportPath"] + os.sep + "summary.txt", "w", encoding="utf-8") as f: f.write( "If you use PhyloSuite, please cite:\nZhang, D., F. Gao, I. Jakovlić, H. Zou, J. Zhang, W.X. Li, and G.T. Wang, PhyloSuite: An integrated and scalable desktop platform for streamlined molecular sequence data management and evolutionary phylogenetics studies. Molecular Ecology Resources, 2020. 20(1): p. 348–355. DOI: 10.1111/1755-0998.13096.\n\n" + self.time_used_des) if not self.seqMatrix.unaligned and not self.seqMatrix.interrupt: if self.workflow: ##work flow跑的 self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "workflow stop", self.dict_args["exportPath"], self.qss_file, self ]) self.workflow_finished.emit("finished") return self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "stop", self.dict_args["exportPath"], self.qss_file, self ]) else: self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.dict_args["exportPath"], self.qss_file, self ]) if not self.workflow: self.focusSig.emit(self.dict_args["exportPath"]) self.seqMatrix.interrupt = True # self.seqMatrix.supplement() # if self.seqMatrix.dist_warning_message: # self.dict_args["warning_signal"].emit( # self.seqMatrix.dist_warning_message) # else: # # if self.mafft_interrupt is not True: # self.seqMatrix.concatenate() # if not self.seqMatrix.unaligned and not self.seqMatrix.interrupt: # if self.workflow: # ##work flow跑的 # self.startButtonStatusSig.emit( # [ # self.pushButton, # self.progressBar, # "workflow stop", # self.dict_args["exportPath"], # self.qss_file, # self]) # self.workflow_finished.emit("finished") # return # self.startButtonStatusSig.emit( # [self.pushButton, self.progressBar, "stop", self.dict_args["exportPath"], self.qss_file, self]) # else: # self.startButtonStatusSig.emit( # [self.pushButton, self.progressBar, "except", self.dict_args["exportPath"], self.qss_file, # self]) # if not self.workflow: # self.focusSig.emit(self.dict_args["exportPath"]) # self.seqMatrix.interrupt = True except BaseException: self.exceptionInfo = ''.join( traceback.format_exception( *sys.exc_info())) # 捕获报错内容,只能在这里捕获,没有报错的地方无法捕获 self.exception_signal.emit(self.exceptionInfo) # 激发这个信号 self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.dict_args["exportPath"], self.qss_file, self ]) @pyqtSlot() def on_pushButton_2_clicked(self): """ Stop """ if self.isRunning(): self.seqMatrix.interrupt = True if not self.workflow: QMessageBox.information( self, "Concatenate sequence", "<p style='line-height:25px; height:25px'>Concatenation has been terminated!</p>" ) self.startButtonStatusSig.emit([ self.pushButton, self.progressBar, "except", self.dict_args["exportPath"], self.qss_file, self ]) @pyqtSlot() def on_pushButton_3_clicked(self): """ open files """ files = QFileDialog.getOpenFileNames( self, "Input Files", filter= "Supported Format(*.fas *.fasta *.phy *.phylip *.nex *.nxs *.nexus);;" ) if files[0]: self.input(files[0]) def unaligned(self, message): msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setText( "<p style='line-height:25px; height:25px'>Unaligned sequences found, see details</p>" ) msg.setWindowTitle("Error") msg.setDetailedText("Unaligned sequences: " + ",".join(message)) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() def runProgress(self, num): oldValue = self.progressBar.value() done_int = int(num) if done_int > oldValue: self.progressBar.setProperty("value", done_int) QCoreApplication.processEvents() def popupException(self, exception): rgx = re.compile(r'Permission.+?[\'\"](.+\.csv)[\'\"]') if rgx.search(exception): csvfile = rgx.search(exception).group(1) reply = QMessageBox.critical( self, "Concatenate sequence", "<p style='line-height:25px; height:25px'>Please close '%s' file first!</p>" % os.path.basename(csvfile), QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes and platform.system().lower( ) == "windows": os.startfile(csvfile) else: msg = QMessageBox(self) msg.setIcon(QMessageBox.Critical) msg.setText( 'The program encountered an unforeseen problem, please report the bug at <a href="https://github.com/dongzhang0725/PhyloSuite/issues">https://github.com/dongzhang0725/PhyloSuite/issues</a> or send an email with the detailed traceback to [email protected]' ) msg.setWindowTitle("Error") msg.setDetailedText(exception) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() @pyqtSlot(list) def popupWarning(self, warning): ## 为了统一,统一用的列表 msg = QMessageBox(self) info = warning[0] if type(info) == OrderedDict: ## 有缺失基因的情况,这时候warning是个字典 msg.setIcon(QMessageBox.Information) msg.setText( "<p style='line-height:25px; height:25px'>Missing genes are replaced with '?' (see details or 'missing_genes.txt')</p>" ) msg.setWindowTitle("Concatenation Warning") max_len_taxa = len(max(list(info), key=len)) max_len_taxa = max_len_taxa if max_len_taxa > 7 else 7 #要大于species的占位符 list_detail = ["Species".ljust(max_len_taxa) + " |Missing genes" ] + [ str(i).ljust(max_len_taxa) + " |" + str(info[i]) for i in info ] # html_detail = "<html>" + "\n".join(list_detail).replace(" ", " ") + "</html>" msg.setDetailedText("\n".join(list_detail)) msg.setStandardButtons(QMessageBox.Ok) with open(self.dict_args["exportPath"] + os.sep + "missing_genes.txt", "w", encoding="utf-8") as f: f.write("\n".join(list_detail)) msg.exec_() elif type(info) == str: # 序列中DNA和AA混合了 msg.setIcon(QMessageBox.Warning) msg.setText( "<p style='line-height:25px; height:25px'>Mixed nucleotide and AA sequences (see details)</p>" ) msg.setWindowTitle("Concatenation Warning") msg.setDetailedText(info) msg.setStandardButtons(QMessageBox.Ok) msg.exec_() # if msg.exec_() == 1024: # QDialog.Accepted: # self.seqMatrix.concatenate() # if self.workflow: # ##work flow跑的 # self.startButtonStatusSig.emit( # [ # self.pushButton, # self.progressBar, # "workflow stop", # self.dict_args["exportPath"], # self.qss_file, # self]) # self.workflow_finished.emit("finished") # return # self.startButtonStatusSig.emit( # [self.pushButton, self.progressBar, "stop", self.dict_args["exportPath"], self.qss_file, self]) # self.focusSig.emit(self.dict_args["exportPath"]) # self.seqMatrix.interrupt = True def guiSave(self): # Save geometry self.concatenate_settings.setValue('size', self.size()) # self.concatenate_settings.setValue('pos', self.pos()) for name, obj in inspect.getmembers(self): # if type(obj) is QComboBox: # this works similar to isinstance, but # missed some field... not sure why? if isinstance(obj, QCheckBox): state = obj.isChecked() self.concatenate_settings.setValue(name, state) if isinstance(obj, QLineEdit): text = obj.text() self.concatenate_settings.setValue(name, text) if isinstance(obj, QSpinBox): value = obj.value() self.concatenate_settings.setValue(name, value) if isinstance(obj, QPushButton): if name == "pushButton_color": color = obj.palette().color(1) self.concatenate_settings.setValue(name, color.name()) if isinstance(obj, QComboBox): if name == "comboBox": # save combobox selection to registry index = obj.currentIndex() self.concatenate_settings.setValue(name, index) if isinstance(obj, QGroupBox): state = obj.isChecked() self.concatenate_settings.setValue(name, state) def guiRestore(self): # Restore geometry size = self.factory.judgeWindowSize(self.concatenate_settings, 646, 476) self.resize(size) self.factory.centerWindow(self) # self.move(self.concatenate_settings.value('pos', QPoint(875, 254))) for name, obj in inspect.getmembers(self): if isinstance(obj, QComboBox): if name == "comboBox": allItems = [obj.itemText(i) for i in range(obj.count())] index = self.concatenate_settings.value(name, "0") if type(index) != str: index = "0" model = obj.model() obj.clear() for num, i in enumerate(allItems): item = QStandardItem(i) # 背景颜色 if num % 2 == 0: item.setBackground(QColor(255, 255, 255)) else: item.setBackground(QColor(237, 243, 254)) model.appendRow(item) obj.setCurrentIndex(int(index)) else: if self.files: self.input(self.files) else: self.input([]) if isinstance(obj, QCheckBox): value = self.concatenate_settings.value( name, "true") # get stored value from registry obj.setChecked( self.factory.str2bool(value)) # restore checkbox if isinstance(obj, QLineEdit): text = self.concatenate_settings.value(name, "concatenation") obj.setText(text) if isinstance(obj, QSpinBox): value = self.concatenate_settings.value(name, None) if value: obj.setValue(int(value)) if isinstance(obj, QGroupBox): value = self.concatenate_settings.value( name, "true") # get stored value from registry obj.setChecked( self.factory.str2bool(value)) # restore checkbox if isinstance(obj, QPushButton): if name == "pushButton_color": color = self.concatenate_settings.value(name, "#F9C997") obj.setStyleSheet("background-color:%s" % color) obj.setText(color) obj.clicked.connect(self.changePbColor) def closeEvent(self, event): self.guiSave() self.closeSig.emit("Concatenation", self.fetchWorkflowSetting()) self.lineEdit.clearFocus() self.lineEdit.deselect() ###断开showSig和closeSig的槽函数连接 try: self.showSig.disconnect() except: pass try: self.closeSig.disconnect() except: pass if self.workflow: self.ui_closeSig.emit("Concatenation") ## 自动跑的时候不杀掉程序 return if self.isRunning(): # print(self.isRunning()) reply = QMessageBox.question( self, "Concatenate sequence", "<p style='line-height:25px; height:25px'>Concatenation is still running, terminate it?</p>", QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes: self.seqMatrix.interrupt = True else: event.ignore() def eventFilter(self, obj, event): # modifiers = QApplication.keyboardModifiers() if isinstance(obj, QComboBox): if event.type() == QEvent.DragEnter: if event.mimeData().hasUrls(): # must accept the dragEnterEvent or else the dropEvent # can't occur !!! event.accept() return True if event.type() == QEvent.Drop: files = [u.toLocalFile() for u in event.mimeData().urls()] files = [ i for i in files if os.path.splitext(i)[1].upper() in [ ".FAS", ".FASTA", ".PHY", ".PHYLIP", ".NEX", ".NXS", ".NEXUS" ] ] self.input(files) if isinstance(obj, QListWidget): if event.type() == QEvent.ChildRemoved: obj.setDragEnabled(True) obj.setDragDropMode(QAbstractItemView.InternalMove) obj.setDefaultDropAction(Qt.MoveAction) list_inputs = self.comboBox_4.fetchListsText() self.comboBox_4.refreshInputs(list_inputs, sort=False, judge=False) if (event.type() == QEvent.Show) and (obj == self.pushButton.toolButton.menu()): if re.search(r"\d+_\d+_\d+\-\d+_\d+_\d+", self.dir_action.text() ) or self.dir_action.text() == "Output Dir: ": self.factory.sync_dir(self.dir_action) ##同步文件夹名字 menu_x_pos = self.pushButton.toolButton.menu().pos().x() menu_width = self.pushButton.toolButton.menu().size().width() button_width = self.pushButton.toolButton.size().width() pos = QPoint(menu_x_pos - menu_width + button_width, self.pushButton.toolButton.menu().pos().y()) self.pushButton.toolButton.menu().move(pos) return True return super(Matrix, self).eventFilter(obj, event) # 0 def input(self, files): empty_files = [] rest_files = [] for file in files: if os.stat(file).st_size == 0: empty_files.append(os.path.basename(file)) else: rest_files.append(file) if empty_files: if len(empty_files) > 1: word1 = "files are" word2 = "they will" else: word1 = "file is" word2 = "it will" QMessageBox.warning( self, "Concatenation", "<p style='line-height:25px; height:25px'>%s %s empty, %s be ignored!</p>" % (str(empty_files), word1, word2), QMessageBox.Ok) self.comboBox_4.refreshInputs(rest_files, sort=self.checkBox_6.isChecked()) def isRunning(self): if hasattr(self, "seqMatrix") and (not self.seqMatrix.interrupt): return True else: return False def popupAutoDec(self, init=False): self.init = init self.factory.popUpAutoDetect("Concatenation", self.workPath, self.auto_popSig, self) def popupAutoDecSub(self, popupUI): if not popupUI: if not self.init: QMessageBox.warning( self, "Warning", "<p style='line-height:25px; height:25px'>No available file detected!</p>" ) return if not self.init: popupUI.checkBox.setVisible(False) if popupUI.exec_() == QDialog.Accepted: widget = popupUI.listWidget_framless.itemWidget( popupUI.listWidget_framless.selectedItems()[0]) autoInputs = widget.autoInputs self.input(autoInputs) def fetchWorkflowSetting(self): ''' export format; export name ''' settings = '''<p class="title">***Concatenate Sequence***</p>''' list_formats = [] for checkbox in [ self.checkBox, self.checkBox_2, self.checkBox_3, self.checkBox_4, self.checkBox_5, self.checkBox_9, self.checkBox_12, self.checkBox_13 ]: if checkbox.isChecked(): list_formats.append(checkbox.text()) formats = ", ".join(list_formats) if list_formats else "None" settings += '<p>Export formats: <a href="self.Concatenation_exe ' \ 'factory.highlightWidgets(x.checkBox,x.checkBox_2,x.checkBox_3,x.checkBox_4,x.checkBox_5,' \ 'x.checkBox_9,x.checkBox_12,x.checkBox_13)">%s</a></p>'%formats export_name = self.lineEdit.text() settings += '<p>Export file name: <a href="self.Concatenation_exe lineEdit.setFocus()' \ ' lineEdit.selectAll() factory.highlightWidgets(x.lineEdit)">%s</a></p>'%export_name return settings def showEvent(self, event): QTimer.singleShot(100, lambda: self.showSig.emit(self)) # self.showSig.emit(self) def isFileIn(self): return self.comboBox_4.count() def changePbColor(self): button = self.sender() ini_color = button.palette().color(1) color = QColorDialog.getColor(QColor(ini_color), self) if color.isValid(): button.setText(color.name()) button.setStyleSheet("background-color:%s" % color.name()) @pyqtSlot(str) def popupEmptyFileWarning(self, text): QMessageBox.warning( self, "Concatenation Warning", "<p style='line-height:25px; height:25px'>%s!</p>" % text)