def _add_tab(self, json=None): """will look at the json and will display the values in conflicts in a new tab to allow the user to fix the conflicts""" number_of_tabs = self.ui.tabWidget.count() _table = QTableWidget() # initialize each table columns_width = self._calculate_columns_width(json=json) for _col in np.arange(len(json[0])): _table.insertColumn(_col) _table.setColumnWidth(_col, columns_width[_col]) for _row in np.arange(len(json)): _table.insertRow(_row) self.list_table.append(_table) _table.setHorizontalHeaderLabels(self.columns_label) for _row in np.arange(len(json)): # run number _col = 0 list_runs = json[_row]["Run Number"] o_parser = ListRunsParser() checkbox = QRadioButton(o_parser.new_runs(list_runs=list_runs)) if _row == 0: checkbox.setChecked(True) # QtCore.QObject.connect(checkbox, QtCore.SIGNAL("clicked(bool)"), # lambda bool, row=_row, table_id=_table: # self._changed_conflict_checkbox(bool, row, table_id)) _table.setCellWidget(_row, _col, checkbox) _col += 1 # chemical formula item = QTableWidgetItem(json[_row]["chemical_formula"]) _table.setItem(_row, _col, item) _col += 1 # geometry item = QTableWidgetItem(json[_row]["geometry"]) _table.setItem(_row, _col, item) _col += 1 # mass_density item = QTableWidgetItem(json[_row]["mass_density"]) _table.setItem(_row, _col, item) _col += 1 # sample_env_device item = QTableWidgetItem(json[_row]["sample_env_device"]) _table.setItem(_row, _col, item) self.ui.tabWidget.insertTab(number_of_tabs, _table, "Conflict #{}".format(number_of_tabs))
class GroupsPostView(QDialog): """ +------------------------+ | Groups : Post/Delete | +------------------------+ | | | check1 Name1 | | check2 Name2 | | check3 Name3 | | | | SetAsMain | | Apply OK Close | +------------------------+ """ def __init__(self, data, win_parent=None): self.win_parent = win_parent #Init the base class groups = data['groups'] inames = data['inames'] self.imain = data['imain'] self.names = [group.name for group in groups] self.white = (255, 255, 255) self.light_grey = (211, 211, 211) self.inames = inames self.shown_set = data['shown'] self.deleted_groups = set() #self.inames = argsort(self.names) #print('inames =', inames) anames = array(self.names) for iname, name in enumerate(anames[self.inames]): print('name[%s] = %r' % (iname, name)) # ignore these... #self._default_name = data['name'] #self._default_coords = data['coords'] #self._default_elements = data['elements'] #self._default_color = data['color'] #self.coords_pound = data['coords_pound'] #self.elements_pound = data['elements_pound'] #self._default_is_discrete = data['is_discrete'] self.out_data = data QDialog.__init__(self, win_parent) #self.setupUi(self) self.setWindowTitle('Groups: Post/View') self.create_widgets() self.create_layout() self.set_connections() #self.show() def create_widgets(self): # main/delete/supergroup self.set_as_main_button = QPushButton("Set As Main") self.create_super_group_button = QPushButton("Create Super Group") self.delete_groups_button = QPushButton("Delete Groups") self.revert_groups_button = QPushButton("Revert Groups") self.show_groups_button = QPushButton("Show Groups") self.hide_groups_button = QPushButton("Hide Groups") # closing self.apply_button = QPushButton("Apply") self.ok_button = QPushButton("OK") self.cancel_button = QPushButton("Cancel") #table self.table = QTableWidget() self.checks = [] self.names_text = [] bold = QtGui.QFont() bold.setBold(True) bold.setItalic(True) bold.setWeight(75) anames = array(self.names) for iname, name in enumerate(anames[self.inames]): check = QTableWidgetItem() check.setCheckState(False) # TODO: create right click menu ??? name_text = QTableWidgetItem(str(name)) if iname == self.imain: name_text.setFont(bold) self.shown_set.add(iname) check.setCheckState(2) name_text.setBackground(QtGui.QColor(*self.light_grey)) elif iname in self.shown_set: name_text.setBackground(QtGui.QColor(*self.light_grey)) self.checks.append(check) self.names_text.append(name_text) def create_layout(self): nrows = len(self.names) table = self.table table.setRowCount(nrows) table.setColumnCount(2) headers = [QString('Operate On'), QString('Name')] table.setHorizontalHeaderLabels(headers) header = table.horizontalHeader() header.setStretchLastSection(True) #table.setAlternatingRowColors(True) #header = table.verticalHeader() #header.setStretchLastSection(True) #table.resize(400, 250) #heighti = table.rowHeight(0) #total_height = nrows * heighti #table.setMaximumHeight(total_height) #table.resize(total_height, None) #for iname, name in enumerate(self.names[self.inames]): #print('name[%s] = %r' % (iname, name)) for iname in self.inames: check = self.checks[iname] name_text = self.names_text[iname] # row, col, value table.setItem(iname, 0, check) table.setItem(iname, 1, name_text) table.resizeRowsToContents() #table.horizontalHeaderItem(1).setTextAlignment(QtCore.AlignHCenter) #= QVBoxLayout() ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.apply_button) ok_cancel_box.addWidget(self.ok_button) ok_cancel_box.addWidget(self.cancel_button) vbox = QVBoxLayout() vbox.addWidget(table) vbox.addWidget(self.set_as_main_button) #vbox.addWidget(self.create_super_group_button) vbox.addStretch() vbox.addWidget(self.show_groups_button) vbox.addWidget(self.hide_groups_button) vbox.addStretch() vbox.addWidget(self.delete_groups_button) vbox.addWidget(self.revert_groups_button) vbox.addStretch() vbox.addStretch() vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def set_connections(self): """creates the actions for the menu""" self.set_as_main_button.clicked.connect(self.on_set_as_main) self.delete_groups_button.clicked.connect(self.on_delete_groups) self.revert_groups_button.clicked.connect(self.on_revert_groups) self.show_groups_button.clicked.connect(self.on_show_groups) self.hide_groups_button.clicked.connect(self.on_hide_groups) self.create_super_group_button.clicked.connect( self.on_create_super_group) self.apply_button.clicked.connect(self.on_apply) self.ok_button.clicked.connect(self.on_ok) self.cancel_button.clicked.connect(self.on_cancel) def closeEvent(self, event): event.accept() @property def nrows(self): return self.table.rowCount() def on_hide_groups(self): self._set_highlight(self.white) def on_show_groups(self): self._set_highlight(self.light_grey) def _set_highlight(self, color): for irow in range(self.nrows): check = self.checks[irow] is_checked = check.checkState() # 0 - unchecked # 1 - partially checked (invalid) # 2 - checked if is_checked: name_text = self.names_text[irow] name_text.setBackground(QtGui.QColor(*color)) def on_delete_groups(self): for irow in range(self.nrows): check = self.checks[irow] is_checked = check.checkState() # 0 - unchecked # 1 - partially checked (invalid) # 2 - checked if irow == 0 and is_checked: # TODO: change this to a log print('error deleting group ALL...change this to a log') #self.window_parent.log return if is_checked: self.table.hideRow(irow) self.deleted_groups.add(irow) check.setCheckState(0) if self.imain > 0 and self.shown_set == set([0]): bold = QtGui.QFont() bold.setBold(True) bold.setItalic(True) self.imain = 0 irow = 0 check = self.checks[irow] name_text = self.names_texts[irow] name_text.setFont(bold) name_text.setBackground(QtGui.QColor(*self.light_grey)) def on_revert_groups(self): for irow in range(self.nrows): self.table.showRow(irow) self.deleted_groups = set() def on_create_super_group(self): inames = [ iname for iname, check in enumerate(self.checks) if bool(check.checkState()) ] if not len(inames): # TODO: add logging print('nothing is checked...') return if inames[0] == 0: # TODO: add logging print("cannot include 'ALL' in supergroup...") return name = 'SuperGroup' # popup gui and get a name irow = self.table.rowCount() self.table.insertRow(irow) check = QTableWidgetItem() check.setCheckState(False) name_text = QTableWidgetItem(str(name)) self.names.extend(name) self.names_text.append(name_text) self.checks.append(check) self.table.setItem(irow, 0, check) self.table.setItem(irow, 1, name_text) def on_set_as_main(self): bold = QtGui.QFont() bold.setBold(True) bold.setItalic(True) normal = QtGui.QFont() normal.setBold(False) normal.setItalic(False) imain = None imain_set = False for irow in range(self.nrows): check = self.checks[irow] name_text = self.names_text[irow] is_checked = check.checkState() # 0 - unchecked # 1 - partially checked (invalid) # 2 - checked if is_checked and not imain_set: # TODO: change this to a log #self.window_parent.log imain_set = True imain = irow name_text.setFont(bold) name_text.setBackground(QtGui.QColor(*self.light_grey)) self.shown_set.add(irow) elif irow == self.imain: name_text.setFont(normal) if irow == 0: name_text.setBackground(QtGui.QColor(*self.white)) if irow in self.shown_set: self.shown_set.remove(irow) elif imain == 0: name_text.setBackground(QtGui.QColor(*self.white)) self.shown_set.remove(imain) self.imain = imain def get_main_group(self): return self.imain def get_shown_group(self): return self.shown_set def get_deleted_groups(self): return self.deleted_groups def on_validate(self): flag0 = flag1 = flag2 = True main_group_id = self.get_main_group() shown_groups_ids = self.get_shown_group() deleted_group_ids = self.get_deleted_groups() if flag0 and flag1 and flag2: self.out_data['imain'] = main_group_id self.out_data['shown'] = shown_groups_ids self.out_data['remove'] = deleted_group_ids self.out_data['clicked_ok'] = True return True return False def on_apply(self): passed = self.on_validate() if passed: self.win_parent.on_post_group(self.out_data) def on_ok(self): passed = self.on_validate() if passed: self.close() #self.destroy() def on_cancel(self): self.close()
class MCSDialog(QDialog): """A dialog to perform minimal cut set computation""" def __init__(self, appdata: CnaData, centralwidget): QDialog.__init__(self) self.setWindowTitle("Minimal Cut Sets Computation") self.appdata = appdata self.centralwidget = centralwidget self.eng = appdata.engine self.out = io.StringIO() self.err = io.StringIO() self.layout = QVBoxLayout() l1 = QLabel("Target Region(s)") self.layout.addWidget(l1) s1 = QHBoxLayout() completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) self.target_list = QTableWidget(1, 4) self.target_list.setHorizontalHeaderLabels( ["region no", "T", "≥/≤", "t"]) self.target_list.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.target_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed) self.target_list.horizontalHeader().resizeSection(0, 100) self.target_list.horizontalHeader().setSectionResizeMode(2, QHeaderView.Fixed) self.target_list.horizontalHeader().resizeSection(2, 50) item = QLineEdit("1") self.target_list.setCellWidget(0, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.target_list.setCellWidget(0, 1, item2) combo = QComboBox(self.target_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.target_list.setCellWidget(0, 2, combo) item = QLineEdit("0") self.target_list.setCellWidget(0, 3, item) s1.addWidget(self.target_list) s11 = QVBoxLayout() self.add_target = QPushButton("+") self.add_target.clicked.connect(self.add_target_region) self.rem_target = QPushButton("-") self.rem_target.clicked.connect(self.rem_target_region) s11.addWidget(self.add_target) s11.addWidget(self.rem_target) s1.addItem(s11) self.layout.addItem(s1) l2 = QLabel("Desired Region(s)") self.layout.addWidget(l2) s2 = QHBoxLayout() self.desired_list = QTableWidget(1, 4) self.desired_list.setHorizontalHeaderLabels( ["region no", "D", "≥/≤", "d"]) self.desired_list.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.desired_list.horizontalHeader().setSectionResizeMode(0, QHeaderView.Fixed) self.desired_list.horizontalHeader().resizeSection(0, 100) self.desired_list.horizontalHeader().setSectionResizeMode(2, QHeaderView.Fixed) self.desired_list.horizontalHeader().resizeSection(2, 50) item = QLineEdit("1") self.desired_list.setCellWidget(0, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.desired_list.setCellWidget(0, 1, item2) combo = QComboBox(self.desired_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.desired_list.setCellWidget(0, 2, combo) item = QLineEdit("0") self.desired_list.setCellWidget(0, 3, item) s2.addWidget(self.desired_list) s21 = QVBoxLayout() self.add_desire = QPushButton("+") self.add_desire.clicked.connect(self.add_desired_region) self.rem_desire = QPushButton("-") self.rem_desire.clicked.connect(self.rem_desired_region) s21.addWidget(self.add_desire) s21.addWidget(self.rem_desire) s2.addItem(s21) self.layout.addItem(s2) s3 = QHBoxLayout() sgx = QVBoxLayout() self.gen_kos = QCheckBox("Gene KOs") self.exclude_boundary = QCheckBox( "Exclude boundary\nreactions as cuts") sg1 = QHBoxLayout() s31 = QVBoxLayout() l = QLabel("Max. Solutions") s31.addWidget(l) l = QLabel("Max. Size") s31.addWidget(l) l = QLabel("Time Limit [sec]") s31.addWidget(l) sg1.addItem(s31) s32 = QVBoxLayout() self.max_solu = QLineEdit("inf") self.max_solu.setMaximumWidth(50) s32.addWidget(self.max_solu) self.max_size = QLineEdit("7") self.max_size.setMaximumWidth(50) s32.addWidget(self.max_size) self.time_limit = QLineEdit("inf") self.time_limit.setMaximumWidth(50) s32.addWidget(self.time_limit) sg1.addItem(s32) sgx.addWidget(self.gen_kos) sgx.addWidget(self.exclude_boundary) sgx.addItem(sg1) s3.addItem(sgx) g3 = QGroupBox("Solver") s33 = QVBoxLayout() self.bg1 = QButtonGroup() optlang_solver_name = interface_to_str( appdata.project.cobra_py_model.problem) self.solver_optlang = QRadioButton(f"{optlang_solver_name} (optlang)") self.solver_optlang.setToolTip( "Uses the solver specified by the current model.") s33.addWidget(self.solver_optlang) self.bg1.addButton(self.solver_optlang) self.solver_cplex_matlab = QRadioButton("CPLEX (MATLAB)") self.solver_cplex_matlab.setToolTip( "Only enabled with MATLAB and CPLEX") s33.addWidget(self.solver_cplex_matlab) self.bg1.addButton(self.solver_cplex_matlab) self.solver_cplex_java = QRadioButton("CPLEX (Octave)") self.solver_cplex_java.setToolTip("Only enabled with Octave and CPLEX") s33.addWidget(self.solver_cplex_java) self.bg1.addButton(self.solver_cplex_java) self.solver_intlinprog = QRadioButton("intlinprog (MATLAB)") self.solver_intlinprog.setToolTip("Only enabled with MATLAB") s33.addWidget(self.solver_intlinprog) self.bg1.addButton(self.solver_intlinprog) self.solver_glpk = QRadioButton("GLPK (Octave/MATLAB)") s33.addWidget(self.solver_glpk) self.bg1.addButton(self.solver_glpk) self.bg1.buttonClicked.connect(self.configure_solver_options) g3.setLayout(s33) s3.addWidget(g3) g4 = QGroupBox("MCS search") s34 = QVBoxLayout() self.bg2 = QButtonGroup() self.any_mcs = QRadioButton("any MCS (fast)") self.any_mcs.setChecked(True) s34.addWidget(self.any_mcs) self.bg2.addButton(self.any_mcs) # Search type: by cardinality only with CPLEX possible self.mcs_by_cardinality = QRadioButton("by cardinality") s34.addWidget(self.mcs_by_cardinality) self.bg2.addButton(self.mcs_by_cardinality) self.smalles_mcs_first = QRadioButton("smallest MCS first") s34.addWidget(self.smalles_mcs_first) self.bg2.addButton(self.smalles_mcs_first) g4.setLayout(s34) s3.addWidget(g4) self.layout.addItem(s3) # Disable incompatible combinations if appdata.selected_engine == 'None': self.solver_optlang.setChecked(True) self.solver_cplex_matlab.setEnabled(False) self.solver_cplex_java.setEnabled(False) self.solver_glpk.setEnabled(False) self.solver_intlinprog.setEnabled(False) if optlang_solver_name != 'cplex': self.mcs_by_cardinality.setEnabled(False) else: self.solver_glpk.setChecked(True) if not self.eng.is_cplex_matlab_ready(): self.solver_cplex_matlab.setEnabled(False) if not self.eng.is_cplex_java_ready(): self.solver_cplex_java.setEnabled(False) if self.appdata.is_matlab_set(): self.solver_cplex_java.setEnabled(False) if not self.appdata.is_matlab_set(): self.solver_cplex_matlab.setEnabled(False) self.solver_intlinprog.setEnabled(False) self.configure_solver_options() s4 = QVBoxLayout() self.consider_scenario = QCheckBox( "Consider constraint given by scenario") s4.addWidget(self.consider_scenario) self.advanced = QCheckBox( "Advanced: Define knockout/addition costs for genes/reactions") self.advanced.setEnabled(False) s4.addWidget(self.advanced) self.layout.addItem(s4) buttons = QHBoxLayout() # self.save = QPushButton("save") # buttons.addWidget(self.save) # self.load = QPushButton("load") # buttons.addWidget(self.load) self.compute_mcs = QPushButton("Compute MCS") buttons.addWidget(self.compute_mcs) # self.compute_mcs2 = QPushButton("Compute MCS2") # buttons.addWidget(self.compute_mcs2) self.cancel = QPushButton("Close") buttons.addWidget(self.cancel) self.layout.addItem(buttons) # max width for buttons self.add_target.setMaximumWidth(20) self.rem_target.setMaximumWidth(20) self.add_desire.setMaximumWidth(20) self.rem_desire.setMaximumWidth(20) self.setLayout(self.layout) # Connecting the signal self.cancel.clicked.connect(self.reject) self.compute_mcs.clicked.connect(self.compute) @Slot() def configure_solver_options(self): optlang_solver_name = interface_to_str( self.appdata.project.cobra_py_model.problem) if self.solver_optlang.isChecked(): self.gen_kos.setChecked(False) self.gen_kos.setEnabled(False) self.exclude_boundary.setEnabled(True) if optlang_solver_name != 'cplex': if self.mcs_by_cardinality.isChecked(): self.mcs_by_cardinality.setChecked(False) self.any_mcs.setChecked(True) self.mcs_by_cardinality.setEnabled(False) self.mcs_by_cardinality.setChecked(False) else: self.gen_kos.setEnabled(True) self.exclude_boundary.setChecked(False) self.exclude_boundary.setEnabled(False) self.mcs_by_cardinality.setEnabled(True) def add_target_region(self): i = self.target_list.rowCount() self.target_list.insertRow(i) completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) item = QLineEdit("1") self.target_list.setCellWidget(i, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.target_list.setCellWidget(i, 1, item2) combo = QComboBox(self.target_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.target_list.setCellWidget(i, 2, combo) item = QLineEdit("0") self.target_list.setCellWidget(i, 3, item) def add_desired_region(self): i = self.desired_list.rowCount() self.desired_list.insertRow(i) completer = QCompleter( self.appdata.project.cobra_py_model.reactions.list_attr("id"), self) completer.setCaseSensitivity(Qt.CaseInsensitive) item = QLineEdit("1") self.desired_list.setCellWidget(i, 0, item) item2 = QLineEdit("") item2.setCompleter(completer) self.desired_list.setCellWidget(i, 1, item2) combo = QComboBox(self.desired_list) combo.insertItem(1, "≤") combo.insertItem(2, "≥") self.desired_list.setCellWidget(i, 2, combo) item = QLineEdit("0") self.desired_list.setCellWidget(i, 3, item) def rem_target_region(self): i = self.target_list.rowCount() self.target_list.removeRow(i-1) def rem_desired_region(self): i = self.desired_list.rowCount() self.desired_list.removeRow(i-1) def compute(self): if self.solver_optlang.isChecked(): self.compute_optlang() else: self.compute_legacy() def compute_legacy(self): self.setCursor(Qt.BusyCursor) # create CobraModel for matlab with self.appdata.project.cobra_py_model as model: if self.consider_scenario.isChecked(): # integrate scenario into model bounds for r in self.appdata.project.scen_values.keys(): model.reactions.get_by_id( r).bounds = self.appdata.project.scen_values[r] cobra.io.save_matlab_model(model, os.path.join( self.appdata.cna_path, "cobra_model.mat"), varname="cbmodel") self.eng.eval("load('cobra_model.mat')", nargout=0) try: self.eng.eval("cnap = CNAcobra2cna(cbmodel);", nargout=0, stdout=self.out, stderr=self.err) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return self.eng.eval("genes = [];", nargout=0, stdout=self.out, stderr=self.err) cmd = "maxSolutions = " + str(float(self.max_solu.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) cmd = "maxSize = " + str(int(self.max_size.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) cmd = "milp_time_limit = " + str(float(self.time_limit.text())) + ";" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) if self.gen_kos.isChecked(): self.eng.eval("gKOs = 1;", nargout=0) else: self.eng.eval("gKOs = 0;", nargout=0) if self.advanced.isChecked(): self.eng.eval("advanced_on = 1;", nargout=0) else: self.eng.eval("advanced_on = 0;", nargout=0) if self.solver_intlinprog.isChecked(): self.eng.eval("solver = 'intlinprog';", nargout=0) if self.solver_cplex_java.isChecked(): self.eng.eval("solver = 'java_cplex_new';", nargout=0) if self.solver_cplex_matlab.isChecked(): self.eng.eval("solver = 'matlab_cplex';", nargout=0) if self.solver_glpk.isChecked(): self.eng.eval("solver = 'glpk';", nargout=0) if self.any_mcs.isChecked(): self.eng.eval("mcs_search_mode = 'search_1';", nargout=0) elif self.mcs_by_cardinality.isChecked(): self.eng.eval("mcs_search_mode = 'search_2';", nargout=0) elif self.smalles_mcs_first.isChecked(): self.eng.eval("mcs_search_mode = 'search_3';", nargout=0) rows = self.target_list.rowCount() for i in range(0, rows): p1 = self.target_list.cellWidget(i, 0).text() p2 = self.target_list.cellWidget(i, 1).text() if self.target_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = self.target_list.cellWidget(i, 3).text() cmd = "dg_T = {[" + p1+"], '" + p2 + \ "', '" + p3 + "', [" + p4 + "']};" self.eng.eval(cmd, nargout=0, stdout=self.out, stderr=self.err) rows = self.desired_list.rowCount() for i in range(0, rows): p1 = self.desired_list.cellWidget(i, 0).text() p2 = self.desired_list.cellWidget(i, 1).text() if self.desired_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = self.desired_list.cellWidget(i, 3).text() cmd = "dg_D = {[" + p1+"], '" + p2 + \ "', '" + p3 + "', [" + p4 + "']};" self.eng.eval(cmd, nargout=0) # get some data self.eng.eval("reac_id = cellstr(cnap.reacID).';", nargout=0, stdout=self.out, stderr=self.err) mcs = [] values = [] reactions = [] reac_id = [] if self.appdata.is_matlab_set(): reac_id = self.eng.workspace['reac_id'] try: self.eng.eval("[mcs] = cnapy_compute_mcs(cnap, genes, maxSolutions, maxSize, milp_time_limit, gKOs, advanced_on, solver, mcs_search_mode, dg_T,dg_D);", nargout=0) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return else: self.eng.eval("[reaction, mcs, value] = find(mcs);", nargout=0, stdout=self.out, stderr=self.err) reactions = self.eng.workspace['reaction'] mcs = self.eng.workspace['mcs'] values = self.eng.workspace['value'] elif self.appdata.is_octave_ready(): reac_id = self.eng.pull('reac_id') reac_id = reac_id[0] try: self.eng.eval("[mcs] = cnapy_compute_mcs(cnap, genes, maxSolutions, maxSize, milp_time_limit, gKOs, advanced_on, solver, mcs_search_mode, dg_T,dg_D);", nargout=0) except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'Unknown exception occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return else: self.eng.eval("[reaction, mcs, value] = find(mcs);", nargout=0, stdout=self.out, stderr=self.err) reactions = self.eng.pull('reaction') mcs = self.eng.pull('mcs') values = self.eng.pull('value') if len(mcs) == 0: QMessageBox.information(self, 'No cut sets', 'Cut sets have not been calculated or do not exist.') else: last_mcs = 1 omcs = [] current_mcs = {} for i in range(0, len(reactions)): reacid = int(reactions[i][0]) reaction = reac_id[reacid-1] c_mcs = int(mcs[i][0]) c_value = int(values[i][0]) if c_value == -1: # -1 stands for removed which is 0 in the ui c_value = 0 if c_mcs > last_mcs: omcs.append(current_mcs) last_mcs = c_mcs current_mcs = {} current_mcs[reaction] = c_value omcs.append(current_mcs) self.appdata.project.modes = omcs self.centralwidget.mode_navigator.current = 0 QMessageBox.information(self, 'Cut sets found', str(len(omcs))+' Cut sets have been calculated.') self.centralwidget.update_mode() self.centralwidget.mode_navigator.title.setText("MCS Navigation") self.setCursor(Qt.ArrowCursor) def compute_optlang(self): self.setCursor(Qt.BusyCursor) max_mcs_num = float(self.max_solu.text()) max_mcs_size = int(self.max_size.text()) timeout = float(self.time_limit.text()) if timeout is float('inf'): timeout = None # if self.gen_kos.isChecked(): # self.eng.eval("gKOs = 1;", nargout=0) # else: # self.eng.eval("gKOs = 0;", nargout=0) # if self.advanced.isChecked(): # self.eng.eval("advanced_on = 1;", nargout=0) # else: # self.eng.eval("advanced_on = 0;", nargout=0) if self.smalles_mcs_first.isChecked(): enum_method = 1 elif self.mcs_by_cardinality.isChecked(): enum_method = 2 elif self.any_mcs.isChecked(): enum_method = 3 with self.appdata.project.cobra_py_model as model: if self.consider_scenario.isChecked(): # integrate scenario into model bounds for r in self.appdata.project.scen_values.keys(): model.reactions.get_by_id( r).bounds = self.appdata.project.scen_values[r] reac_id = model.reactions.list_attr("id") reac_id_symbols = cMCS_enumerator.get_reac_id_symbols(reac_id) rows = self.target_list.rowCount() targets = dict() for i in range(0, rows): p1 = self.target_list.cellWidget(i, 0).text() p2 = self.target_list.cellWidget(i, 1).text() if len(p1) > 0 and len(p2) > 0: if self.target_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = float(self.target_list.cellWidget(i, 3).text()) targets.setdefault(p1, []).append((p2, p3, p4)) targets = list(targets.values()) targets = [cMCS_enumerator.relations2leq_matrix(cMCS_enumerator.parse_relations( t, reac_id_symbols=reac_id_symbols), reac_id) for t in targets] rows = self.desired_list.rowCount() desired = dict() for i in range(0, rows): p1 = self.desired_list.cellWidget(i, 0).text() p2 = self.desired_list.cellWidget(i, 1).text() if len(p1) > 0 and len(p2) > 0: if self.desired_list.cellWidget(i, 2).currentText() == '≤': p3 = "<=" else: p3 = ">=" p4 = float(self.desired_list.cellWidget(i, 3).text()) desired.setdefault(p1, []).append((p2, p3, p4)) desired = list(desired.values()) desired = [cMCS_enumerator.relations2leq_matrix(cMCS_enumerator.parse_relations( d, reac_id_symbols=reac_id_symbols), reac_id) for d in desired] try: mcs = cMCS_enumerator.compute_mcs(model, targets=targets, desired=desired, enum_method=enum_method, max_mcs_size=max_mcs_size, max_mcs_num=max_mcs_num, timeout=timeout, exclude_boundary_reactions_as_cuts=self.exclude_boundary.isChecked()) except cMCS_enumerator.InfeasibleRegion as e: QMessageBox.warning(self, 'Cannot calculate MCS', str(e)) return targets, desired except Exception: output = io.StringIO() traceback.print_exc(file=output) exstr = output.getvalue() print(exstr) QMessageBox.warning(self, 'An exception has occured!', exstr+'\nPlease report the problem to:\n\ \nhttps://github.com/cnapy-org/CNApy/issues') return targets, desired finally: self.setCursor(Qt.ArrowCursor) if len(mcs) == 0: QMessageBox.information(self, 'No cut sets', 'Cut sets have not been calculated or do not exist.') return targets, desired omcs = [{reac_id[i]: 0 for i in m} for m in mcs] self.appdata.project.modes = omcs self.centralwidget.mode_navigator.current = 0 QMessageBox.information(self, 'Cut sets found', str(len(omcs))+' Cut sets have been calculated.') self.centralwidget.update_mode() self.centralwidget.mode_navigator.title.setText("MCS Navigation")
class CameraWindow(PyDialog): """defines the CameraWindow class""" def __init__(self, data, win_parent=None): """ +--------+ | Camera | +--------+---------------+ | Camera Name | | +-------------------+ | | | | | | | | | | | | | | | | | | | | | | +-------------------+ | | | | Name xxx Save | | Delete Set | | | | Apply OK Cancel | +--------+---------------+ """ PyDialog.__init__(self, data, win_parent) self.setWindowTitle('Camera Views') #self.setWindowIcon(view_icon) self._default_name = 'Camera' self.out_data['clicked_ok'] = False self.cameras = deepcopy(data['cameras']) self.names = sorted(self.cameras.keys()) self.name = QLabel("Name:") self.name_edit = QLineEdit(str(self._default_name)) self.delete_button = QPushButton("Delete") self.set_button = QPushButton("Set") self.save_button = QPushButton("Save") # closing self.apply_button = QPushButton("Apply") self.close_button = QPushButton("Close") self.cancel_button = QPushButton("Cancel") self.table = QTableWidget() names_text = [] for name in self.names: name_text = QTableWidgetItem(str(name)) names_text.append(name_text) self.create_layout(names_text) self.set_connections() def create_layout(self, names_text): nrows = len(self.names) table = self.table table.setRowCount(nrows) table.setColumnCount(1) headers = ['Camera Name'] table.setHorizontalHeaderLabels(headers) header = table.horizontalHeader() header.setStretchLastSection(True) for iname, name_text in enumerate(names_text): # row, col, value table.setItem(iname, 0, name_text) table.resizeRowsToContents() ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.apply_button) ok_cancel_box.addWidget(self.close_button) ok_cancel_box.addWidget(self.cancel_button) grid = QGridLayout() irow = 0 grid.addWidget(self.name, irow, 0) grid.addWidget(self.name_edit, irow, 1) grid.addWidget(self.save_button, irow, 2) irow += 1 grid.addWidget(self.delete_button, irow, 0) grid.addWidget(self.set_button, irow, 1) irow += 1 vbox = QVBoxLayout() vbox.addWidget(self.table) vbox.addLayout(grid) vbox.addStretch() vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def set_connections(self): """creates the actions for the menu""" #if qt_version == 4: #self.connect(self.ok_button, QtCore.SIGNAL('clicked()'), self.on_ok) self.set_button.clicked.connect(self.on_set) self.save_button.clicked.connect(self.on_save) self.delete_button.clicked.connect(self.on_delete) self.apply_button.clicked.connect(self.on_apply) self.close_button.clicked.connect(self.on_close) self.cancel_button.clicked.connect(self.on_cancel) def on_set(self): objs = self.table.selectedIndexes() if len(objs) == 1: obj = objs[0] irow = obj.row() name = self.names[irow] self.set_camera(name) return True return False def on_save(self): name = str(self.name_edit.text()).strip() if name in self.cameras: return irow = self.nrows if len(name): self.table.insertRow(irow) name_text = QTableWidgetItem(str(name)) self.table.setItem(irow, 0, name_text) self.name_edit.setText('') self.save_camera(name) def set_camera(self, name): camera_data = self.cameras[name] if self.win_parent is None: return self.win_parent.on_set_camera_data(camera_data) def save_camera(self, name): self.names.append(name) if self.win_parent is None: self.cameras[name] = None return self.cameras[name] = self.win_parent.get_camera_data() @property def nrows(self): return self.table.rowCount() def on_delete(self): irows = [] for obj in self.table.selectedIndexes(): irow = obj.row() irows.append(irow) irows.sort() for irow in reversed(irows): self.table.removeRow(irow) name = self.names.pop(irow) del self.cameras[name] #print(' removing irow=%s name=%r' % (irow, name)) def closeEvent(self, event): event.accept() def on_apply(self): passed = self.on_set() #if passed: # self.win_parent.create_plane(self.out_data) return passed def on_close(self): self.out_data['clicked_ok'] = True self.out_data['cameras'] = self.cameras self.close() def on_ok(self): passed = self.on_apply() if passed: name = str(self.name_edit.text()).strip() self.out_data['name'] = name self.out_data['cameras'] = self.cameras self.out_data['clicked_ok'] = True self.close() #self.destroy() def on_cancel(self): self.close()
class EventsDialog(QDialog): def __init__(self, parent, pos, desc): super().__init__(parent) self.setWindowTitle("Edit Events") self.table = QTableWidget(len(pos), 2) for row, (p, d) in enumerate(zip(pos, desc)): self.table.setItem(row, 0, IntTableWidgetItem(p)) self.table.setItem(row, 1, IntTableWidgetItem(d)) self.table.setHorizontalHeaderLabels(["Position", "Type"]) self.table.horizontalHeader().setStretchLastSection(True) self.table.verticalHeader().setVisible(False) self.table.setShowGrid(False) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSortingEnabled(True) self.table.sortByColumn(0, Qt.AscendingOrder) vbox = QVBoxLayout(self) vbox.addWidget(self.table) hbox = QHBoxLayout() self.add_button = QPushButton("+") self.remove_button = QPushButton("-") buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) hbox.addWidget(self.add_button) hbox.addWidget(self.remove_button) hbox.addStretch() hbox.addWidget(buttonbox) vbox.addLayout(hbox) buttonbox.accepted.connect(self.accept) buttonbox.rejected.connect(self.reject) self.table.itemSelectionChanged.connect(self.toggle_buttons) self.remove_button.clicked.connect(self.remove_event) self.add_button.clicked.connect(self.add_event) self.toggle_buttons() self.resize(300, 500) @Slot() def toggle_buttons(self): """Toggle + and - buttons.""" n_items = len(self.table.selectedItems()) if n_items == 2: # one row (2 items) selected self.add_button.setEnabled(True) self.remove_button.setEnabled(True) elif n_items > 2: # more than one row selected self.add_button.setEnabled(False) self.remove_button.setEnabled(True) else: # no rows selected self.add_button.setEnabled(False) self.remove_button.setEnabled(False) def add_event(self): current_row = self.table.selectedIndexes()[0].row() pos = int(self.table.item(current_row, 0).data(Qt.DisplayRole)) self.table.setSortingEnabled(False) self.table.insertRow(current_row) self.table.setItem(current_row, 0, IntTableWidgetItem(pos)) self.table.setItem(current_row, 1, IntTableWidgetItem(0)) self.table.setSortingEnabled(True) def remove_event(self): rows = {index.row() for index in self.table.selectedIndexes()} self.table.clearSelection() for row in sorted(rows, reverse=True): self.table.removeRow(row)
class ReactionMask(QWidget): """The input mask for a reaction""" def __init__(self, parent: ReactionList): QWidget.__init__(self) self.parent = parent self.reaction = None self.is_valid = True self.changed = False self.setAcceptDrops(False) layout = QVBoxLayout() l = QHBoxLayout() self.delete_button = QPushButton("Delete reaction") self.delete_button.setIcon(QIcon.fromTheme("edit-delete")) policy = QSizePolicy() policy.ShrinkFlag = True self.delete_button.setSizePolicy(policy) l.addWidget(self.delete_button) layout.addItem(l) l = QHBoxLayout() label = QLabel("Id:") self.id = QLineEdit() l.addWidget(label) l.addWidget(self.id) layout.addItem(l) l = QHBoxLayout() label = QLabel("Name:") self.name = QLineEdit() l.addWidget(label) l.addWidget(self.name) layout.addItem(l) l = QHBoxLayout() label = QLabel("Equation:") self.equation = QLineEdit() l.addWidget(label) l.addWidget(self.equation) layout.addItem(l) l = QHBoxLayout() label = QLabel("Rate min:") self.lower_bound = QLineEdit() l.addWidget(label) l.addWidget(self.lower_bound) layout.addItem(l) l = QHBoxLayout() label = QLabel("Rate max:") self.upper_bound = QLineEdit() l.addWidget(label) l.addWidget(self.upper_bound) layout.addItem(l) l = QHBoxLayout() label = QLabel("Coefficient in obj. function:") self.coefficent = QLineEdit() l.addWidget(label) l.addWidget(self.coefficent) layout.addItem(l) l = QHBoxLayout() label = QLabel("Gene reaction rule:") self.gene_reaction_rule = QLineEdit() l.addWidget(label) l.addWidget(self.gene_reaction_rule) layout.addItem(l) l = QVBoxLayout() label = QLabel("Annotations:") l.addWidget(label) l2 = QHBoxLayout() self.annotation = QTableWidget(0, 2) self.annotation.setHorizontalHeaderLabels( ["key", "value"]) self.annotation.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) l2.addWidget(self.annotation) self.add_anno = QPushButton("+") self.add_anno.clicked.connect(self.add_anno_row) l2.addWidget(self.add_anno) l.addItem(l2) layout.addItem(l) l = QVBoxLayout() label = QLabel("Metabolites involved in this reaction:") l.addWidget(label) l2 = QHBoxLayout() self.metabolites = QTreeWidget() self.metabolites.setHeaderLabels(["Id"]) self.metabolites.setSortingEnabled(True) l2.addWidget(self.metabolites) l.addItem(l2) self.metabolites.itemDoubleClicked.connect( self.emit_jump_to_metabolite) layout.addItem(l) self.jump_list = JumpList(self) layout.addWidget(self.jump_list) self.setLayout(layout) self.delete_button.clicked.connect(self.delete_reaction) self.throttler = SignalThrottler(500) self.throttler.triggered.connect(self.reaction_data_changed) self.id.textEdited.connect(self.throttler.throttle) self.name.textEdited.connect(self.throttler.throttle) self.equation.textEdited.connect(self.throttler.throttle) self.lower_bound.textEdited.connect(self.throttler.throttle) self.upper_bound.textEdited.connect(self.throttler.throttle) self.coefficent.textEdited.connect(self.throttler.throttle) self.gene_reaction_rule.textEdited.connect(self.throttler.throttle) self.annotation.itemChanged.connect(self.throttler.throttle) self.validate_mask() def add_anno_row(self): i = self.annotation.rowCount() self.annotation.insertRow(i) self.changed = True def apply(self): try: self.reaction.id = self.id.text() except ValueError: turn_red(self.id) QMessageBox.information( self, 'Invalid id', 'Could not apply changes identifier ' + self.id.text() + ' already used.') else: self.reaction.name = self.name.text() self.reaction.build_reaction_from_string(self.equation.text()) self.reaction.lower_bound = float(self.lower_bound.text()) self.reaction.upper_bound = float(self.upper_bound.text()) self.reaction.objective_coefficient = float(self.coefficent.text()) self.reaction.gene_reaction_rule = self.gene_reaction_rule.text() self.reaction.annotation = {} rows = self.annotation.rowCount() for i in range(0, rows): key = self.annotation.item(i, 0).text() if self.annotation.item(i, 1) is None: value = "" else: value = self.annotation.item(i, 1).text() self.reaction.annotation[key] = value self.changed = False self.reactionChanged.emit(self.reaction) def delete_reaction(self): self.hide() self.reactionDeleted.emit(self.reaction) def validate_id(self): with self.parent.appdata.project.cobra_py_model as model: try: r = cobra.Reaction(id=self.id.text()) model.add_reaction(r) except ValueError: turn_red(self.id) return False else: turn_white(self.id) return True def validate_name(self): with self.parent.appdata.project.cobra_py_model as model: try: r = cobra.Reaction(id="testid", name=self.name.text()) model.add_reaction(r) except ValueError: turn_red(self.name) return False else: turn_white(self.name) return True def validate_equation(self): ok = False test_reaction = cobra.Reaction( "xxxx_cnapy_test_reaction", name="cnapy test reaction") with self.parent.appdata.project.cobra_py_model as model: model.add_reaction(test_reaction) try: eqtxt = self.equation.text().rstrip() if len(eqtxt) > 0 and eqtxt[-1] == '+': turn_red(self.equation) else: test_reaction.build_reaction_from_string(eqtxt) turn_white(self.equation) ok = True except ValueError: turn_red(self.equation) try: test_reaction = self.parent.appdata.project.cobra_py_model.reactions.get_by_id( "xxxx_cnapy_test_reaction") self.parent.appdata.project.cobra_py_model.remove_reactions( [test_reaction], remove_orphans=True) except KeyError: pass return ok def validate_lowerbound(self): try: _x = float(self.lower_bound.text()) except ValueError: turn_red(self.lower_bound) return False else: turn_white(self.lower_bound) return True def validate_upperbound(self): try: _x = float(self.upper_bound.text()) except ValueError: turn_red(self.upper_bound) return False else: turn_white(self.upper_bound) return True def validate_coefficient(self): try: _x = float(self.coefficent.text()) except ValueError: turn_red(self.coefficent) return False else: turn_white(self.coefficent) return True def validate_gene_reaction_rule(self): try: _x = float(self.gene_reaction_rule.text()) except ValueError: turn_red(self.gene_reaction_rule) return False else: turn_white(self.gene_reaction_rule) return True def validate_mask(self): valid_id = self.validate_id() valid_name = self.validate_name() valid_equation = self.validate_equation() valid_lb = self.validate_lowerbound() valid_ub = self.validate_upperbound() valid_coefficient = self.validate_coefficient() if valid_id & valid_name & valid_equation & valid_lb & valid_ub & valid_coefficient: self.is_valid = True else: self.is_valid = False def reaction_data_changed(self): self.changed = True self.validate_mask() if self.is_valid: self.apply() self.update_state() def update_state(self): self.jump_list.clear() for name, m in self.parent.appdata.project.maps.items(): if self.id.text() in m["boxes"]: self.jump_list.add(name) self.metabolites.clear() if self.parent.appdata.project.cobra_py_model.reactions.has_id(self.id.text()): reaction = self.parent.appdata.project.cobra_py_model.reactions.get_by_id( self.id.text()) for m in reaction.metabolites: item = QTreeWidgetItem(self.metabolites) item.setText(0, m.id) item.setText(1, m.name) item.setData(2, 0, m) text = "Id: " + m.id + "\nName: " + m.name item.setToolTip(1, text) def emit_jump_to_map(self, name): self.jumpToMap.emit(name, self.id.text()) def emit_jump_to_metabolite(self, metabolite): self.jumpToMetabolite.emit(str(metabolite.data(2, 0))) jumpToMap = Signal(str, str) jumpToMetabolite = Signal(str) reactionChanged = Signal(cobra.Reaction) reactionDeleted = Signal(cobra.Reaction)
class MetabolitesMask(QWidget): """The input mask for a metabolites""" def __init__(self, appdata): QWidget.__init__(self) self.appdata = appdata self.metabolite = None self.is_valid = True self.changed = False self.setAcceptDrops(False) layout = QVBoxLayout() l = QHBoxLayout() label = QLabel("Id:") self.id = QLineEdit() l.addWidget(label) l.addWidget(self.id) layout.addItem(l) l = QHBoxLayout() label = QLabel("Name:") self.name = QLineEdit() l.addWidget(label) l.addWidget(self.name) layout.addItem(l) l = QHBoxLayout() label = QLabel("Formula:") self.formula = QLineEdit() l.addWidget(label) l.addWidget(self.formula) layout.addItem(l) l = QHBoxLayout() label = QLabel("Charge:") self.charge = QLineEdit() l.addWidget(label) l.addWidget(self.charge) layout.addItem(l) l = QHBoxLayout() label = QLabel("Compartment:") self.compartment = QLineEdit() l.addWidget(label) l.addWidget(self.compartment) layout.addItem(l) l = QVBoxLayout() label = QLabel("Annotations:") l.addWidget(label) l2 = QHBoxLayout() self.annotation = QTableWidget(0, 2) self.annotation.setHorizontalHeaderLabels( ["key", "value"]) self.annotation.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) l2.addWidget(self.annotation) self.add_anno = QPushButton("+") self.add_anno.clicked.connect(self.add_anno_row) l2.addWidget(self.add_anno) l.addItem(l2) layout.addItem(l) l = QVBoxLayout() label = QLabel("Reactions using this metabolite:") l.addWidget(label) l2 = QHBoxLayout() self.reactions = QTreeWidget() self.reactions.setHeaderLabels(["Id"]) self.reactions.setSortingEnabled(True) l2.addWidget(self.reactions) l.addItem(l2) self.reactions.itemDoubleClicked.connect(self.emit_jump_to_reaction) layout.addItem(l) self.setLayout(layout) self.throttler = SignalThrottler(500) self.throttler.triggered.connect(self.metabolites_data_changed) self.id.textEdited.connect(self.throttler.throttle) self.name.textEdited.connect(self.throttler.throttle) self.formula.textEdited.connect(self.throttler.throttle) self.charge.textEdited.connect(self.throttler.throttle) self.compartment.textEdited.connect(self.throttler.throttle) self.annotation.itemChanged.connect(self.throttler.throttle) self.validate_mask() def add_anno_row(self): i = self.annotation.rowCount() self.annotation.insertRow(i) self.changed = True def apply(self): try: self.metabolite.id = self.id.text() except ValueError: turn_red(self.id) QMessageBox.information( self, 'Invalid id', 'Could not apply changes identifier ' + self.id.text()+' already used.') else: self.metabolite.name = self.name.text() self.metabolite.formula = self.formula.text() if self.charge.text() == "": self.metabolite.charge = None else: self.metabolite.charge = int(self.charge.text()) self.metabolite.compartment = self.compartment.text() self.metabolite.annotation = {} rows = self.annotation.rowCount() for i in range(0, rows): key = self.annotation.item(i, 0).text() if self.annotation.item(i, 1) is None: value = "" else: value = self.annotation.item(i, 1).text() self.metabolite.annotation[key] = value self.changed = False self.metaboliteChanged.emit(self.metabolite) def validate_id(self): with self.appdata.project.cobra_py_model as model: text = self.id.text() if text == "": turn_red(self.id) return False if ' ' in text: turn_red(self.id) return False try: m = cobra.Metabolite(id=self.id.text()) model.add_metabolites([m]) except ValueError: turn_red(self.id) return False else: turn_white(self.id) return True def validate_name(self): with self.appdata.project.cobra_py_model as model: try: m = cobra.Metabolite(id="test_id", name=self.name.text()) model.add_metabolites([m]) except ValueError: turn_red(self.name) return False else: turn_white(self.name) return True def validate_formula(self): return True def validate_charge(self): try: if self.charge.text() != "": _x = int(self.charge.text()) except ValueError: turn_red(self.charge) return False else: turn_white(self.charge) return True def validate_compartment(self): try: if ' ' in self.compartment.text(): turn_red(self.compartment) return False if '-' in self.compartment.text(): turn_red(self.compartment) return False _m = cobra.Metabolite(id="test_id", name=self.compartment.text()) except ValueError: turn_red(self.compartment) return False else: turn_white(self.compartment) return True def validate_mask(self): valid_id = self.validate_id() valid_name = self.validate_name() valid_formula = self.validate_formula() valid_charge = self.validate_charge() valid_compartment = self.validate_compartment() if valid_id & valid_name & valid_formula & valid_charge & valid_compartment: self.is_valid = True else: self.is_valid = False def metabolites_data_changed(self): self.changed = True self.validate_mask() if self.is_valid: self.apply() self.update_state() def update_state(self): self.reactions.clear() if self.appdata.project.cobra_py_model.metabolites.has_id(self.id.text()): metabolite = self.appdata.project.cobra_py_model.metabolites.get_by_id( self.id.text()) for r in metabolite.reactions: item = QTreeWidgetItem(self.reactions) item.setText(0, r.id) item.setText(1, r.name) item.setData(2, 0, r) text = "Id: " + r.id + "\nName: " + r.name item.setToolTip(1, text) def emit_jump_to_reaction(self, reaction): self.jumpToReaction.emit(reaction.data(2, 0).id) jumpToReaction = Signal(str) metaboliteChanged = Signal(cobra.Metabolite)
class Extension2ReaderTable(QWidget): """Table showing extension to reader mappings with removal button. Widget presented in preferences-plugin dialog.""" valueChanged = Signal(int) def __init__(self, parent=None, npe2_readers=None, npe1_readers=None): super().__init__(parent=parent) npe2, npe1 = get_all_readers() if npe2_readers is None: npe2_readers = npe2 if npe1_readers is None: npe1_readers = npe1 self._npe2_readers = npe2_readers self._npe1_readers = npe1_readers self._table = QTableWidget() self._table.setShowGrid(False) self._set_up_table() self._edit_row = self._make_new_preference_row() self._populate_table() instructions = QLabel( trans. _('Enter a filename pattern to associate with a reader e.g. "*.tif" for all TIFF files.' ) + trans. _('Available readers will be filtered to those compatible with your pattern. Hover over a reader to see what patterns it accepts.' ) + trans. _('\n\nPreference saving for folder readers is not supported, so these readers are not shown.' ) + trans. _('\n\nFor documentation on valid filename patterns, see https://docs.python.org/3/library/fnmatch.html' )) instructions.setWordWrap(True) instructions.setOpenExternalLinks(True) layout = QVBoxLayout() instructions.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Expanding) layout.addWidget(instructions) layout.addWidget(self._edit_row) layout.addWidget(self._table) self.setLayout(layout) def _set_up_table(self): """Add table columns and headers, define styling""" self._fn_pattern_col = 0 self._reader_col = 1 header_strs = [trans._('Filename Pattern'), trans._('Reader Plugin')] self._table.setColumnCount(2) self._table.setColumnWidth(self._fn_pattern_col, 200) self._table.setColumnWidth(self._reader_col, 200) self._table.verticalHeader().setVisible(False) self._table.setMinimumHeight(120) self._table.horizontalHeader().setStyleSheet( 'border-bottom: 2px solid white;') self._table.setHorizontalHeaderLabels(header_strs) self._table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) def _populate_table(self): """Add row for each extension to reader mapping in settings""" fnpattern2reader = get_settings().plugins.extension2reader if len(fnpattern2reader) > 0: for fn_pattern, plugin_name in fnpattern2reader.items(): self._add_new_row(fn_pattern, plugin_name) else: # Display that there are no filename patterns with reader associations self._display_no_preferences_found() def _make_new_preference_row(self): """Make row for user to add a new filename pattern assignment""" edit_row_widget = QWidget() edit_row_widget.setLayout(QGridLayout()) edit_row_widget.layout().setContentsMargins(0, 0, 0, 0) self._fn_pattern_edit = QLineEdit() self._fn_pattern_edit.setPlaceholderText( "Start typing filename pattern...") self._fn_pattern_edit.textChanged.connect( self._filter_compatible_readers) add_reader_widg = QWidget() add_reader_widg.setLayout(QHBoxLayout()) add_reader_widg.layout().setContentsMargins(0, 0, 0, 0) self._new_reader_dropdown = QComboBox() for i, (plugin_name, display_name) in enumerate( sorted(dict(self._npe2_readers, **self._npe1_readers).items())): self._add_reader_choice(i, plugin_name, display_name) add_btn = QPushButton('Add') add_btn.setToolTip(trans._('Save reader preference for pattern')) add_btn.clicked.connect(self._save_new_preference) add_reader_widg.layout().addWidget(self._new_reader_dropdown) add_reader_widg.layout().addWidget(add_btn) edit_row_widget.layout().addWidget( self._fn_pattern_edit, 0, 0, ) edit_row_widget.layout().addWidget(add_reader_widg, 0, 1) return edit_row_widget def _display_no_preferences_found(self): self._table.setRowCount(1) item = QTableWidgetItem(trans._('No filename preferences found.')) item.setFlags(Qt.NoItemFlags) self._table.setItem(self._fn_pattern_col, 0, item) def _add_reader_choice(self, i, plugin_name, display_name): """Add dropdown item for plugin_name with reader pattern tooltip""" reader_patterns = get_filename_patterns_for_reader(plugin_name) # TODO: no reader_patterns means directory reader, # we don't support preference association yet if not reader_patterns: return self._new_reader_dropdown.addItem(display_name, plugin_name) if '*' in reader_patterns: tooltip_text = 'Accepts all' else: reader_patterns_formatted = ', '.join(sorted( list(reader_patterns))) tooltip_text = f'Accepts: {reader_patterns_formatted}' self._new_reader_dropdown.setItemData(i, tooltip_text, role=Qt.ToolTipRole) def _filter_compatible_readers(self, new_pattern): """Filter reader dropwdown items to those that accept `new_extension`""" self._new_reader_dropdown.clear() readers = self._npe2_readers.copy() to_delete = [] compatible_readers = get_potential_readers(new_pattern) for plugin_name, display_name in readers.items(): if plugin_name not in compatible_readers: to_delete.append(plugin_name) for reader in to_delete: del readers[reader] readers.update(self._npe1_readers) if not readers: self._new_reader_dropdown.addItem("None available") else: for i, (plugin_name, display_name) in enumerate(sorted(readers.items())): self._add_reader_choice(i, plugin_name, display_name) def _save_new_preference(self, event): """Save current preference to settings and show in table""" fn_pattern = self._fn_pattern_edit.text() reader = self._new_reader_dropdown.currentData() if not fn_pattern or not reader: return # if user types pattern that starts with a . it's probably a file extension so prepend the * if fn_pattern.startswith('.'): fn_pattern = f'*{fn_pattern}' if fn_pattern in get_settings().plugins.extension2reader: self._edit_existing_preference(fn_pattern, reader) else: self._add_new_row(fn_pattern, reader) get_settings().plugins.extension2reader = { **get_settings().plugins.extension2reader, fn_pattern: reader, } def _edit_existing_preference(self, fn_pattern, reader): """Edit existing extension preference""" current_reader_label = self.findChild(QLabel, fn_pattern) if reader in self._npe2_readers: reader = self._npe2_readers[reader] current_reader_label.setText(reader) def _add_new_row(self, fn_pattern, reader): """Add new reader preference to table""" last_row = self._table.rowCount() if (last_row == 1 and 'No filename preferences found' in self._table.item(0, 0).text()): self._table.removeRow(0) last_row = 0 self._table.insertRow(last_row) item = QTableWidgetItem(fn_pattern) item.setFlags(Qt.NoItemFlags) self._table.setItem(last_row, self._fn_pattern_col, item) plugin_widg = QWidget() # need object name to easily find row plugin_widg.setObjectName(f'{fn_pattern}') plugin_widg.setLayout(QHBoxLayout()) plugin_widg.layout().setContentsMargins(0, 0, 0, 0) if reader in self._npe2_readers: reader = self._npe2_readers[reader] plugin_label = QLabel(reader, objectName=fn_pattern) # need object name to easily work out which button was clicked remove_btn = QPushButton('X', objectName=fn_pattern) remove_btn.setFixedWidth(30) remove_btn.setStyleSheet('margin: 4px;') remove_btn.setToolTip( trans._('Remove this filename pattern to reader association')) remove_btn.clicked.connect(self.remove_existing_preference) plugin_widg.layout().addWidget(plugin_label) plugin_widg.layout().addWidget(remove_btn) self._table.setCellWidget(last_row, self._reader_col, plugin_widg) def remove_existing_preference(self, event): """Delete extension to reader mapping setting and remove table row""" pattern_to_remove = self.sender().objectName() current_settings = get_settings().plugins.extension2reader # need explicit assignment to new object here for persistence get_settings().plugins.extension2reader = { k: v for k, v in current_settings.items() if k != pattern_to_remove } for i in range(self._table.rowCount()): row_widg_name = self._table.cellWidget( i, self._reader_col).objectName() if row_widg_name == pattern_to_remove: self._table.removeRow(i) break if self._table.rowCount() == 0: self._display_no_preferences_found() def value(self): """Return extension:reader mapping from settings. Returns ------- Dict[str, str] mapping of extension to reader plugin display name """ return get_settings().plugins.extension2reader