def createInputWidgets(self, propertiesWidget): inputsCategory = super(UIConstantNode, self).createInputWidgets(propertiesWidget) selector = QComboBox() overrideType = QCheckBox() for i in self._rawNode.pinTypes: selector.addItem(i) if self.input.dataType in self._rawNode.pinTypes: selector.setCurrentIndex( self._rawNode.pinTypes.index(self.input.dataType)) structSelector = QComboBox() for i in [i.name for i in list(PinStructure)]: structSelector.addItem(i) overrideType.setChecked(self.input._rawPin.changeTypeOnConnection) structSelector.setCurrentIndex(self.input._rawPin._currStructure) selector.setEnabled(self.input._rawPin.changeTypeOnConnection) overrideType.stateChanged.connect(selector.setEnabled) overrideType.stateChanged.connect(self._rawNode.overrideTypeChanged) selector.activated.connect(self._rawNode.updateType) structSelector.activated.connect(self._rawNode.selectStructure) structSelector.activated.connect(self.selectStructure) inputsCategory.insertWidget(0, "DataType", selector) inputsCategory.insertWidget(1, "Change Type On Connection", overrideType) inputsCategory.insertWidget(1, "Structure", structSelector)
def __init__(self, parent=None, **kwargs): super(BoolEditor, self).__init__(parent=parent, **kwargs) self._default_value = False self.cbx = QCheckBox(self) self.main_layout.addWidget(self.cbx) self.cbx.toggled.connect(self.OnValueUpdated)
class BoolInputWidget(InputWidgetSingle): """Boolean data input widget""" def __init__(self, parent=None, **kwds): super(BoolInputWidget, self).__init__(parent=parent, **kwds) self.cb = QCheckBox(self) self.setWidget(self.cb) self.cb.stateChanged.connect( lambda val: self.dataSetCallback(bool(val))) def setWidgetValue(self, val): if bool(val): self.cb.setCheckState(QtCore.Qt.Checked) else: self.cb.setCheckState(QtCore.Qt.Unchecked)
def _build_ui(self): """ ui should have: * table with a list of available tests and show results after they are done * way to filter tests * button to run tests """ layout = QFormLayout() # table to list test classes and the results self.table = QTableWidget() self.table.setColumnCount(2) self.table.setHorizontalHeaderLabels(["test", "result"]) self.table.horizontalHeader().setSectionResizeMode( 0, self.table.horizontalHeader().Interactive) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.horizontalHeader().setSectionResizeMode( 1, self.table.horizontalHeader().Stretch) self.table.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.table.setSelectionBehavior(self.table.SelectRows) layout.addRow(self.table) self.filter = QLineEdit() self.filter.setPlaceholderText("filter test names") self.filter.setClearButtonEnabled(True) self.filter.textChanged.connect(self.apply_filter) layout.addRow(self.filter) self.profile = QCheckBox() self.profile.setToolTip("profile functions called during testing " "using cProfile") layout.addRow("profile calls:", self.profile) self.run_button = QPushButton("run tests") self.run_button.clicked.connect(self.run_tests) self.run_button.setToolTip( "if no tests are selected on the table, run all tests\n" + "otherwise, run selected tests") layout.addRow(self.run_button) self.fill_table() self.table.resizeColumnToContents(0) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
def __init__(self, session, isolde, main_frame, category_grid, region_table, bottom_layout, update_button): super().__init__(session, isolde, main_frame, sim_sensitive=True) from Qt.QtCore import Qt self._data_role = Qt.ItemDataRole.UserRole cg = self.category_grid = category_grid rt = self.region_table = region_table rt.itemClicked.connect(self.item_clicked_cb) self.update_button = update_button update_button.clicked.connect(self.update) from .problems import ProblemAggregator pa = self.problem_aggregator = ProblemAggregator(session) from Qt.QtWidgets import QCheckBox, QLabel, QSpinBox, QDoubleSpinBox, QLabel cg.addWidget(QLabel("Unsatisfied restraints"), 0, 0) cg.addWidget(QLabel("Validation issues"), 0, 1) ocb = self.outliers_only_checkbox = QCheckBox("Outliers only") cg.addWidget(ocb, 0, 2) self.restraint_checkboxes = [] for i, name in enumerate(pa.registered_restraint_problem_types): cb = QCheckBox(name) cb.setChecked(True) self.restraint_checkboxes.append(cb) cg.addWidget(cb, i + 1, 0) self.validation_checkboxes = [] for i, name in enumerate(pa.registered_validation_problem_types): cb = QCheckBox(name) cb.setChecked(True) self.validation_checkboxes.append(cb) cg.addWidget(cb, i + 1, 1) csb = self.cutoff_spinbox = QDoubleSpinBox(main_frame) csbl = QLabel("Dist cutoff") csb.setRange(1.0, 10.0) csb.setSingleStep(1.0) csb.setValue(4.0) bottom_layout.insertWidget(0, csb) bottom_layout.insertWidget(1, csbl) clsb = self.cluster_spinbox = QSpinBox(main_frame) clsbl = QLabel("Min cluster size") clsb.setRange(2, 20) clsb.setValue(5) bottom_layout.insertWidget(2, clsb) bottom_layout.insertWidget(3, clsbl)
def __init__(self, settings, parent=None, description=None, pstvOnly=True): super(ConfigDialog, self).__init__(parent) self.setWindowTitle("Configure source") self.layout = QGridLayout(self) row = 0 self.widgets = {} self.settings = settings # Settings should be a dictionary for name, val in settings.items(): label = QLabel(self) label.setText(str(name)) self.layout.addWidget(label, row, 0, 1, 1) # Check the type of each setting, and create widgets accordingly if isinstance(val, str): # A string of some kind widget = QLineEdit(self) widget.setText(val) elif isinstance(val, list): # A list of alternative values, first is selected print("List: ", name) continue elif isinstance(val, bool): widget = QCheckBox(self) if val: widget.setCheckState(Qt.CheckState.Checked) else: widget.setCheckState(Qt.CheckState.Unchecked) elif isinstance(val, int): widget = QLineEdit(self) widget.setInputMask("9000000") widget.setText(str(val).strip()) elif isinstance(val, float): widget = QLineEdit(self) if pstvOnly: widget.setInputMask("0.000") widget.setText(str(val).strip()) else: print("Ignoring: " + name) continue widget.config = name self.widgets[name] = widget self.layout.addWidget(widget, row, 1, 1, 1) row += 1 # Add OK and Cancel buttons buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonbox.accepted.connect(self.getValues) buttonbox.rejected.connect(self.reject) self.layout.addWidget(buttonbox, row, 1, 2, 1)
def addItem(self, item, selected=False): checkBox = QCheckBox(item, self.menu) checkBox.setTristate(self.triState) if selected: checkBox.setChecked(True) checkableAction = QWidgetAction(self.menu) checkableAction.setDefaultWidget(checkBox) self.menu.addAction(checkableAction) return checkBox
def test_resolution_clicked(self): """Test clicking on resolution checkboxes""" def get_first_tree(stack): for w in stack.widget(1).children(): if isinstance(w, QTreeView): self.assertIsInstance(w.model(), src.tool._RMFHierarchyModel) return w raise ValueError("could not find tree") def get_buttons(stack): for w in stack.widget(1).children(): if isinstance(w, QCheckBox): yield w class TestChimeraObj: pass root = make_node("root", 0) child1 = make_node("child1", 1, resolution=1) child2 = make_node("child2", 2, resolution=10) root.add_children((child1, child2)) mock_session = make_session() m1 = Model(mock_session, 'test') m1.rmf_hierarchy = root m1.rmf_features = [make_node("f1", 4), make_node("f2", 5)] m1.rmf_provenance = [] m1._rmf_resolutions = set((1, 10)) m1._selected_rmf_resolutions = set((1, None)) mock_session.models.add((m1, )) r = src.tool.RMFViewer(mock_session, "RMF Viewer") tree1 = get_first_tree(r.model_stack.widget(0)) res1b, res10b = list(get_buttons(r.model_stack.widget(0))) # Call "clicked" methods directly # Show/hide resolution 10 cb = QCheckBox('foo') cb.setChecked(True) r._resolution_button_clicked(cb, tree1, 10) cb = QCheckBox('bar') cb.setChecked(False) r._resolution_button_clicked(cb, tree1, 10) tree1.selectAll() # Call indirectly via clicking each button for b in res1b, res10b: b.click()
class BoolEditor(BaseEditor, object): attr_type = 'bool' def __init__(self, parent=None, **kwargs): super(BoolEditor, self).__init__(parent=parent, **kwargs) self._default_value = False self.cbx = QCheckBox(self) self.main_layout.addWidget(self.cbx) self.cbx.toggled.connect(self.OnValueUpdated) def get_value(self): return self.cbx.isChecked() def initialize_editor(self): editor_value = self.default_value node_values = self.values if node_values: if len(node_values) > 1: pass elif len(node_values) == 1: if node_values[0]: editor_value = node_values[0] self.cbx.blockSignals(True) self.cbx.setChecked(editor_value) self.cbx.blockSignals(False) def set_connected(self, conn): if conn != self._connection: self._connection = conn self.value_line.setText(conn) self.cbx.setEnabled(False) self.value_line.setProperty('class', 'Connected')
def add_ligand(tool): rows = tool.ligand_table.rowCount() if rows != 0: rows -= 1 ligand_name = QTableWidgetItem() name = "<click to choose>" is_c2 = Qt.Unchecked enabled = True if rows > 0: name = tool.ligand_table.item(rows - 1, 0).text() is_c2 = tool.ligand_table.cellWidget(rows - 1, 1).layout().itemAt(0).widget().checkState() enabled = tool.ligand_table.cellWidget(rows - 1, 1).layout().itemAt(0).widget().isEnabled() ligand_name.setData(Qt.DisplayRole, name) tool.ligand_table.setItem(rows, 0, ligand_name) widget_that_lets_me_horizontally_align_a_checkbox = QWidget() widget_layout = QHBoxLayout(widget_that_lets_me_horizontally_align_a_checkbox) c2 = QCheckBox() c2.setEnabled(enabled) c2.setCheckState(is_c2) widget_layout.addWidget(c2, 0, Qt.AlignHCenter) widget_layout.setContentsMargins(0, 0, 0, 0) tool.ligand_table.setCellWidget( rows, 1, widget_that_lets_me_horizontally_align_a_checkbox ) widget_that_lets_me_horizontally_align_an_icon = QWidget() widget_layout = QHBoxLayout(widget_that_lets_me_horizontally_align_an_icon) section_remove = QLabel() dim = int(1.5 * section_remove.fontMetrics().boundingRect("Q").height()) section_remove.setPixmap( QIcon(section_remove.style().standardIcon( QStyle.SP_DialogDiscardButton) ).pixmap(dim, dim) ) widget_layout.addWidget(section_remove, 0, Qt.AlignHCenter) widget_layout.setContentsMargins(0, 0, 0, 0) tool.ligand_table.setCellWidget( rows, 2, widget_that_lets_me_horizontally_align_an_icon ) rows += 1 tool.ligand_table.insertRow(rows) widget_that_lets_me_horizontally_align_an_icon = QWidget() widget_layout = QHBoxLayout(widget_that_lets_me_horizontally_align_an_icon) ligand_add = QLabel("add ligand") widget_layout.addWidget(ligand_add, 0, Qt.AlignHCenter) widget_layout.setContentsMargins(0, 0, 0, 0) tool.ligand_table.setCellWidget(rows, 1, widget_that_lets_me_horizontally_align_an_icon)
def _build_ui(self): layout = QFormLayout() self.substituent_table = SubstituentTable(singleSelect=True) layout.addRow(self.substituent_table) self.new_residue = QCheckBox() self.new_residue.setCheckState( Qt.Checked if SubstituteMouseMode.newRes else Qt.Unchecked) layout.addRow("new residue:", self.new_residue) self.res_name = QLineEdit() self.res_name.setPlaceholderText("leave blank to keep current") layout.addRow("set residue name:", self.res_name) self.distance_names = QCheckBox() self.distance_names.setCheckState( Qt.Checked if SubstituteMouseMode.useRemoteness else Qt.Unchecked) layout.addRow("distance atom names:", self.distance_names) self.keep_open = QCheckBox() layout.addRow("keep list open:", self.keep_open) do_it = QPushButton("set substituent") do_it.clicked.connect(self.set_sub) layout.addRow(do_it) self.keep_open.stateChanged.connect( lambda state: do_it.setVisible(state != Qt.Checked)) self.keep_open.stateChanged.connect(self.sub_changed) self.substituent_table.table.itemSelectionChanged.connect( self.sub_changed) self.new_residue.stateChanged.connect(self.sub_changed) self.res_name.textChanged.connect(self.sub_changed) self.distance_names.stateChanged.connect(self.sub_changed) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
def _build_ui(self): layout = QGridLayout() tabs = QTabWidget() calc_widget = QWidget() calc_layout = QFormLayout(calc_widget) settings_widget = QWidget() settings_layout = QFormLayout(settings_widget) steric_map_widget = QWidget() steric_layout = QFormLayout(steric_map_widget) cutout_widget = QWidget() vol_cutout_layout = QFormLayout(cutout_widget) layout.addWidget(tabs) tabs.addTab(calc_widget, "calculation") tabs.addTab(settings_widget, "settings") tabs.addTab(steric_map_widget, "steric map") tabs.addTab(cutout_widget, "volume cutout") self.radii_option = QComboBox() self.radii_option.addItems(["Bondi", "UMN"]) ndx = self.radii_option.findText(self.settings.radii, Qt.MatchExactly) self.radii_option.setCurrentIndex(ndx) settings_layout.addRow("radii:", self.radii_option) self.scale = QDoubleSpinBox() self.scale.setValue(self.settings.vdw_scale) self.scale.setSingleStep(0.01) self.scale.setRange(1., 1.5) settings_layout.addRow("VDW scale:", self.scale) set_ligand_atoms = QPushButton("set ligands to current selection") set_ligand_atoms.clicked.connect(self.set_ligand_atoms) set_ligand_atoms.setToolTip( "specify atoms to use in calculation\n" + "by default, all atoms will be used unless a single center is specified\n" + "in the case of a single center, all atoms except the center is used" ) calc_layout.addRow(set_ligand_atoms) self.set_ligand_atoms = set_ligand_atoms self.radius = QDoubleSpinBox() self.radius.setValue(self.settings.center_radius) self.radius.setSuffix(" \u212B") self.radius.setDecimals(1) self.radius.setSingleStep(0.1) self.radius.setRange(1., 15.) settings_layout.addRow("radius around center:", self.radius) self.method = QComboBox() self.method.addItems(["Lebedev", "Monte-Carlo"]) self.method.setToolTip("Lebedev: deterministic method\n" + "Monte-Carlo: non-deterministic method") ndx = self.method.findText(self.settings.method, Qt.MatchExactly) self.method.setCurrentIndex(ndx) settings_layout.addRow("integration method:", self.method) leb_widget = QWidget() leb_layout = QFormLayout(leb_widget) leb_layout.setContentsMargins(0, 0, 0, 0) self.radial_points = QComboBox() self.radial_points.addItems(["20", "32", "64", "75", "99", "127"]) self.radial_points.setToolTip( "more radial points will give more accurate results, but integration will take longer" ) ndx = self.radial_points.findText(self.settings.radial_points, Qt.MatchExactly) self.radial_points.setCurrentIndex(ndx) leb_layout.addRow("radial points:", self.radial_points) self.angular_points = QComboBox() self.angular_points.addItems([ "110", "194", "302", "590", "974", "1454", "2030", "2702", "5810" ]) self.angular_points.setToolTip( "more angular points will give more accurate results, but integration will take longer" ) ndx = self.angular_points.findText(self.settings.angular_points, Qt.MatchExactly) self.angular_points.setCurrentIndex(ndx) leb_layout.addRow("angular points:", self.angular_points) settings_layout.addRow(leb_widget) mc_widget = QWidget() mc_layout = QFormLayout(mc_widget) mc_layout.setContentsMargins(0, 0, 0, 0) self.min_iter = QSpinBox() self.min_iter.setValue(self.settings.minimum_iterations) self.min_iter.setRange(0, 10000) self.min_iter.setToolTip( "each iteration is 3000 points\n" + "iterations continue until convergence criteria are met") mc_layout.addRow("minimum interations:", self.min_iter) settings_layout.addRow(mc_widget) if self.settings.method == "Lebedev": mc_widget.setVisible(False) elif self.settings.method == "Monte-Carlo": leb_widget.setVisible(False) self.report_component = QComboBox() self.report_component.addItems(["total", "quadrants", "octants"]) ndx = self.report_component.findText(self.settings.report_component, Qt.MatchExactly) self.report_component.setCurrentIndex(ndx) settings_layout.addRow("report volume:", self.report_component) self.use_scene = QCheckBox() self.use_scene.setChecked(self.settings.use_scene) self.use_scene.setToolTip( "quadrants/octants will use the orientation the molecule is displayed in" ) settings_layout.addRow("use display orientation:", self.use_scene) self.method.currentTextChanged.connect( lambda text, widget=leb_widget: widget.setVisible(text == "Lebedev" )) self.method.currentTextChanged.connect( lambda text, widget=mc_widget: widget.setVisible(text == "Monte-Carlo")) self.use_centroid = QCheckBox() self.use_centroid.setChecked(self.settings.use_centroid) self.use_centroid.setToolTip( "place the center between selected atoms\n" + "might be useful for polydentate ligands") calc_layout.addRow("use centroid of centers:", self.use_centroid) self.steric_map = QCheckBox() self.steric_map.setChecked(self.settings.steric_map) self.steric_map.setToolTip( "produce a 2D projection of steric bulk\ncauses buried volume to be reported for individual quadrants" ) steric_layout.addRow("create steric map:", self.steric_map) self.num_pts = QSpinBox() self.num_pts.setRange(25, 250) self.num_pts.setValue(self.settings.num_pts) self.num_pts.setToolTip("number of points along x and y axes") steric_layout.addRow("number of points:", self.num_pts) self.include_vbur = QCheckBox() self.include_vbur.setChecked(self.settings.include_vbur) steric_layout.addRow("label quadrants with %V<sub>bur</sub>", self.include_vbur) self.map_shape = QComboBox() self.map_shape.addItems(["circle", "square"]) ndx = self.map_shape.findText(self.settings.map_shape, Qt.MatchExactly) self.map_shape.setCurrentIndex(ndx) steric_layout.addRow("map shape:", self.map_shape) self.auto_minmax = QCheckBox() self.auto_minmax.setChecked(self.settings.auto_minmax) steric_layout.addRow("automatic min. and max.:", self.auto_minmax) self.map_min = QDoubleSpinBox() self.map_min.setRange(-15., 0.) self.map_min.setSuffix(" \u212B") self.map_min.setSingleStep(0.1) self.map_min.setValue(self.settings.map_min) steric_layout.addRow("minimum value:", self.map_min) self.map_max = QDoubleSpinBox() self.map_max.setRange(0., 15.) self.map_max.setSuffix(" \u212B") self.map_max.setSingleStep(0.1) self.map_max.setValue(self.settings.map_max) steric_layout.addRow("maximum value:", self.map_max) self.num_pts.setEnabled(self.settings.steric_map) self.steric_map.stateChanged.connect( lambda state, widget=self.num_pts: widget.setEnabled(state == Qt. Checked)) self.include_vbur.setEnabled(self.settings.steric_map) self.steric_map.stateChanged.connect( lambda state, widget=self.include_vbur: widget.setEnabled( state == Qt.Checked)) self.map_shape.setEnabled(self.settings.steric_map) self.steric_map.stateChanged.connect( lambda state, widget=self.map_shape: widget.setEnabled(state == Qt. Checked)) self.auto_minmax.setEnabled(self.settings.steric_map) self.steric_map.stateChanged.connect( lambda state, widget=self.auto_minmax: widget.setEnabled( state == Qt.Checked)) self.map_min.setEnabled(not self.settings.auto_minmax and self.settings.steric_map) self.steric_map.stateChanged.connect( lambda state, widget=self.map_min, widget2=self.auto_minmax: widget .setEnabled(state == Qt.Checked and not widget2.isChecked())) self.auto_minmax.stateChanged.connect( lambda state, widget=self.map_min, widget2=self.steric_map: widget. setEnabled(not state == Qt.Checked and widget2.isChecked())) self.map_max.setEnabled(not self.settings.auto_minmax and self.settings.steric_map) self.steric_map.stateChanged.connect( lambda state, widget=self.map_max, widget2=self.auto_minmax: widget .setEnabled(state == Qt.Checked and not widget2.isChecked())) self.auto_minmax.stateChanged.connect( lambda state, widget=self.map_max, widget2=self.steric_map: widget. setEnabled(not state == Qt.Checked and widget2.isChecked())) self.display_cutout = QComboBox() self.display_cutout.addItems(["no", "free", "buried"]) ndx = self.display_cutout.findText(self.settings.display_cutout, Qt.MatchExactly) self.display_cutout.setCurrentIndex(ndx) self.display_cutout.setToolTip("show free or buried volume") vol_cutout_layout.addRow("display volume:", self.display_cutout) self.point_spacing = QDoubleSpinBox() self.point_spacing.setDecimals(3) self.point_spacing.setRange(0.01, 0.5) self.point_spacing.setSingleStep(0.005) self.point_spacing.setSuffix(" \u212B") self.point_spacing.setValue(self.settings.point_spacing) self.point_spacing.setToolTip( "distance between points on cutout\n" + "smaller spacing will narrow gaps, but increase time to create the cutout" ) vol_cutout_layout.addRow("point spacing:", self.point_spacing) self.intersection_scale = QDoubleSpinBox() self.intersection_scale.setDecimals(2) self.intersection_scale.setRange(1., 10.) self.intersection_scale.setSingleStep(0.5) self.intersection_scale.setSuffix("x") self.intersection_scale.setToolTip( "relative density of points where VDW radii intersect\n" + "higher density will narrow gaps, but increase time to create cutout" ) self.intersection_scale.setValue(self.settings.intersection_scale) vol_cutout_layout.addRow("intersection density:", self.intersection_scale) self.cutout_labels = QComboBox() self.cutout_labels.addItems(["none", "quadrants", "octants"]) ndx = self.cutout_labels.findText(self.settings.cutout_labels, Qt.MatchExactly) self.cutout_labels.setCurrentIndex(ndx) vol_cutout_layout.addRow("label sections:", self.cutout_labels) self.point_spacing.setEnabled(self.settings.display_cutout != "no") self.intersection_scale.setEnabled( self.settings.display_cutout != "no") self.cutout_labels.setEnabled(self.settings.display_cutout != "no") self.display_cutout.currentTextChanged.connect( lambda text, widget=self.point_spacing: widget.setEnabled(text != "no")) self.display_cutout.currentTextChanged.connect( lambda text, widget=self.intersection_scale: widget.setEnabled( text != "no")) self.display_cutout.currentTextChanged.connect( lambda text, widget=self.cutout_labels: widget.setEnabled(text != "no")) calc_vbur_button = QPushButton( "calculate % buried volume for selected centers") calc_vbur_button.clicked.connect(self.calc_vbur) calc_layout.addRow(calc_vbur_button) self.calc_vbur_button = calc_vbur_button remove_vbur_button = QPushButton( "remove % buried volume visualizations") remove_vbur_button.clicked.connect(self.del_vbur) vol_cutout_layout.addRow(remove_vbur_button) self.table = QTableWidget() self.table.setColumnCount(3) self.table.setHorizontalHeaderLabels(['model', 'center', '%Vbur']) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.resizeColumnToContents(0) self.table.resizeColumnToContents(1) self.table.resizeColumnToContents(2) self.table.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Interactive) self.table.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Interactive) self.table.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) calc_layout.addRow(self.table) menu = QMenuBar() export = menu.addMenu("&Export") clear = QAction("Clear data table", self.tool_window.ui_area) clear.triggered.connect(self.clear_table) export.addAction(clear) copy = QAction("&Copy CSV to clipboard", self.tool_window.ui_area) copy.triggered.connect(self.copy_csv) shortcut = QKeySequence(Qt.CTRL + Qt.Key_C) copy.setShortcut(shortcut) export.addAction(copy) self.copy = copy save = QAction("&Save CSV...", self.tool_window.ui_area) save.triggered.connect(self.save_csv) #this shortcut interferes with main window's save shortcut #I've tried different shortcut contexts to no avail #thanks Qt... #shortcut = QKeySequence(Qt.CTRL + Qt.Key_S) #save.setShortcut(shortcut) #save.setShortcutContext(Qt.WidgetShortcut) export.addAction(save) delimiter = export.addMenu("Delimiter") comma = QAction("comma", self.tool_window.ui_area, checkable=True) comma.setChecked(self.settings.delimiter == "comma") comma.triggered.connect(lambda *args, delim="comma": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(comma) tab = QAction("tab", self.tool_window.ui_area, checkable=True) tab.setChecked(self.settings.delimiter == "tab") tab.triggered.connect(lambda *args, delim="tab": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(tab) space = QAction("space", self.tool_window.ui_area, checkable=True) space.setChecked(self.settings.delimiter == "space") space.triggered.connect(lambda *args, delim="space": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(space) semicolon = QAction("semicolon", self.tool_window.ui_area, checkable=True) semicolon.setChecked(self.settings.delimiter == "semicolon") semicolon.triggered.connect(lambda *args, delim="semicolon": self. settings.__setattr__("delimiter", delim)) delimiter.addAction(semicolon) add_header = QAction("&Include CSV header", self.tool_window.ui_area, checkable=True) add_header.setChecked(self.settings.include_header) add_header.triggered.connect(self.header_check) export.addAction(add_header) comma.triggered.connect( lambda *args, action=tab: action.setChecked(False)) comma.triggered.connect( lambda *args, action=space: action.setChecked(False)) comma.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) tab.triggered.connect( lambda *args, action=comma: action.setChecked(False)) tab.triggered.connect( lambda *args, action=space: action.setChecked(False)) tab.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) space.triggered.connect( lambda *args, action=comma: action.setChecked(False)) space.triggered.connect( lambda *args, action=tab: action.setChecked(False)) space.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=comma: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=tab: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=space: action.setChecked(False)) menu.setNativeMenuBar(False) self._menu = menu layout.setMenuBar(menu) menu.setVisible(True) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
class BondEditor(ToolInstance): help = "https://github.com/QChASM/SEQCROW/wiki/Bond-Editor-Tool" SESSION_ENDURING = False SESSION_SAVE = False def __init__(self, session, name): super().__init__(session, name) self.tool_window = MainToolWindow(self) self.settings = _BondEditorSettings(session, name) self._build_ui() def _build_ui(self): layout = QGridLayout() tabs = QTabWidget() layout.addWidget(tabs) ts_bond_tab = QWidget() ts_options = QFormLayout(ts_bond_tab) self.tsbond_color = ColorButton(has_alpha_channel=False, max_size=(16, 16)) self.tsbond_color.set_color(self.settings.tsbond_color) ts_options.addRow("color:", self.tsbond_color) self.tsbond_transparency = QSpinBox() self.tsbond_transparency.setRange(1, 99) self.tsbond_transparency.setValue(self.settings.tsbond_transparency) self.tsbond_transparency.setSuffix("%") ts_options.addRow("transparency:", self.tsbond_transparency) self.tsbond_radius = QDoubleSpinBox() self.tsbond_radius.setRange(0.01, 1) self.tsbond_radius.setDecimals(3) self.tsbond_radius.setSingleStep(0.005) self.tsbond_radius.setSuffix(" \u212B") self.tsbond_radius.setValue(self.settings.tsbond_radius) ts_options.addRow("radius:", self.tsbond_radius) draw_tsbonds = QPushButton("draw TS bonds on selected atoms/bonds") draw_tsbonds.clicked.connect(self.run_tsbond) ts_options.addRow(draw_tsbonds) self.draw_tsbonds = draw_tsbonds erase_tsbonds = QPushButton("erase selected TS bonds") erase_tsbonds.clicked.connect(self.run_erase_tsbond) ts_options.addRow(erase_tsbonds) self.erase_tsbonds = erase_tsbonds bond_tab = QWidget() bond_options = QFormLayout(bond_tab) self.bond_halfbond = QCheckBox() self.bond_halfbond.setChecked(self.settings.bond_halfbond) self.bond_halfbond.setToolTip( "each half of the bond will be colored according to the atom's color" ) bond_options.addRow("half-bond:", self.bond_halfbond) self.bond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.bond_color.set_color(self.settings.bond_color) self.bond_color.setEnabled( self.bond_halfbond.checkState() != Qt.Checked) self.bond_halfbond.stateChanged.connect( lambda state, widget=self.bond_color: self.bond_color.setEnabled( state != Qt.Checked)) bond_options.addRow("color:", self.bond_color) self.bond_radius = QDoubleSpinBox() self.bond_radius.setRange(0.01, 1) self.bond_radius.setDecimals(3) self.bond_radius.setSingleStep(0.005) self.bond_radius.setSuffix(" \u212B") self.bond_radius.setValue(self.settings.bond_radius) bond_options.addRow("radius:", self.bond_radius) draw_tsbonds = QPushButton("draw bond between selected atoms") draw_tsbonds.clicked.connect(self.run_bond) bond_options.addRow(draw_tsbonds) self.draw_tsbonds = draw_tsbonds erase_bonds = QPushButton("erase selected bonds") erase_bonds.clicked.connect( lambda *, ses=self.session: run(ses, "delete bonds sel")) bond_options.addRow(erase_bonds) self.erase_bonds = erase_bonds hbond_tab = QWidget() hbond_options = QFormLayout(hbond_tab) self.hbond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.hbond_color.set_color(self.settings.hbond_color) hbond_options.addRow("color:", self.hbond_color) self.hbond_radius = QDoubleSpinBox() self.hbond_radius.setDecimals(3) self.hbond_radius.setSuffix(" \u212B") self.hbond_radius.setValue(self.settings.hbond_radius) hbond_options.addRow("radius:", self.hbond_radius) self.hbond_dashes = QSpinBox() self.hbond_dashes.setRange(0, 28) self.hbond_dashes.setSingleStep(2) self.hbond_radius.setSingleStep(0.005) self.hbond_dashes.setValue(self.settings.hbond_dashes) hbond_options.addRow("dashes:", self.hbond_dashes) draw_hbonds = QPushButton("draw H-bonds") draw_hbonds.clicked.connect(self.run_hbond) hbond_options.addRow(draw_hbonds) self.draw_hbonds = draw_hbonds erase_hbonds = QPushButton("erase all H-bonds") erase_hbonds.clicked.connect( lambda *, ses=self.session: run(ses, "~hbonds")) hbond_options.addRow(erase_hbonds) self.erase_hbonds = erase_hbonds tm_bond_tab = QWidget() tm_bond_options = QFormLayout(tm_bond_tab) self.tm_bond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.tm_bond_color.set_color(self.settings.tm_bond_color) tm_bond_options.addRow("color:", self.tm_bond_color) self.tm_bond_radius = QDoubleSpinBox() self.tm_bond_radius.setDecimals(3) self.tm_bond_radius.setSuffix(" \u212B") self.tm_bond_radius.setValue(self.settings.tm_bond_radius) tm_bond_options.addRow("radius:", self.tm_bond_radius) self.tm_bond_dashes = QSpinBox() self.tm_bond_dashes.setRange(0, 28) self.tm_bond_dashes.setSingleStep(2) self.tm_bond_radius.setSingleStep(0.005) self.tm_bond_dashes.setValue(self.settings.tm_bond_dashes) tm_bond_options.addRow("dashes:", self.tm_bond_dashes) draw_tm_bonds = QPushButton("draw metal coordination bonds") draw_tm_bonds.clicked.connect(self.run_tm_bond) tm_bond_options.addRow(draw_tm_bonds) self.draw_tm_bonds = draw_tm_bonds erase_tm_bonds = QPushButton("erase all metal coordination bonds") erase_tm_bonds.clicked.connect(self.del_tm_bond) tm_bond_options.addRow(erase_tm_bonds) self.erase_tm_bonds = erase_tm_bonds bond_length_tab = QWidget() bond_length_layout = QFormLayout(bond_length_tab) self.bond_distance = QDoubleSpinBox() self.bond_distance.setRange(0.5, 10.0) self.bond_distance.setSingleStep(0.05) self.bond_distance.setValue(1.51) self.bond_distance.setSuffix(" \u212B") bond_length_layout.addRow("bond length:", self.bond_distance) self.move_fragment = QComboBox() self.move_fragment.addItems(["both", "smaller", "larger"]) bond_length_layout.addRow("move side:", self.move_fragment) bond_lookup = QGroupBox("bond length lookup:") bond_lookup_layout = QGridLayout(bond_lookup) bond_lookup_layout.addWidget( QLabel("elements:"), 0, 0, ) self.ele1 = ElementButton("C", single_state=True) self.ele1.clicked.connect( lambda *args, button=self.ele1: self.open_ptable(button)) bond_lookup_layout.addWidget(self.ele1, 0, 1, Qt.AlignRight | Qt.AlignTop) bond_lookup_layout.addWidget(QLabel("-"), 0, 2, Qt.AlignHCenter | Qt.AlignVCenter) self.ele2 = ElementButton("C", single_state=True) self.ele2.clicked.connect( lambda *args, button=self.ele2: self.open_ptable(button)) bond_lookup_layout.addWidget(self.ele2, 0, 3, Qt.AlignLeft | Qt.AlignTop) bond_lookup_layout.addWidget(QLabel("bond order:"), 1, 0) self.bond_order = BondOrderSpinBox() self.bond_order.setRange(1., 3.) self.bond_order.setValue(1) self.bond_order.setSingleStep(0.5) self.bond_order.setDecimals(1) self.bond_order.valueChanged.connect(self.check_bond_lengths) bond_lookup_layout.addWidget(self.bond_order, 1, 1, 1, 3) bond_lookup_layout.setColumnStretch(0, 0) bond_lookup_layout.setColumnStretch(1, 0) bond_lookup_layout.setColumnStretch(2, 0) bond_lookup_layout.setColumnStretch(3, 1) bond_length_layout.addRow(bond_lookup) self.status = QStatusBar() self.status.setSizeGripEnabled(False) bond_lookup_layout.addWidget(self.status, 2, 0, 1, 4) self.do_bond_change = QPushButton("change selected bond lengths") self.do_bond_change.clicked.connect(self.change_bond_length) bond_length_layout.addRow(self.do_bond_change) tabs.addTab(bond_tab, "covalent bonds") tabs.addTab(ts_bond_tab, "TS bonds") tabs.addTab(hbond_tab, "H-bonds") tabs.addTab(tm_bond_tab, "coordination bonds") tabs.addTab(bond_length_tab, "bond length") self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None) def run_tsbond(self, *args): args = ["tsbond", "sel"] color = self.tsbond_color.get_color() args.extend(["color", "rgb(%i, %i, %i)" % tuple(color[:-1])]) self.settings.tsbond_color = tuple([c / 255. for c in color]) radius = self.tsbond_radius.value() args.extend(["radius", "%.3f" % radius]) self.settings.tsbond_radius = radius transparency = self.tsbond_transparency.value() args.extend(["transparency", "%i" % transparency]) self.settings.tsbond_transparency = transparency run(self.session, " ".join(args)) def run_erase_tsbond(self, *args): run(self.session, "~tsbond sel") def run_bond(self, *args): # TODO: switch to `bond sel` in 1.2 sel = selected_atoms(self.session) halfbond = self.bond_halfbond.checkState() == Qt.Checked self.settings.bond_halfbond = halfbond if not halfbond: color = self.bond_color.get_color() color = tuple(x / 255. for x in color) self.settings.bond_color = color radius = self.bond_radius.value() self.settings.bond_radius = radius for b in selected_bonds(self.session): b.halfbond = halfbond if not halfbond: b.color = np.array([int(x * 255) for x in color]) b.radius = radius for i, a1 in enumerate(sel): for a2 in sel[:i]: if a1.structure is a2.structure and a2 not in a1.neighbors: new_bond = a1.structure.new_bond(a1, a2) new_bond.halfbond = halfbond if not halfbond: new_bond.color = np.array( [int(x * 255) for x in color]) new_bond.radius = radius def run_hbond(self, *args): args = ["hbonds", "reveal", "true"] color = self.hbond_color.get_color() args.extend(["color", "rgb(%i, %i, %i)" % tuple(color[:-1])]) self.settings.hbond_color = tuple([c / 255. for c in color]) radius = self.hbond_radius.value() args.extend(["radius", "%.3f" % radius]) self.settings.hbond_radius = radius dashes = self.hbond_dashes.value() args.extend(["dashes", "%i" % dashes]) self.settings.hbond_dashes = dashes run(self.session, " ".join(args)) def run_tm_bond(self, *args): color = self.tm_bond_color.get_color() self.settings.tm_bond_color = tuple([c / 255. for c in color]) radius = self.tm_bond_radius.value() self.settings.tm_bond_radius = radius dashes = self.tm_bond_dashes.value() self.settings.tm_bond_dashes = dashes models = self.session.models.list(type=AtomicStructure) for model in models: rescol = ResidueCollection(model, bonds_matter=False) try: tm_list = rescol.find([ AnyTransitionMetal(), "Na", "K", "Rb", "Cs", "Fr", "Mg", "Ca", "Sr", "Ba", "Ra" ]) for tm in tm_list: for atom in rescol.atoms: if atom is tm: continue if atom.is_connected(tm): pbg = model.pseudobond_group( model.PBG_METAL_COORDINATION, create_type="normal") pbg.new_pseudobond(tm.chix_atom, atom.chix_atom) pbg.dashes = dashes pbg.color = color pbg.radius = radius except LookupError: pass def del_tm_bond(self, *args): models = self.session.models.list(type=AtomicStructure) for model in models: pbg = model.pseudobond_group(model.PBG_METAL_COORDINATION, create_type=None) if pbg is not None: pbg.delete() def open_ptable(self, button): self.tool_window.create_child_window("select element", window_class=PTable2, button=button, callback=self.check_bond_lengths) def check_bond_lengths(self, *args): ele1 = self.ele1.text() ele2 = self.ele2.text() key = ORDER_BOND_ORDER.key(ele1, ele2) order = "%.1f" % self.bond_order.value() if key in ORDER_BOND_ORDER.bonds and order in ORDER_BOND_ORDER.bonds[ key]: self.bond_distance.setValue(ORDER_BOND_ORDER.bonds[key][order]) self.status.showMessage("") else: self.status.showMessage("no bond data for %s-%s %sx bonds" % (ele1, ele2, order)) def change_bond_length(self, *args): dist = self.bond_distance.value() atom_pairs = [] sel = selected_atoms(self.session) if len(sel) == 2 and sel[0].structure is sel[1].structure: atom_pairs.append(sel) for bond in selected_bonds(self.session): if not all(atom in sel for atom in bond.atoms): atom_pairs.append(bond.atoms) for bond in selected_pseudobonds(self.session): if not all(atom in sel for atom in bond.atoms): atom_pairs.append(bond.atoms) for pair in atom_pairs: atom1, atom2 = pair frag1 = get_fragment(atom1, stop=atom2, max_len=atom1.structure.num_atoms) frag2 = get_fragment(atom2, stop=atom1, max_len=atom1.structure.num_atoms) v = atom2.coord - atom1.coord cur_dist = np.linalg.norm(v) change = dist - cur_dist if self.move_fragment.currentText() == "both": change = 0.5 * change frag1.coords -= change * v / cur_dist frag2.coords += change * v / cur_dist elif self.move_fragment.currentText() == "smaller": if len(frag1) < len(frag2) or (len(frag1) == len(frag2) and sum(frag1.elements.masses) < sum(frag2.elements.masses)): frag1.coords -= change * v / cur_dist else: frag2.coords += change * v / cur_dist elif self.move_fragment.currentText() == "larger": if len(frag1) > len(frag2) or (len(frag1) == len(frag2) and sum(frag1.elements.masses) > sum(frag2.elements.masses)): frag1.coords -= change * v / cur_dist else: frag2.coords += change * v / cur_dist
def _build_ui(self): layout = QGridLayout() tabs = QTabWidget() layout.addWidget(tabs) ts_bond_tab = QWidget() ts_options = QFormLayout(ts_bond_tab) self.tsbond_color = ColorButton(has_alpha_channel=False, max_size=(16, 16)) self.tsbond_color.set_color(self.settings.tsbond_color) ts_options.addRow("color:", self.tsbond_color) self.tsbond_transparency = QSpinBox() self.tsbond_transparency.setRange(1, 99) self.tsbond_transparency.setValue(self.settings.tsbond_transparency) self.tsbond_transparency.setSuffix("%") ts_options.addRow("transparency:", self.tsbond_transparency) self.tsbond_radius = QDoubleSpinBox() self.tsbond_radius.setRange(0.01, 1) self.tsbond_radius.setDecimals(3) self.tsbond_radius.setSingleStep(0.005) self.tsbond_radius.setSuffix(" \u212B") self.tsbond_radius.setValue(self.settings.tsbond_radius) ts_options.addRow("radius:", self.tsbond_radius) draw_tsbonds = QPushButton("draw TS bonds on selected atoms/bonds") draw_tsbonds.clicked.connect(self.run_tsbond) ts_options.addRow(draw_tsbonds) self.draw_tsbonds = draw_tsbonds erase_tsbonds = QPushButton("erase selected TS bonds") erase_tsbonds.clicked.connect(self.run_erase_tsbond) ts_options.addRow(erase_tsbonds) self.erase_tsbonds = erase_tsbonds bond_tab = QWidget() bond_options = QFormLayout(bond_tab) self.bond_halfbond = QCheckBox() self.bond_halfbond.setChecked(self.settings.bond_halfbond) self.bond_halfbond.setToolTip( "each half of the bond will be colored according to the atom's color" ) bond_options.addRow("half-bond:", self.bond_halfbond) self.bond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.bond_color.set_color(self.settings.bond_color) self.bond_color.setEnabled( self.bond_halfbond.checkState() != Qt.Checked) self.bond_halfbond.stateChanged.connect( lambda state, widget=self.bond_color: self.bond_color.setEnabled( state != Qt.Checked)) bond_options.addRow("color:", self.bond_color) self.bond_radius = QDoubleSpinBox() self.bond_radius.setRange(0.01, 1) self.bond_radius.setDecimals(3) self.bond_radius.setSingleStep(0.005) self.bond_radius.setSuffix(" \u212B") self.bond_radius.setValue(self.settings.bond_radius) bond_options.addRow("radius:", self.bond_radius) draw_tsbonds = QPushButton("draw bond between selected atoms") draw_tsbonds.clicked.connect(self.run_bond) bond_options.addRow(draw_tsbonds) self.draw_tsbonds = draw_tsbonds erase_bonds = QPushButton("erase selected bonds") erase_bonds.clicked.connect( lambda *, ses=self.session: run(ses, "delete bonds sel")) bond_options.addRow(erase_bonds) self.erase_bonds = erase_bonds hbond_tab = QWidget() hbond_options = QFormLayout(hbond_tab) self.hbond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.hbond_color.set_color(self.settings.hbond_color) hbond_options.addRow("color:", self.hbond_color) self.hbond_radius = QDoubleSpinBox() self.hbond_radius.setDecimals(3) self.hbond_radius.setSuffix(" \u212B") self.hbond_radius.setValue(self.settings.hbond_radius) hbond_options.addRow("radius:", self.hbond_radius) self.hbond_dashes = QSpinBox() self.hbond_dashes.setRange(0, 28) self.hbond_dashes.setSingleStep(2) self.hbond_radius.setSingleStep(0.005) self.hbond_dashes.setValue(self.settings.hbond_dashes) hbond_options.addRow("dashes:", self.hbond_dashes) draw_hbonds = QPushButton("draw H-bonds") draw_hbonds.clicked.connect(self.run_hbond) hbond_options.addRow(draw_hbonds) self.draw_hbonds = draw_hbonds erase_hbonds = QPushButton("erase all H-bonds") erase_hbonds.clicked.connect( lambda *, ses=self.session: run(ses, "~hbonds")) hbond_options.addRow(erase_hbonds) self.erase_hbonds = erase_hbonds tm_bond_tab = QWidget() tm_bond_options = QFormLayout(tm_bond_tab) self.tm_bond_color = ColorButton(has_alpha_channel=True, max_size=(16, 16)) self.tm_bond_color.set_color(self.settings.tm_bond_color) tm_bond_options.addRow("color:", self.tm_bond_color) self.tm_bond_radius = QDoubleSpinBox() self.tm_bond_radius.setDecimals(3) self.tm_bond_radius.setSuffix(" \u212B") self.tm_bond_radius.setValue(self.settings.tm_bond_radius) tm_bond_options.addRow("radius:", self.tm_bond_radius) self.tm_bond_dashes = QSpinBox() self.tm_bond_dashes.setRange(0, 28) self.tm_bond_dashes.setSingleStep(2) self.tm_bond_radius.setSingleStep(0.005) self.tm_bond_dashes.setValue(self.settings.tm_bond_dashes) tm_bond_options.addRow("dashes:", self.tm_bond_dashes) draw_tm_bonds = QPushButton("draw metal coordination bonds") draw_tm_bonds.clicked.connect(self.run_tm_bond) tm_bond_options.addRow(draw_tm_bonds) self.draw_tm_bonds = draw_tm_bonds erase_tm_bonds = QPushButton("erase all metal coordination bonds") erase_tm_bonds.clicked.connect(self.del_tm_bond) tm_bond_options.addRow(erase_tm_bonds) self.erase_tm_bonds = erase_tm_bonds bond_length_tab = QWidget() bond_length_layout = QFormLayout(bond_length_tab) self.bond_distance = QDoubleSpinBox() self.bond_distance.setRange(0.5, 10.0) self.bond_distance.setSingleStep(0.05) self.bond_distance.setValue(1.51) self.bond_distance.setSuffix(" \u212B") bond_length_layout.addRow("bond length:", self.bond_distance) self.move_fragment = QComboBox() self.move_fragment.addItems(["both", "smaller", "larger"]) bond_length_layout.addRow("move side:", self.move_fragment) bond_lookup = QGroupBox("bond length lookup:") bond_lookup_layout = QGridLayout(bond_lookup) bond_lookup_layout.addWidget( QLabel("elements:"), 0, 0, ) self.ele1 = ElementButton("C", single_state=True) self.ele1.clicked.connect( lambda *args, button=self.ele1: self.open_ptable(button)) bond_lookup_layout.addWidget(self.ele1, 0, 1, Qt.AlignRight | Qt.AlignTop) bond_lookup_layout.addWidget(QLabel("-"), 0, 2, Qt.AlignHCenter | Qt.AlignVCenter) self.ele2 = ElementButton("C", single_state=True) self.ele2.clicked.connect( lambda *args, button=self.ele2: self.open_ptable(button)) bond_lookup_layout.addWidget(self.ele2, 0, 3, Qt.AlignLeft | Qt.AlignTop) bond_lookup_layout.addWidget(QLabel("bond order:"), 1, 0) self.bond_order = BondOrderSpinBox() self.bond_order.setRange(1., 3.) self.bond_order.setValue(1) self.bond_order.setSingleStep(0.5) self.bond_order.setDecimals(1) self.bond_order.valueChanged.connect(self.check_bond_lengths) bond_lookup_layout.addWidget(self.bond_order, 1, 1, 1, 3) bond_lookup_layout.setColumnStretch(0, 0) bond_lookup_layout.setColumnStretch(1, 0) bond_lookup_layout.setColumnStretch(2, 0) bond_lookup_layout.setColumnStretch(3, 1) bond_length_layout.addRow(bond_lookup) self.status = QStatusBar() self.status.setSizeGripEnabled(False) bond_lookup_layout.addWidget(self.status, 2, 0, 1, 4) self.do_bond_change = QPushButton("change selected bond lengths") self.do_bond_change.clicked.connect(self.change_bond_length) bond_length_layout.addRow(self.do_bond_change) tabs.addTab(bond_tab, "covalent bonds") tabs.addTab(ts_bond_tab, "TS bonds") tabs.addTab(hbond_tab, "H-bonds") tabs.addTab(tm_bond_tab, "coordination bonds") tabs.addTab(bond_length_tab, "bond length") self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
class BaseFileFolderDialog(BaseDialog, abstract_dialog.AbstractFileFolderDialog): """ Base dialog classes for folders and files """ def_title = 'Select File' def_size = (200, 125) def_use_app_browser = False def __init__(self, name='BaseFileFolder', parent=None, **kwargs): super(BaseFileFolderDialog, self).__init__(name=name, parent=parent) self.directory = None self.filters = None self._use_app_browser = kwargs.pop('use_app_browser', self.def_use_app_browser) self.set_filters('All Files (*.*)') # By default, we set the directory to the user folder self.set_directory(os.path.expanduser('~')) self.center() def open_app_browser(self): return def ui(self): super(BaseFileFolderDialog, self).ui() from tpDcc.libs.qt.widgets import directory self.places = dict() self.grid = layouts.GridLayout() sub_grid = layouts.GridLayout() self.grid.addWidget(QLabel('Path:'), 0, 0, Qt.AlignRight) self.path_edit = QLineEdit(self) self.path_edit.setReadOnly(True) self.filter_box = QComboBox(self) self.file_edit = QLineEdit(self) self.view = directory.FileListWidget(self) self.view.setWrapping(True) self.view.setFocusPolicy(Qt.StrongFocus) self.open_button = QPushButton('Select', self) self.cancel_button = QPushButton('Cancel', self) size = QSize(32, 24) self.up_button = QPushButton('Up') self.up_button.setToolTip('Go up') self.up_button.setMinimumSize(size) self.up_button.setMaximumSize(size) size = QSize(56, 24) self.refresh_button = QPushButton('Reload') self.refresh_button.setToolTip('Reload file list') self.refresh_button.setMinimumSize(size) self.refresh_button.setMaximumSize(size) self.show_hidden = QCheckBox('Hidden') self.show_hidden.setChecked(False) self.show_hidden.setToolTip('Toggle show hidden files') sub_grid.addWidget(self.up_button, 0, 1) sub_grid.addWidget(self.path_edit, 0, 2) sub_grid.addWidget(self.refresh_button, 0, 3) sub_grid.addWidget(self.show_hidden, 0, 4) self.grid.addLayout(sub_grid, 0, 1) self.grid.addWidget(self.get_drives_widget(), 1, 0) self.grid.addWidget(self.view, 1, 1) self.grid.addWidget(QLabel('File name:'), 7, 0, Qt.AlignRight) self.grid.addWidget(self.file_edit, 7, 1) self.filter_label = QLabel('Filter:') self.grid.addWidget(self.filter_label, 8, 0, Qt.AlignRight) self.grid.addWidget(self.filter_box, 8, 1) hbox = layouts.GridLayout() hbox.addWidget(self.open_button, 0, 0, Qt.AlignRight) hbox.addWidget(self.cancel_button, 0, 1, Qt.AlignRight) self.grid.addLayout(hbox, 9, 1, Qt.AlignRight) self.main_layout.addLayout(self.grid) self.setGeometry(200, 100, 600, 400) self.open_button.clicked.connect(self.accept) self.cancel_button.clicked.connect(self.reject) self.up_button.clicked.connect(self.go_up) self.refresh_button.clicked.connect(self.update_view) self.show_hidden.stateChanged.connect(self.update_view) self.view.directory_activated.connect( self.activate_directory_from_view) self.view.file_activated.connect(self.activate_file_from_view) self.view.file_selected.connect(self.select_file_item) self.view.folder_selected.connect(self.select_folder_item) self.view.up_requested.connect(self.go_up) self.view.update_requested.connect(self.update_view) def exec_(self, *args, **kwargs): if self._use_app_browser: return self.open_app_browser() else: self.update_view() self.filter_box.currentIndexChanged.connect(self.update_view) accepted = super(BaseFileFolderDialog, self).exec_() self.filter_box.currentIndexChanged.disconnect(self.update_view) return self.get_result() if accepted == 1 else None def set_filters(self, filters, selected=0): self.filter_box.clear() filter_types = filters.split(';;') for ft in filter_types: extensions = string.extract(ft, '(', ')') filter_name = string.rstrips(ft, '({})'.format(extensions)) extensions = extensions.split(' ') self.filter_box.addItem( '{} ({})'.format(filter_name, ','.join(extensions)), extensions) if 0 <= selected < self.filter_box.count(): self.filter_box.setCurrentIndex(selected) self.filters = filters def get_drives_widget(self): """ Returns a QGroupBox widget that contains all disk drivers of the PC in a vertical layout :return: QGroupBox """ w = QGroupBox('') w.setParent(self) box = layouts.VerticalLayout() box.setAlignment(Qt.AlignTop) places = [(getpass.getuser(), os.path.realpath(os.path.expanduser('~')))] places += [ (q, q) for q in [os.path.realpath(x.absolutePath()) for x in QDir().drives()] ] for label, loc in places: icon = QFileIconProvider().icon(QFileInfo(loc)) drive_btn = QRadioButton(label) drive_btn.setIcon(icon) drive_btn.setToolTip(loc) drive_btn.setProperty('path', loc) drive_btn.clicked.connect(self.go_to_drive) self.places[loc] = drive_btn box.addWidget(drive_btn) w.setLayout(box) return w def go_to_drive(self): """ Updates widget to show the content of the selected disk drive """ sender = self.sender() self.set_directory(sender.property('path'), False) def get_result(self): tf = self.file_edit.text() sf = self.get_file_path(tf) return sf, os.path.dirname(sf), tf.split(os.pathsep) def get_filter_patterns(self): """ Get list of filter patterns that are being used by the widget :return: list<str> """ idx = self.filter_box.currentIndex() if idx >= 0: return self.filter_box.itemData(idx) else: return [] def get_file_path(self, file_name): """ Returns file path of the given file name taking account the selected directory :param file_name: str, name of the file without path :return: str """ sname = file_name.split(os.pathsep)[0] return os.path.realpath( os.path.join(os.path.abspath(self.directory), sname)) # def accept(self): # self._overlay.close() # super(BaseFileFolderDialog, self).accept() # # # def reject(self): # self._overlay.close() # super(BaseFileFolderDialog, self).reject() def update_view(self): """ Updates file/folder view :return: """ self.view.clear() qdir = QDir(self.directory) qdir.setNameFilters(self.get_filter_patterns()) filters = QDir.Dirs | QDir.AllDirs | QDir.Files | QDir.NoDot | QDir.NoDotDot if self.show_hidden.isChecked(): filters = filters | QDir.Hidden entries = qdir.entryInfoList(filters=filters, sort=QDir.DirsFirst | QDir.Name) file_path = self.get_file_path('..') if os.path.exists(file_path) and file_path != self.directory: icon = QFileIconProvider().icon(QFileInfo(self.directory)) QListWidgetItem(icon, '..', self.view, 0) for info in entries: icon = QFileIconProvider().icon(info) suf = info.completeSuffix() name, tp = (info.fileName(), 0) if info.isDir() else ( '%s%s' % (info.baseName(), '.%s' % suf if suf else ''), 1) QListWidgetItem(icon, name, self.view, tp) self.view.setFocus() def set_directory(self, path, check_drive=True): """ Sets the directory that you want to explore :param path: str, valid path :param check_drive: bool, :return: """ self.directory = os.path.realpath(path) self.path_edit.setText(self.directory) self.file_edit.setText('') # If necessary, update selected disk driver if check_drive: for loc in self.places: rb = self.places[loc] rb.setAutoExclusive(False) rb.setChecked(loc.lower() == self.directory.lower()) rb.setAutoExclusive(True) self.update_view() self.up_button.setEnabled(not self.cant_go_up()) def go_up(self): """ Updates the current directory to go to its parent directory """ self.set_directory(os.path.dirname(self.directory)) def cant_go_up(self): """ Checks whether we can naviage to current selected parent directory or not :return: bool """ return os.path.dirname(self.directory) == self.directory def activate_directory_from_view(self, name): """ Updates selected directory :param name: str, name of the directory """ self.set_directory(os.path.join(self.directory, name)) def activate_file_from_view(self, name): """ Updates selected file text and returns its info by accepting it :param name: str, name of the file """ self.select_file_item(name=name) self.accept() def select_file_item(self, name): """ Updates selected file text and returns its info by accepting it :param name: str, name of the file """ self.file_edit.setText(name) def select_folder_item(self, name): """ Updates selected folder text and returns its info by accepting it :param name: str, name of the folder """ self.file_edit.setText(name)
def ui(self): super(BaseFileFolderDialog, self).ui() from tpDcc.libs.qt.widgets import directory self.places = dict() self.grid = layouts.GridLayout() sub_grid = layouts.GridLayout() self.grid.addWidget(QLabel('Path:'), 0, 0, Qt.AlignRight) self.path_edit = QLineEdit(self) self.path_edit.setReadOnly(True) self.filter_box = QComboBox(self) self.file_edit = QLineEdit(self) self.view = directory.FileListWidget(self) self.view.setWrapping(True) self.view.setFocusPolicy(Qt.StrongFocus) self.open_button = QPushButton('Select', self) self.cancel_button = QPushButton('Cancel', self) size = QSize(32, 24) self.up_button = QPushButton('Up') self.up_button.setToolTip('Go up') self.up_button.setMinimumSize(size) self.up_button.setMaximumSize(size) size = QSize(56, 24) self.refresh_button = QPushButton('Reload') self.refresh_button.setToolTip('Reload file list') self.refresh_button.setMinimumSize(size) self.refresh_button.setMaximumSize(size) self.show_hidden = QCheckBox('Hidden') self.show_hidden.setChecked(False) self.show_hidden.setToolTip('Toggle show hidden files') sub_grid.addWidget(self.up_button, 0, 1) sub_grid.addWidget(self.path_edit, 0, 2) sub_grid.addWidget(self.refresh_button, 0, 3) sub_grid.addWidget(self.show_hidden, 0, 4) self.grid.addLayout(sub_grid, 0, 1) self.grid.addWidget(self.get_drives_widget(), 1, 0) self.grid.addWidget(self.view, 1, 1) self.grid.addWidget(QLabel('File name:'), 7, 0, Qt.AlignRight) self.grid.addWidget(self.file_edit, 7, 1) self.filter_label = QLabel('Filter:') self.grid.addWidget(self.filter_label, 8, 0, Qt.AlignRight) self.grid.addWidget(self.filter_box, 8, 1) hbox = layouts.GridLayout() hbox.addWidget(self.open_button, 0, 0, Qt.AlignRight) hbox.addWidget(self.cancel_button, 0, 1, Qt.AlignRight) self.grid.addLayout(hbox, 9, 1, Qt.AlignRight) self.main_layout.addLayout(self.grid) self.setGeometry(200, 100, 600, 400) self.open_button.clicked.connect(self.accept) self.cancel_button.clicked.connect(self.reject) self.up_button.clicked.connect(self.go_up) self.refresh_button.clicked.connect(self.update_view) self.show_hidden.stateChanged.connect(self.update_view) self.view.directory_activated.connect( self.activate_directory_from_view) self.view.file_activated.connect(self.activate_file_from_view) self.view.file_selected.connect(self.select_file_item) self.view.folder_selected.connect(self.select_folder_item) self.view.up_requested.connect(self.go_up) self.view.update_requested.connect(self.update_view)
class Second(QMainWindow): def __init__(self): super(Second, self).__init__() self.setWindowTitle("Add new device") self.nameLabel = QLabel(self) self.nameLabel.move(10, 10) self.nameLabel.setText("Device name") self.nameEntry = QLineEdit(self) self.nameEntry.move(10, 40) self.nameEntry.resize(100, 30) self.colorLabel = QLabel(self) self.colorLabel.move(120, 10) self.colorLabel.setText("Color cameras") self.colorEntry = QLineEdit(self) self.colorEntry.move(140, 40) self.colorEntry.resize(70, 30) self.colorEntry.setValidator(QtGui.QIntValidator()) self.monoLabel = QLabel(self) self.monoLabel.move(230, 10) self.monoLabel.setText("Mono cameras") self.monoEntry = QLineEdit(self) self.monoEntry.move(250, 40) self.monoEntry.resize(70, 30) self.monoEntry.setValidator(QtGui.QIntValidator()) self.depthPresent = QCheckBox("Include depth", self) self.depthPresent.move(10, 80) self.depthPresent.resize(150, 30) self.depthPresent.stateChanged.connect(self.toggle_depth) self.leftfovLabel = QLabel(self) self.leftfovLabel.move(10, 120) self.leftfovLabel.setText("Left FOV deg.") self.leftfovEntry = QLineEdit(self) self.leftfovEntry.move(180, 120) self.leftfovEntry.resize(140, 30) self.leftfovEntry.setValidator(QtGui.QDoubleValidator()) self.rightfovLabel = QLabel(self) self.rightfovLabel.move(10, 160) self.rightfovLabel.setText("Right FOV deg.") self.rightfovEntry = QLineEdit(self) self.rightfovEntry.move(180, 160) self.rightfovEntry.resize(140, 30) self.rightfovEntry.setValidator(QtGui.QDoubleValidator()) self.rgbfovLabel = QLabel(self) self.rgbfovLabel.move(10, 200) self.rgbfovLabel.setText("RGB FOV deg.") self.rgbfovEntry = QLineEdit(self) self.rgbfovEntry.move(180, 200) self.rgbfovEntry.resize(140, 30) self.rgbfovEntry.setValidator(QtGui.QDoubleValidator()) self.lrdistanceLabel = QLabel(self) self.lrdistanceLabel.move(10, 240) self.lrdistanceLabel.resize(200, 30) self.lrdistanceLabel.setText("Left - Right distance cm.") self.lrdistanceEntry = QLineEdit(self) self.lrdistanceEntry.move(180, 240) self.lrdistanceEntry.resize(140, 30) self.lrdistanceEntry.setValidator(QtGui.QDoubleValidator()) self.lrgbdistanceLabel = QLabel(self) self.lrgbdistanceLabel.move(10, 280) self.lrgbdistanceLabel.resize(200, 30) self.lrgbdistanceLabel.setText("Left - RGB distance cm.") self.lrgbdistanceEntry = QLineEdit(self) self.lrgbdistanceEntry.move(180, 280) self.lrgbdistanceEntry.resize(140, 30) self.lrgbdistanceEntry.setValidator(QtGui.QDoubleValidator()) self.saveButton = QPushButton("Save", self) self.saveButton.resize(100, 30) self.saveButton.clicked.connect(self.save) self.cancelButton = QPushButton("Cancel", self) self.cancelButton.resize(100, 30) self.cancelButton.clicked.connect(self.cancel) self.toggle_depth(False) def toggle_depth(self, checked): if checked: self.leftfovLabel.setVisible(True) self.leftfovEntry.setVisible(True) self.rightfovLabel.setVisible(True) self.rightfovEntry.setVisible(True) self.rgbfovLabel.setVisible(True) self.rgbfovEntry.setVisible(True) self.lrdistanceLabel.setVisible(True) self.lrdistanceEntry.setVisible(True) self.lrgbdistanceLabel.setVisible(True) self.lrgbdistanceEntry.setVisible(True) self.saveButton.move(200, 330) self.cancelButton.move(30, 330) self.resize(330, 380) else: self.leftfovLabel.setVisible(False) self.leftfovEntry.setVisible(False) self.rightfovLabel.setVisible(False) self.rightfovEntry.setVisible(False) self.rgbfovLabel.setVisible(False) self.rgbfovEntry.setVisible(False) self.lrdistanceLabel.setVisible(False) self.lrdistanceEntry.setVisible(False) self.lrgbdistanceLabel.setVisible(False) self.lrgbdistanceEntry.setVisible(False) self.saveButton.move(200, 120) self.cancelButton.move(30, 120) self.resize(330, 170) def save(self, *args, **kwargs): try: data = { "name": self.nameEntry.text(), "color_count": int(self.colorEntry.text()), "mono_count": int(self.monoEntry.text()), } if self.depthPresent.isChecked(): data.update({ "depth": True, "left_fov_deg": float(self.leftfovEntry.text()), "right_fov_deg": float(self.rightfovEntry.text()), "rgb_fov_deg": float(self.rgbfovEntry.text()), "left_to_right_distance_cm": float(self.lrdistanceEntry.text()), "left_to_rgb_distance_cm": float(self.lrgbdistanceEntry.text()), }) append_to_json( data, Path(__file__).parent.parent / Path('custom_devices.json')) self.close() INITIALIZE() self.instance.getRegisteredTools(['NodeBoxTool'])[0].refresh() except Exception as e: QMessageBox.warning(self, "Warning", str(e)) def cancel(self): self.close()
def _build_ui(self): layout = QFormLayout() self.radii_option = QComboBox() self.radii_option.addItems(["Bondi", "UMN"]) ndx = self.radii_option.findText(self.settings.radii, Qt.MatchExactly) self.radii_option.setCurrentIndex(ndx) layout.addRow("radii:", self.radii_option) self.L_option = QComboBox() self.L_option.addItems([ "to centroid of coordinating atoms", "bisect angle between coordinating atoms" ]) ndx = self.L_option.findText(self.settings.L_option, Qt.MatchExactly) self.L_option.setCurrentIndex(ndx) layout.addRow("L axis:", self.L_option) self.sterimol2vec = QGroupBox("Sterimol2Vec") sterimol2vec_layout = QFormLayout(self.sterimol2vec) self.at_L = QDoubleSpinBox() self.at_L.setRange(-10, 30) self.at_L.setDecimals(2) self.at_L.setSingleStep(0.25) self.at_L.setValue(self.settings.at_L) sterimol2vec_layout.addRow("L value:", self.at_L) layout.addRow(self.sterimol2vec) self.sterimol2vec.setCheckable(True) self.sterimol2vec.toggled.connect(lambda x: self.at_L.setEnabled(x)) self.sterimol2vec.setChecked(self.settings.sterimol2vec) self.display_vectors = QCheckBox() self.display_vectors.setChecked(self.settings.display_vectors) layout.addRow("show vectors:", self.display_vectors) self.display_radii = QCheckBox() self.display_radii.setChecked(self.settings.display_radii) layout.addRow("show radii:", self.display_radii) calc_sterimol_button = QPushButton( "calculate parameters for selected ligands") calc_sterimol_button.clicked.connect(self.calc_sterimol) layout.addRow(calc_sterimol_button) self.calc_sterimol_button = calc_sterimol_button remove_sterimol_button = QPushButton("remove Sterimol visualizations") remove_sterimol_button.clicked.connect(self.del_sterimol) layout.addRow(remove_sterimol_button) self.remove_sterimol_button = remove_sterimol_button self.table = QTableWidget() self.table.setColumnCount(8) self.table.setHorizontalHeaderLabels([ 'model', 'coord. atoms', 'B\u2081', 'B\u2082', 'B\u2083', 'B\u2084', 'B\u2085', 'L', ]) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.resizeColumnToContents(0) self.table.resizeColumnToContents(1) self.table.resizeColumnToContents(2) self.table.resizeColumnToContents(3) self.table.resizeColumnToContents(4) self.table.resizeColumnToContents(5) self.table.resizeColumnToContents(6) self.table.resizeColumnToContents(7) self.table.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 3, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 4, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 5, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 6, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 7, QHeaderView.Stretch) layout.addRow(self.table) menu = QMenuBar() export = menu.addMenu("&Export") clear = QAction("Clear data table", self.tool_window.ui_area) clear.triggered.connect(self.clear_table) export.addAction(clear) copy = QAction("&Copy CSV to clipboard", self.tool_window.ui_area) copy.triggered.connect(self.copy_csv) shortcut = QKeySequence(Qt.CTRL + Qt.Key_C) copy.setShortcut(shortcut) export.addAction(copy) save = QAction("&Save CSV...", self.tool_window.ui_area) save.triggered.connect(self.save_csv) #this shortcut interferes with main window's save shortcut #I've tried different shortcut contexts to no avail #thanks Qt... #shortcut = QKeySequence(Qt.CTRL + Qt.Key_S) #save.setShortcut(shortcut) #save.setShortcutContext(Qt.WidgetShortcut) export.addAction(save) delimiter = export.addMenu("Delimiter") comma = QAction("comma", self.tool_window.ui_area, checkable=True) comma.setChecked(self.settings.delimiter == "comma") comma.triggered.connect(lambda *args, delim="comma": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(comma) tab = QAction("tab", self.tool_window.ui_area, checkable=True) tab.setChecked(self.settings.delimiter == "tab") tab.triggered.connect(lambda *args, delim="tab": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(tab) space = QAction("space", self.tool_window.ui_area, checkable=True) space.setChecked(self.settings.delimiter == "space") space.triggered.connect(lambda *args, delim="space": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(space) semicolon = QAction("semicolon", self.tool_window.ui_area, checkable=True) semicolon.setChecked(self.settings.delimiter == "semicolon") semicolon.triggered.connect(lambda *args, delim="semicolon": self. settings.__setattr__("delimiter", delim)) delimiter.addAction(semicolon) add_header = QAction("&Include CSV header", self.tool_window.ui_area, checkable=True) add_header.setChecked(self.settings.include_header) add_header.triggered.connect(self.header_check) export.addAction(add_header) comma.triggered.connect( lambda *args, action=tab: action.setChecked(False)) comma.triggered.connect( lambda *args, action=space: action.setChecked(False)) comma.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) tab.triggered.connect( lambda *args, action=comma: action.setChecked(False)) tab.triggered.connect( lambda *args, action=space: action.setChecked(False)) tab.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) space.triggered.connect( lambda *args, action=comma: action.setChecked(False)) space.triggered.connect( lambda *args, action=tab: action.setChecked(False)) space.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=comma: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=tab: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=space: action.setChecked(False)) menu.setNativeMenuBar(False) self._menu = menu layout.setMenuBar(menu) menu.setVisible(True) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
def __init__(self, parent=None, **kwds): super(BoolInputWidget, self).__init__(parent=parent, **kwds) self.cb = QCheckBox(self) self.setWidget(self.cb) self.cb.stateChanged.connect( lambda val: self.dataSetCallback(bool(val)))
class TestRunner(ToolInstance): def __init__(self, session, name): super().__init__(session, name) self.tool_window = MainToolWindow(self) self._build_ui() def _build_ui(self): """ ui should have: * table with a list of available tests and show results after they are done * way to filter tests * button to run tests """ layout = QFormLayout() # table to list test classes and the results self.table = QTableWidget() self.table.setColumnCount(2) self.table.setHorizontalHeaderLabels(["test", "result"]) self.table.horizontalHeader().setSectionResizeMode( 0, self.table.horizontalHeader().Interactive) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.horizontalHeader().setSectionResizeMode( 1, self.table.horizontalHeader().Stretch) self.table.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.table.setSelectionBehavior(self.table.SelectRows) layout.addRow(self.table) self.filter = QLineEdit() self.filter.setPlaceholderText("filter test names") self.filter.setClearButtonEnabled(True) self.filter.textChanged.connect(self.apply_filter) layout.addRow(self.filter) self.profile = QCheckBox() self.profile.setToolTip("profile functions called during testing " "using cProfile") layout.addRow("profile calls:", self.profile) self.run_button = QPushButton("run tests") self.run_button.clicked.connect(self.run_tests) self.run_button.setToolTip( "if no tests are selected on the table, run all tests\n" + "otherwise, run selected tests") layout.addRow(self.run_button) self.fill_table() self.table.resizeColumnToContents(0) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None) def apply_filter(self, text=None): """filter table to only show tests matching text""" if text is None: text = self.filter.text() if text: text = text.replace("(", "\(") text = text.replace(")", "\)") m = QRegularExpression(text) m.setPatternOptions(QRegularExpression.CaseInsensitiveOption) if m.isValid(): m.optimize() filter = lambda row_num: m.match( self.table.item(row_num, 0).text()).hasMatch() else: return else: filter = lambda row: True for i in range(0, self.table.rowCount()): self.table.setRowHidden(i, not filter(i)) def fill_table(self): """adds test names to the table""" mgr = self.session.test_manager for name in mgr.tests.keys(): row = self.table.rowCount() self.table.insertRow(row) test_name = QTableWidgetItem() test_name.setData(Qt.DisplayRole, name) self.table.setItem(row, 0, test_name) def run_tests(self): """run the tests selected on the table and show the results""" from TestManager.commands.test import test test_list = [] use_selected = True for row in self.table.selectionModel().selectedRows(): if self.table.isRowHidden(row.row()): continue test_name = self.table.item(row.row(), 0).text() test_list.append(test_name) if not test_list: use_selected = False for i in range(0, self.table.rowCount()): if self.table.isRowHidden(i): continue test_name = self.table.item(i, 0).text() test_list.append(test_name) if not test_list: test_list = ["all"] results, stats = test(self.session, test_list, profile=self.profile.checkState() == Qt.Checked) cell_widgets = [] for name in results: widget = QWidget() widget_layout = QHBoxLayout(widget) widget_layout.setContentsMargins(0, 0, 0, 0) success_button = get_button("success") fail_button = get_button("fail") skip_button = get_button("skip") error_button = get_button("error") expected_fail_button = get_button("expected fail") unexpected_success_button = get_button("unexpected success") success_count = 0 fail_count = 0 error_count = 0 unexpected_success_count = 0 expected_fail_count = 0 skip_count = 0 success_tooltip = "Successes:\n" fail_tooltip = "Failed tests:\n" error_tooltip = "Errors during test:\n" unexpected_success_tooltip = "Unexpected successes:\n" expected_fail_tooltip = "Expected fails:\n" skip_tooltip = "Skipped tests:\n" for case in results[name]: result, msg = results[name][case] if result == "success": success_count += 1 success_tooltip += "%s.%s: %s\n" % ( case.__class__.__qualname__, case._testMethodName, msg) elif result == "fail": fail_count += 1 fail_tooltip += "%s.%s failed: %s\n" % ( case.__class__.__qualname__, case._testMethodName, msg) elif result == "error": error_count += 1 error_tooltip += "error during %s.%s: %s\n" % ( case.__class__.__qualname__, case._testMethodName, msg) elif result == "expected_failure": expected_fail_count += 1 expected_fail_tooltip += "intended failure during %s.%s: %s\n" % ( case.__class__.__qualname__, case._testMethodName, msg) elif result == "skip": skip_count += 1 skip_tooltip += "%s.%s\n" % (case.__class__.__qualname__, case._testMethodName) elif result == "unexpected_success": unexpected_success_count += 1 unexpected_success_tooltip += "%s.%s should not have worked, but did\n" % ( case.__class__.__qualname__, case._testMethodName) success_tooltip = success_tooltip.strip() fail_tooltip = fail_tooltip.strip() error_tooltip = error_tooltip.strip() expected_fail_tooltip = expected_fail_tooltip.strip() skip_tooltip = skip_tooltip.strip() unexpected_success_tooltip = unexpected_success_tooltip.strip() icon_count = 0 if success_count: success_button.setText("%i" % success_count) success_button.setToolTip(success_tooltip) success_button.clicked.connect( lambda *args, t_name=name, res=success_tooltip: self. tool_window.create_child_window( "successes for %s" % t_name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, success_button, 1) icon_count += 1 if fail_count: fail_button.setText("%i" % fail_count) fail_button.setToolTip(fail_tooltip) fail_button.clicked.connect( lambda *args, res=fail_tooltip: self.tool_window. create_child_window( "failures for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, fail_button, 1) icon_count += 1 if error_count: error_button.setText("%i" % error_count) error_button.setToolTip(error_tooltip) error_button.clicked.connect( lambda *args, res=error_tooltip: self.tool_window. create_child_window( "errors for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, error_button, 1) icon_count += 1 if unexpected_success_count: unexpected_success_button.setText("%i" % unexpected_success_count) unexpected_success_button.setToolTip( unexpected_success_tooltip) unexpected_success_button.clicked.connect( lambda *args, res=unexpected_success_tooltip: self. tool_window.create_child_window( "unexpected successes for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, unexpected_success_button, 1) icon_count += 1 if expected_fail_count: expected_fail_button.setText("%i" % expected_fail_count) expected_fail_button.setToolTip(expected_fail_tooltip) expected_fail_button.clicked.connect( lambda *args, res=expected_fail_tooltip: self.tool_window. create_child_window( "expected failures for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, expected_fail_button, 1) icon_count += 1 if skip_count: skip_button.setText("%i" % skip_count) skip_button.setToolTip(skip_tooltip) skip_button.clicked.connect( lambda *args, res=skip_tooltip: self.tool_window. create_child_window( "skipped tests for %s" % name, text=res, window_class=ResultsWindow, )) widget_layout.insertWidget(icon_count, skip_button, 1) cell_widgets.append(widget) widget_count = 0 if use_selected: for row in self.table.selectionModel().selectedRows(): if self.table.isRowHidden(row.row()): continue self.table.setCellWidget(row.row(), 1, cell_widgets[widget_count]) self.table.resizeRowToContents(row.row()) widget_count += 1 else: for i in range(0, self.table.rowCount()): if self.table.isRowHidden(i): continue self.table.setCellWidget(i, 1, cell_widgets[widget_count]) # self.table.resizeRowToContents(i) widget_count += 1 if self.profile.checkState() == Qt.Checked: self.tool_window.create_child_window("stats", text=stats, window_class=ResultsWindow)
def _build_ui(self): layout = QGridLayout() tab_widget = QTabWidget() layout.addWidget(tab_widget) structure_widget = QWidget() structure_layout = QGridLayout(structure_widget) tab_widget.addTab(structure_widget, "structure") self.structure_source = QComboBox() self.structure_source.addItems( ["AARON template", "file", "coordination complexes", "SMILES"]) structure_layout.addWidget(QLabel("structure source:"), 0, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) structure_layout.addWidget( self.structure_source, 0, 1, 1, 1, ) open_button = QPushButton("open") open_button.clicked.connect(self.open_template) structure_layout.addWidget( open_button, 0, 2, 1, 1, ) # SMILES options self.smiles_options = QGroupBox("SMILES options") smiles_layout = QFormLayout(self.smiles_options) structure_layout.addWidget(self.smiles_options, 1, 0, 1, 3, Qt.AlignTop | Qt.AlignHCenter) self.smiles_line = QLineEdit() smiles_layout.addRow("SMILES string:", self.smiles_line) self.smiles_options.setVisible( self.structure_source.currentText() == "SMILES") self.structure_source.currentTextChanged.connect( lambda text: self.smiles_options.setVisible(text == "SMILES")) # coordination complexes options self.coord_comp_options = QGroupBox("Coordination Complex Options") coord_layout = QFormLayout(self.coord_comp_options) structure_layout.addWidget(self.coord_comp_options, 1, 0, 1, 3, Qt.AlignTop | Qt.AlignHCenter) create_coord_items(self, coord_layout, allow_minimization=False, default_ele="Pd") self.coord_comp_options.setVisible( self.structure_source.currentText() == "coordination complexes") self.structure_source.currentTextChanged.connect( lambda text: self.coord_comp_options.setVisible( text == "coordination complexes")) # aaron template options self.aaron_options = QGroupBox("AARON template structures") aaron_layout = QFormLayout(self.aaron_options) structure_layout.addWidget(self.aaron_options, 1, 0, 1, 3, Qt.AlignTop | Qt.AlignHCenter) self.aaron_options.setVisible( self.structure_source.currentText() == "AARON template") self.structure_source.currentTextChanged.connect( lambda text: self.aaron_options.setVisible(text == "AARON template" )) # file options self.file_options = QGroupBox("single file options") file_layout = QFormLayout(self.file_options) structure_layout.addWidget(self.file_options, 1, 0, 1, 3, Qt.AlignTop | Qt.AlignHCenter) file_widget = QWidget() file_browse_layout = QGridLayout(file_widget) self.template_file = QLineEdit() file_browse_layout.addWidget(self.template_file, 0, 0, 1, 1, Qt.AlignCenter) browse_button = QPushButton("browse...") # browse_button.clicked.connect(self.browse_file_template) file_browse_layout.addWidget(browse_button, 0, 1, 1, 1, Qt.AlignCenter) file_browse_layout.setColumnStretch(0, 1) file_browse_layout.setColumnStretch(1, 0) margins = file_browse_layout.contentsMargins() file_browse_layout.setContentsMargins(margins.left(), 0, margins.right(), 0) file_layout.addRow("file:", file_widget) self.file_options.setVisible( self.structure_source.currentText() == "file") self.structure_source.currentTextChanged.connect( lambda text: self.file_options.setVisible(text == "file")) self.optimize_template = QCheckBox() self.optimize_template.setCheckState(Qt.Checked) structure_layout.addWidget( QLabel("optimize template:"), 2, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter, ) structure_layout.addWidget( self.optimize_template, 2, 1, 1, 2, Qt.AlignLeft | Qt.AlignVCenter, ) structure_layout.setColumnStretch(0, 0) structure_layout.setColumnStretch(1, 1) structure_layout.setColumnStretch(2, 0) # structure changes changes_widget = QWidget() changes_layout = QGridLayout(changes_widget) tab_widget.addTab(changes_widget, "changes") # HPC settings hpc_widget = QWidget() hpc_layout = QGridLayout(hpc_widget) tab_widget.addTab(hpc_widget, "HPC") # theory settings theory_widget = QWidget() theory_layout = QGridLayout(theory_widget) tab_widget.addTab(theory_widget, "theory") # results results_widget = QWidget() results_layout = QGridLayout(results_widget) tab_widget.addTab(results_widget, "results") self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
def _build_ui(self): layout = QGridLayout() layout.addWidget(QLabel("center of rotation:"), 0, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) self.cor_button = QComboBox() self.cor_button.addItems( ["automatic", "select atoms", "view's center of rotation"]) layout.addWidget(self.cor_button, 0, 1, 1, 1, Qt.AlignTop) self.set_cor_selection = QPushButton("set selection") self.cor_button.currentTextChanged.connect( lambda t, widget=self.set_cor_selection: widget.setEnabled( t == "select atoms")) self.set_cor_selection.clicked.connect(self.manual_cor) layout.addWidget(self.set_cor_selection, 0, 2, 1, 1, Qt.AlignTop) self.set_cor_selection.setEnabled(False) layout.addWidget(QLabel("rotation vector:"), 1, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) self.vector_option = QComboBox() self.vector_option.addItems([ "axis", "view axis", "bond", "perpendicular to plane", "centroid of atoms", "custom" ]) layout.addWidget(self.vector_option, 1, 1, 1, 1, Qt.AlignVCenter) vector = QWidget() vector.setToolTip("vector will be normalized before rotating") vector_layout = QHBoxLayout(vector) vector_layout.setContentsMargins(0, 0, 0, 0) self.vector_x = QDoubleSpinBox() self.vector_y = QDoubleSpinBox() self.vector_z = QDoubleSpinBox() self.vector_z.setValue(1.0) for c, t in zip([self.vector_x, self.vector_y, self.vector_z], [" x", " y", " z"]): c.setSingleStep(0.01) c.setRange(-100, 100) # c.setSuffix(t) c.valueChanged.connect(self.show_rot_vec) vector_layout.addWidget(c) layout.addWidget(vector, 1, 2, 1, 1, Qt.AlignTop) vector.setVisible(self.vector_option.currentText() == "custom") self.vector_option.currentTextChanged.connect( lambda text, widget=vector: widget.setVisible(text == "custom")) self.view_axis = QComboBox() self.view_axis.addItems(["z", "y", "x"]) layout.addWidget(self.view_axis, 1, 2, 1, 1, Qt.AlignTop) self.view_axis.setVisible( self.vector_option.currentText() == "view axis") self.vector_option.currentTextChanged.connect( lambda text, widget=self.view_axis: widget.setVisible(text == "view axis")) self.axis = QComboBox() self.axis.addItems(["z", "y", "x"]) layout.addWidget(self.axis, 1, 2, 1, 1, Qt.AlignTop) self.axis.setVisible(self.vector_option.currentText() == "axis") self.vector_option.currentTextChanged.connect( lambda text, widget=self.axis: widget.setVisible(text == "axis")) self.bond_button = QPushButton("set selected bond") self.bond_button.clicked.connect(self.set_bonds) layout.addWidget(self.bond_button, 1, 2, 1, 1, Qt.AlignTop) self.bond_button.setVisible(self.vector_option.currentText() == "bond") self.vector_option.currentTextChanged.connect( lambda text, widget=self.bond_button: widget.setVisible(text == "bond")) self.perp_button = QPushButton("set selected atoms") self.perp_button.clicked.connect(self.set_perpendicular) layout.addWidget(self.perp_button, 1, 2, 1, 1, Qt.AlignTop) self.perp_button.setVisible( self.vector_option.currentText() == "perpendicular to plane") self.vector_option.currentTextChanged.connect( lambda text, widget=self.perp_button: widget.setVisible( text == "perpendicular to plane")) self.group_button = QPushButton("set selected atoms") self.group_button.clicked.connect(self.set_group) layout.addWidget(self.group_button, 1, 2, 1, 1, Qt.AlignTop) self.group_button.setVisible( self.vector_option.currentText() == "centroid of atoms") self.vector_option.currentTextChanged.connect( lambda text, widget=self.group_button: widget.setVisible( text == "centroid of atoms")) layout.addWidget(QLabel("angle:"), 2, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) self.angle = QDoubleSpinBox() self.angle.setRange(-360, 360) self.angle.setSingleStep(5) self.angle.setSuffix("°") layout.addWidget(self.angle, 2, 1, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) layout.addWidget(QLabel("preview rotation axis:"), 3, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) self.display_rot_vec = QCheckBox() self.display_rot_vec.setCheckState(Qt.Checked) self.display_rot_vec.stateChanged.connect(self.show_rot_vec) layout.addWidget(self.display_rot_vec, 3, 1, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) rotate_button = QPushButton("rotate selected atoms") rotate_button.clicked.connect(self.do_rotate) layout.addWidget(rotate_button, 4, 0, 1, 3, Qt.AlignTop) self.rotate_button = rotate_button self.status_bar = QStatusBar() self.status_bar.setSizeGripEnabled(False) layout.addWidget(self.status_bar, 5, 0, 1, 3, Qt.AlignTop) self.vector_option.currentTextChanged.connect(self.show_auto_status) self.cor_button.currentIndexChanged.connect( lambda *args: self.show_auto_status("select atoms")) self.cor_button.currentIndexChanged.connect(self.show_rot_vec) self.set_cor_selection.clicked.connect(self.show_rot_vec) self.vector_option.currentIndexChanged.connect(self.show_rot_vec) self.axis.currentIndexChanged.connect(self.show_rot_vec) self.view_axis.currentIndexChanged.connect(self.show_rot_vec) self.bond_button.clicked.connect(self.show_rot_vec) self.perp_button.clicked.connect(self.show_rot_vec) self.group_button.clicked.connect(self.show_rot_vec) layout.setRowStretch(0, 0) layout.setRowStretch(1, 0) layout.setRowStretch(2, 0) layout.setRowStretch(3, 0) layout.setRowStretch(4, 0) layout.setRowStretch(5, 1) layout.setColumnStretch(0, 0) layout.setColumnStretch(1, 1) layout.setColumnStretch(2, 1) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
def addAttributeSlot(self): """ Adds a new attribute (column) to the table """ dialog = QDialog(self) dialog.setModal(True) dialog.setWindowTitle('Add Attribute') layout = QVBoxLayout() dialog.setLayout(layout) form = QFormLayout() nameBox = QLineEdit() typeCombo = QComboBox() for attrType in _attrTypes: typeName = partio.TypeName(attrType) typeCombo.addItem(typeName) typeCombo.setCurrentIndex(partio.FLOAT) countBox = QLineEdit() countBox.setValidator(QIntValidator()) countBox.setText('1') fixedCheckbox = QCheckBox() valueBox = QLineEdit() valueBox.setText('0') form.addRow('Name:', nameBox) form.addRow('Type:', typeCombo) form.addRow('Count:', countBox) form.addRow('Fixed:', fixedCheckbox) form.addRow('Default Value:', valueBox) layout.addLayout(form) buttons = QHBoxLayout() layout.addLayout(buttons) add = QPushButton('Add') add.clicked.connect(dialog.accept) buttons.addWidget(add) cancel = QPushButton('Cancel') cancel.clicked.connect(dialog.reject) buttons.addWidget(cancel) if not dialog.exec_(): return name = str(nameBox.text()) if not name: print 'Please supply a name for the new attribute' # TODO: prompt return attrType = typeCombo.currentIndex() count = int(countBox.text()) fixed = fixedCheckbox.isChecked() values = list(str(valueBox.text()).strip().split()) for i in range(count): if i < len(values): value = values[i] else: value = values[-1] if attrType == partio.INT or attrType == partio.INDEXEDSTR: values[i] = int(value) elif attrType == partio.FLOAT or attrType == partio.VECTOR: values[i] = float(value) # pylint:disable=R0204 else: values[i] = 0.0 # pylint:disable=R0204 value = tuple(values) self.data.addAttribute(name, attrType, count, fixed, value)
class _SubstituentSelector(ToolInstance): def __init__(self, session, name): super().__init__(session, name) self.tool_window = MainToolWindow(self) self._build_ui() def _build_ui(self): layout = QFormLayout() self.substituent_table = SubstituentTable(singleSelect=True) layout.addRow(self.substituent_table) self.new_residue = QCheckBox() self.new_residue.setCheckState( Qt.Checked if SubstituteMouseMode.newRes else Qt.Unchecked) layout.addRow("new residue:", self.new_residue) self.res_name = QLineEdit() self.res_name.setPlaceholderText("leave blank to keep current") layout.addRow("set residue name:", self.res_name) self.distance_names = QCheckBox() self.distance_names.setCheckState( Qt.Checked if SubstituteMouseMode.useRemoteness else Qt.Unchecked) layout.addRow("distance atom names:", self.distance_names) self.keep_open = QCheckBox() layout.addRow("keep list open:", self.keep_open) do_it = QPushButton("set substituent") do_it.clicked.connect(self.set_sub) layout.addRow(do_it) self.keep_open.stateChanged.connect( lambda state: do_it.setVisible(state != Qt.Checked)) self.keep_open.stateChanged.connect(self.sub_changed) self.substituent_table.table.itemSelectionChanged.connect( self.sub_changed) self.new_residue.stateChanged.connect(self.sub_changed) self.res_name.textChanged.connect(self.sub_changed) self.distance_names.stateChanged.connect(self.sub_changed) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None) def sub_changed(self): if self.keep_open.checkState() == Qt.Checked: self.set_sub() def set_sub(self): sub_names = [] for row in self.substituent_table.table.selectionModel().selectedRows( ): if self.substituent_table.table.isRowHidden(row.row()): continue sub_names.append(row.data()) if sub_names: SubstituteMouseMode.substituent = sub_names[0] SubstituteMouseMode.newRes = self.new_residue.checkState( ) == Qt.Checked SubstituteMouseMode.useRemoteness = self.distance_names.checkState( ) == Qt.Checked sub_name = self.res_name.text() SubstituteMouseMode.resName = sub_name if self.keep_open.checkState() != Qt.Checked: self.delete() def close(self): sub_names = [] for row in self.substituent_table.table.selectionModel().selectedRows( ): if self.substituent_table.table.isRowHidden(row.row()): continue sub_names.append(row.data()) if sub_names: SubstituteMouseMode.substituent = sub_names[0] SubstituteMouseMode.newRes = self.new_residue.checkState( ) == Qt.Checked SubstituteMouseMode.useRemoteness = self.distance_names.checkState( ) == Qt.Checked sub_name = self.res_name.text() SubstituteMouseMode.resName = sub_name super().close()
def _build_ui(self): layout = QFormLayout() initial_elements = [] if ChangeElementMouseMode.element: initial_elements = [ChangeElementMouseMode.element] else: initial_elements = ["C"] self.periodic_table = PeriodicTable(select_multiple=False, initial_elements=initial_elements) self.periodic_table.elementSelectionChanged.connect( self.element_changed) layout.addRow(self.periodic_table) self.vsepr = QComboBox() self.vsepr.addItems([ "do not change", # 0 "linear (1 bond)", # 1 "linear (2 bonds)", # 2 "trigonal planar (2 bonds)", # 3 "tetrahedral (2 bonds)", # 4 "trigonal planar", # 5 "tetrahedral (3 bonds)", # 6 "T-shaped", # 7 "trigonal pyramidal", # 8 "tetrahedral", # 9 "sawhorse", #10 "seesaw", #11 "square planar", #12 "trigonal bipyramidal", #13 "square pyramidal", #14 "pentagonal", #15 "octahedral", #16 "hexagonal", #17 "trigonal prismatic", #18 "pentagonal pyramidal", #19 "capped octahedral", #20 "capped trigonal prismatic", #21 "heptagonal", #22 "hexagonal pyramidal", #23 "pentagonal bipyramidal", #24 "biaugmented trigonal prismatic", #25 "cubic", #26 "elongated trigonal bipyramidal", #27 "hexagonal bipyramidal", #28 "heptagonal pyramidal", #29 "octagonal", #30 "square antiprismatic", #31 "trigonal dodecahedral", #32 "capped cube", #33 "capped square antiprismatic", #34 "enneagonal", #35 "heptagonal bipyramidal", #36 "hula-hoop", #37 "triangular cupola", #38 "tridiminished icosahedral", #39 "muffin", #40 "octagonal pyramidal", #41 "tricapped trigonal prismatic", #42 ]) self.vsepr.setCurrentIndex(9) self.vsepr.insertSeparator(33) self.vsepr.insertSeparator(25) self.vsepr.insertSeparator(20) self.vsepr.insertSeparator(16) self.vsepr.insertSeparator(13) self.vsepr.insertSeparator(8) self.vsepr.insertSeparator(5) self.vsepr.insertSeparator(2) self.vsepr.insertSeparator(1) self.vsepr.insertSeparator(0) self.vsepr.currentTextChanged.connect(self.element_changed) layout.addRow("shape:", self.vsepr) self.keep_open = QCheckBox() layout.addRow("keep table open:", self.keep_open) if ChangeElementMouseMode.vsepr: ndx = self.vsepr.findText(ChangeElementMouseMode.vsepr, Qt.MatchExactly) if ndx != -1: self.vsepr.setCurrentIndex(ndx) do_it = QPushButton("set element and shape") do_it.clicked.connect(self.set_selected) layout.addRow(do_it) self.keep_open.stateChanged.connect( lambda state: do_it.setVisible(state != Qt.Checked)) self.keep_open.stateChanged.connect(self.element_changed) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
class ConeAngle(ToolInstance): help = "https://github.com/QChASM/SEQCROW/wiki/Cone-Angle-Tool" def __init__(self, session, name): super().__init__(session, name) from chimerax.ui import MainToolWindow self.tool_window = MainToolWindow(self) self.settings = _ConeAngleSettings(self.session, name) self.ligands = dict() self._build_ui() def _build_ui(self): layout = QFormLayout() self.cone_option = QComboBox() self.cone_option.addItems(["Tolman (Unsymmetrical)", "Exact"]) ndx = self.cone_option.findText(self.settings.cone_option, Qt.MatchExactly) self.cone_option.setCurrentIndex(ndx) layout.addRow("method:", self.cone_option) self.radii_option = QComboBox() self.radii_option.addItems(["Bondi", "UMN"]) ndx = self.radii_option.findText(self.settings.radii, Qt.MatchExactly) self.radii_option.setCurrentIndex(ndx) layout.addRow("radii:", self.radii_option) self.display_cone = QCheckBox() self.display_cone.setChecked(self.settings.display_cone) layout.addRow("show cone:", self.display_cone) self.display_radii = QCheckBox() self.display_radii.setChecked(self.settings.display_radii) layout.addRow("show radii:", self.display_radii) set_ligand_button = QPushButton("set ligand to current selection") set_ligand_button.clicked.connect(self.set_ligand) layout.addRow(set_ligand_button) self.set_ligand_button = set_ligand_button calc_cone_button = QPushButton( "calculate cone angle for ligand on selected center") calc_cone_button.clicked.connect(self.calc_cone) layout.addRow(calc_cone_button) self.calc_cone_button = calc_cone_button remove_cone_button = QPushButton("remove cone visualizations") remove_cone_button.clicked.connect(self.del_cone) layout.addRow(remove_cone_button) self.remove_cone_button = remove_cone_button self.table = QTableWidget() self.table.setColumnCount(3) self.table.setHorizontalHeaderLabels([ 'model', 'center', 'cone angle (°)', ]) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.resizeColumnToContents(0) self.table.resizeColumnToContents(1) self.table.resizeColumnToContents(2) self.table.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) layout.addRow(self.table) menu = QMenuBar() export = menu.addMenu("&Export") clear = QAction("Clear data table", self.tool_window.ui_area) clear.triggered.connect(self.clear_table) export.addAction(clear) copy = QAction("&Copy CSV to clipboard", self.tool_window.ui_area) copy.triggered.connect(self.copy_csv) shortcut = QKeySequence(Qt.CTRL + Qt.Key_C) copy.setShortcut(shortcut) export.addAction(copy) save = QAction("&Save CSV...", self.tool_window.ui_area) save.triggered.connect(self.save_csv) #this shortcut interferes with main window's save shortcut #I've tried different shortcut contexts to no avail #thanks Qt... #shortcut = QKeySequence(Qt.CTRL + Qt.Key_S) #save.setShortcut(shortcut) #save.setShortcutContext(Qt.WidgetShortcut) export.addAction(save) delimiter = export.addMenu("Delimiter") comma = QAction("comma", self.tool_window.ui_area, checkable=True) comma.setChecked(self.settings.delimiter == "comma") comma.triggered.connect(lambda *args, delim="comma": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(comma) tab = QAction("tab", self.tool_window.ui_area, checkable=True) tab.setChecked(self.settings.delimiter == "tab") tab.triggered.connect(lambda *args, delim="tab": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(tab) space = QAction("space", self.tool_window.ui_area, checkable=True) space.setChecked(self.settings.delimiter == "space") space.triggered.connect(lambda *args, delim="space": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(space) semicolon = QAction("semicolon", self.tool_window.ui_area, checkable=True) semicolon.setChecked(self.settings.delimiter == "semicolon") semicolon.triggered.connect(lambda *args, delim="semicolon": self. settings.__setattr__("delimiter", delim)) delimiter.addAction(semicolon) add_header = QAction("&Include CSV header", self.tool_window.ui_area, checkable=True) add_header.setChecked(self.settings.include_header) add_header.triggered.connect(self.header_check) export.addAction(add_header) comma.triggered.connect( lambda *args, action=tab: action.setChecked(False)) comma.triggered.connect( lambda *args, action=space: action.setChecked(False)) comma.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) tab.triggered.connect( lambda *args, action=comma: action.setChecked(False)) tab.triggered.connect( lambda *args, action=space: action.setChecked(False)) tab.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) space.triggered.connect( lambda *args, action=comma: action.setChecked(False)) space.triggered.connect( lambda *args, action=tab: action.setChecked(False)) space.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=comma: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=tab: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=space: action.setChecked(False)) menu.setNativeMenuBar(False) self._menu = menu layout.setMenuBar(menu) menu.setVisible(True) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None) def clear_table(self): are_you_sure = QMessageBox.question( None, "Clear table?", "Are you sure you want to clear the data table?", ) if are_you_sure != QMessageBox.Yes: return self.table.setRowCount(0) def header_check(self, state): """user has [un]checked the 'include header' option on the menu""" if state: self.settings.include_header = True else: self.settings.include_header = False def get_csv(self): if self.settings.delimiter == "comma": delim = "," elif self.settings.delimiter == "space": delim = " " elif self.settings.delimiter == "tab": delim = "\t" elif self.settings.delimiter == "semicolon": delim = ";" if self.settings.include_header: s = delim.join(["model", "center_atom", "cone_angle"]) s += "\n" else: s = "" for i in range(0, self.table.rowCount()): s += delim.join([ item.data(Qt.DisplayRole) for item in [self.table.item(i, j) for j in range(0, 3)] ]) s += "\n" return s def copy_csv(self): app = QApplication.instance() clipboard = app.clipboard() csv = self.get_csv() clipboard.setText(csv) self.session.logger.status("copied to clipboard") def save_csv(self): """save data on current tab to CSV file""" filename, _ = QFileDialog.getSaveFileName(filter="CSV Files (*.csv)") if filename: s = self.get_csv() with open(filename, 'w') as f: f.write(s.strip()) self.session.logger.status("saved to %s" % filename) def del_cone(self): for model in self.session.models.list(type=Generic3DModel): if model.name.startswith("Cone angle"): model.delete() def set_ligand(self, *args): self.ligands = {} for atom in selected_atoms(self.session): if atom.structure not in self.ligands: self.ligands[atom.structure] = [] self.ligands[atom.structure].append(atom) self.session.logger.status("set ligand to current selection") def calc_cone(self, *args): self.settings.cone_option = self.cone_option.currentText() self.settings.radii = self.radii_option.currentText() self.settings.display_radii = self.display_radii.checkState( ) == Qt.Checked self.settings.display_cone = self.display_cone.checkState( ) == Qt.Checked if self.cone_option.currentText() == "Tolman (Unsymmetrical)": method = "tolman" else: method = self.cone_option.currentText() radii = self.radii_option.currentText() return_cones = self.display_cone.checkState() == Qt.Checked display_radii = self.display_radii.checkState() == Qt.Checked # self.table.setRowCount(0) for center_atom in selected_atoms(self.session): rescol = ResidueCollection(center_atom.structure) at_center = rescol.find_exact(AtomSpec(center_atom.atomspec))[0] if center_atom.structure in self.ligands: comp = Component( rescol.find([ AtomSpec(atom.atomspec) for atom in self.ligands[center_atom.structure] ]), to_center=rescol.find_exact(AtomSpec( center_atom.atomspec)), key_atoms=rescol.find(BondedTo(at_center)), ) else: comp = Component( rescol.find(NotAny(at_center)), to_center=rescol.find_exact(AtomSpec( center_atom.atomspec)), key_atoms=rescol.find(BondedTo(at_center)), ) cone_angle = comp.cone_angle( center=rescol.find(AtomSpec(center_atom.atomspec)), method=method, radii=radii, return_cones=return_cones, ) if return_cones: cone_angle, cones = cone_angle s = ".transparency 0.5\n" for cone in cones: apex, base, radius = cone s += ".cone %6.3f %6.3f %6.3f %6.3f %6.3f %6.3f %.3f open\n" % ( *apex, *base, radius) stream = BytesIO(bytes(s, "utf-8")) bild_obj, status = read_bild(self.session, stream, "Cone angle %s" % center_atom) self.session.models.add(bild_obj, parent=center_atom.structure) if display_radii: s = ".note radii\n" s += ".transparency 75\n" color = None for atom in comp.atoms: chix_atom = atom.chix_atom if radii.lower() == "umn": r = VDW_RADII[chix_atom.element.name] elif radii.lower() == "bondi": r = BONDI_RADII[chix_atom.element.name] if color is None or chix_atom.color != color: color = chix_atom.color rgb = [x / 255. for x in chix_atom.color] rgb.pop(-1) s += ".color %f %f %f\n" % tuple(rgb) s += ".sphere %f %f %f %f\n" % (*chix_atom.coord, r) stream = BytesIO(bytes(s, "utf-8")) bild_obj, status = read_bild(self.session, stream, "Cone angle radii") self.session.models.add(bild_obj, parent=center_atom.structure) row = self.table.rowCount() self.table.insertRow(row) name = QTableWidgetItem() name.setData(Qt.DisplayRole, center_atom.structure.name) self.table.setItem(row, 0, name) center = QTableWidgetItem() center.setData(Qt.DisplayRole, center_atom.atomspec) self.table.setItem(row, 1, center) ca = QTableWidgetItem() ca.setData(Qt.DisplayRole, "%.2f" % cone_angle) self.table.setItem(row, 2, ca) self.table.resizeColumnToContents(0) self.table.resizeColumnToContents(1) self.table.resizeColumnToContents(2)
class PrecisionRotate(ToolInstance): help = "https://github.com/QChASM/SEQCROW/wiki/Rotate-Tool" SESSION_ENDURING = False SESSION_SAVE = False def __init__(self, session, name): super().__init__(session, name) self.tool_window = MainToolWindow(self) self.settings = _PrecisionRotateSettings(session, name) self.bonds = {} self.bond_centers = {} self.groups = {} self.perpendiculars = {} self.perp_centers = {} self.manual_center = {} self._build_ui() self._show_rot_vec = self.session.triggers.add_handler( SELECTION_CHANGED, self.show_rot_vec) global_triggers = get_triggers() self._changes = global_triggers.add_handler("changes done", self.show_rot_vec) self.show_rot_vec() def _build_ui(self): layout = QGridLayout() layout.addWidget(QLabel("center of rotation:"), 0, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) self.cor_button = QComboBox() self.cor_button.addItems( ["automatic", "select atoms", "view's center of rotation"]) layout.addWidget(self.cor_button, 0, 1, 1, 1, Qt.AlignTop) self.set_cor_selection = QPushButton("set selection") self.cor_button.currentTextChanged.connect( lambda t, widget=self.set_cor_selection: widget.setEnabled( t == "select atoms")) self.set_cor_selection.clicked.connect(self.manual_cor) layout.addWidget(self.set_cor_selection, 0, 2, 1, 1, Qt.AlignTop) self.set_cor_selection.setEnabled(False) layout.addWidget(QLabel("rotation vector:"), 1, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) self.vector_option = QComboBox() self.vector_option.addItems([ "axis", "view axis", "bond", "perpendicular to plane", "centroid of atoms", "custom" ]) layout.addWidget(self.vector_option, 1, 1, 1, 1, Qt.AlignVCenter) vector = QWidget() vector.setToolTip("vector will be normalized before rotating") vector_layout = QHBoxLayout(vector) vector_layout.setContentsMargins(0, 0, 0, 0) self.vector_x = QDoubleSpinBox() self.vector_y = QDoubleSpinBox() self.vector_z = QDoubleSpinBox() self.vector_z.setValue(1.0) for c, t in zip([self.vector_x, self.vector_y, self.vector_z], [" x", " y", " z"]): c.setSingleStep(0.01) c.setRange(-100, 100) # c.setSuffix(t) c.valueChanged.connect(self.show_rot_vec) vector_layout.addWidget(c) layout.addWidget(vector, 1, 2, 1, 1, Qt.AlignTop) vector.setVisible(self.vector_option.currentText() == "custom") self.vector_option.currentTextChanged.connect( lambda text, widget=vector: widget.setVisible(text == "custom")) self.view_axis = QComboBox() self.view_axis.addItems(["z", "y", "x"]) layout.addWidget(self.view_axis, 1, 2, 1, 1, Qt.AlignTop) self.view_axis.setVisible( self.vector_option.currentText() == "view axis") self.vector_option.currentTextChanged.connect( lambda text, widget=self.view_axis: widget.setVisible(text == "view axis")) self.axis = QComboBox() self.axis.addItems(["z", "y", "x"]) layout.addWidget(self.axis, 1, 2, 1, 1, Qt.AlignTop) self.axis.setVisible(self.vector_option.currentText() == "axis") self.vector_option.currentTextChanged.connect( lambda text, widget=self.axis: widget.setVisible(text == "axis")) self.bond_button = QPushButton("set selected bond") self.bond_button.clicked.connect(self.set_bonds) layout.addWidget(self.bond_button, 1, 2, 1, 1, Qt.AlignTop) self.bond_button.setVisible(self.vector_option.currentText() == "bond") self.vector_option.currentTextChanged.connect( lambda text, widget=self.bond_button: widget.setVisible(text == "bond")) self.perp_button = QPushButton("set selected atoms") self.perp_button.clicked.connect(self.set_perpendicular) layout.addWidget(self.perp_button, 1, 2, 1, 1, Qt.AlignTop) self.perp_button.setVisible( self.vector_option.currentText() == "perpendicular to plane") self.vector_option.currentTextChanged.connect( lambda text, widget=self.perp_button: widget.setVisible( text == "perpendicular to plane")) self.group_button = QPushButton("set selected atoms") self.group_button.clicked.connect(self.set_group) layout.addWidget(self.group_button, 1, 2, 1, 1, Qt.AlignTop) self.group_button.setVisible( self.vector_option.currentText() == "centroid of atoms") self.vector_option.currentTextChanged.connect( lambda text, widget=self.group_button: widget.setVisible( text == "centroid of atoms")) layout.addWidget(QLabel("angle:"), 2, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) self.angle = QDoubleSpinBox() self.angle.setRange(-360, 360) self.angle.setSingleStep(5) self.angle.setSuffix("°") layout.addWidget(self.angle, 2, 1, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) layout.addWidget(QLabel("preview rotation axis:"), 3, 0, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) self.display_rot_vec = QCheckBox() self.display_rot_vec.setCheckState(Qt.Checked) self.display_rot_vec.stateChanged.connect(self.show_rot_vec) layout.addWidget(self.display_rot_vec, 3, 1, 1, 1, Qt.AlignLeft | Qt.AlignVCenter) rotate_button = QPushButton("rotate selected atoms") rotate_button.clicked.connect(self.do_rotate) layout.addWidget(rotate_button, 4, 0, 1, 3, Qt.AlignTop) self.rotate_button = rotate_button self.status_bar = QStatusBar() self.status_bar.setSizeGripEnabled(False) layout.addWidget(self.status_bar, 5, 0, 1, 3, Qt.AlignTop) self.vector_option.currentTextChanged.connect(self.show_auto_status) self.cor_button.currentIndexChanged.connect( lambda *args: self.show_auto_status("select atoms")) self.cor_button.currentIndexChanged.connect(self.show_rot_vec) self.set_cor_selection.clicked.connect(self.show_rot_vec) self.vector_option.currentIndexChanged.connect(self.show_rot_vec) self.axis.currentIndexChanged.connect(self.show_rot_vec) self.view_axis.currentIndexChanged.connect(self.show_rot_vec) self.bond_button.clicked.connect(self.show_rot_vec) self.perp_button.clicked.connect(self.show_rot_vec) self.group_button.clicked.connect(self.show_rot_vec) layout.setRowStretch(0, 0) layout.setRowStretch(1, 0) layout.setRowStretch(2, 0) layout.setRowStretch(3, 0) layout.setRowStretch(4, 0) layout.setRowStretch(5, 1) layout.setColumnStretch(0, 0) layout.setColumnStretch(1, 1) layout.setColumnStretch(2, 1) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None) def manual_cor(self, *args): selection = selected_atoms(self.session) models = {} for atom in selection: if atom.structure not in models: models[atom.structure] = [atom] else: models[atom.structure].append(atom) self.manual_center = {} for model in models: atoms = models[model] coords = np.array([atom.coord for atom in atoms]) self.manual_center[model] = np.mean(coords, axis=0) def show_auto_status(self, text): if self.cor_button.currentText() == "automatic": if text == "bond": self.status_bar.showMessage( "center set to one of the bonded atoms") elif text == "perpendicular to plane": self.status_bar.showMessage("center set to centroid of atoms") else: self.status_bar.showMessage( "center set to centroid of rotating atoms") elif self.cor_button.currentText() == "select atoms": self.status_bar.showMessage( "center set to centroid of specified atoms") else: self.status_bar.showMessage( "center set to view's center of rotation") def set_bonds(self, *args): bonds = selected_bonds(self.session) if len(bonds) == 0: self.session.logger.error("no bonds selected") return models = [bond.structure for bond in bonds] if any(models.count(m) > 1 for m in models): self.session.logger.error( "multiple bonds selected on the same structure") return self.bonds = { model: (bond.atoms[0].coord - bond.atoms[1].coord) for model, bond in zip(models, bonds) } self.bond_centers = { model: bond.atoms[1].coord for model, bond in zip(models, bonds) } def set_perpendicular(self, *args): atoms = selected_atoms(self.session) if len(atoms) == 0: self.session.logger.error("no atoms selected") return self.perpendiculars = {} self.perp_centers = {} models = set(atom.structure for atom in atoms) for model in models: atom_coords = [] for atom in atoms: if atom.structure is model: atom_coords.append(atom.coord) if len(atom_coords) < 3: self.session.logger.error("fewer than 3 atoms selected on %s" % model.atomspec) continue xyz = np.array(atom_coords) xyz -= np.mean(atom_coords, axis=0) R = np.dot(xyz.T, xyz) u, s, vh = np.linalg.svd(R, compute_uv=True) vector = u[:, -1] self.perpendiculars[model] = vector self.perp_centers[model] = np.mean(atom_coords, axis=0) def set_group(self, *args): atoms = selected_atoms(self.session) if len(atoms) == 0: self.session.logger.error("no atoms selected") return self.groups = {} models = set(atom.structure for atom in atoms) for model in models: atom_coords = [] for atom in atoms: if atom.structure is model: atom_coords.append(atom.coord) self.groups[model] = np.mean(atom_coords, axis=0) def do_rotate(self, *args): selection = selected_atoms(self.session) models = {} for atom in selection: if atom.structure not in models: models[atom.structure] = [atom] else: models[atom.structure].append(atom) if len(models.keys()) == 0: return if self.vector_option.currentText() == "axis": if self.axis.currentText() == "z": vector = np.array([0., 0., 1.]) elif self.axis.currentText() == "y": vector = np.array([0., 1., 0.]) elif self.axis.currentText() == "x": vector = np.array([1., 0., 0.]) elif self.vector_option.currentText() == "view axis": if self.view_axis.currentText() == "z": vector = self.session.view.camera.get_position().axes()[2] elif self.view_axis.currentText() == "y": vector = self.session.view.camera.get_position().axes()[1] elif self.view_axis.currentText() == "x": vector = self.session.view.camera.get_position().axes()[0] elif self.vector_option.currentText() == "bond": vector = self.bonds elif self.vector_option.currentText() == "perpendicular to plane": vector = self.perpendiculars elif self.vector_option.currentText() == "centroid of atoms": vector = self.groups elif self.vector_option.currentText() == "custom": x = self.vector_x.value() y = self.vector_y.value() z = self.vector_z.value() vector = np.array([x, y, z]) angle = np.deg2rad(self.angle.value()) center = {} for model in models: atoms = models[model] coords = np.array([atom.coord for atom in atoms]) center[model] = np.mean(coords, axis=0) if self.cor_button.currentText() == "automatic": if self.vector_option.currentText() == "perpendicular to plane": center = self.perp_centers elif self.vector_option.currentText() == "bond": center = self.bond_centers elif self.cor_button.currentText() == "select atoms": center = self.manual_center else: center = self.session.main_view.center_of_rotation for model in models: if isinstance(vector, dict): if model not in vector.keys(): continue else: v = vector[model] else: v = vector if isinstance(center, dict): if model not in center.keys(): continue else: c = center[model] else: c = center if self.vector_option.currentText( ) == "centroid of atoms" and self.cor_button.currentText( ) != "automatic": v = v - c v = v / np.linalg.norm(v) q = np.hstack(([np.cos(angle / 2)], v * np.sin(angle / 2))) q /= np.linalg.norm(q) qs = q[0] qv = q[1:] xyz = np.array([a.coord for a in models[model]]) xyz -= c xprod = np.cross(qv, xyz) qs_xprod = 2 * qs * xprod qv_xprod = 2 * np.cross(qv, xprod) xyz += qs_xprod + qv_xprod + c for t, coord in zip(models[model], xyz): t.coord = coord def show_rot_vec(self, *args): for model in self.session.models.list(type=Generic3DModel): if model.name == "rotation vector": model.delete() if self.display_rot_vec.checkState() == Qt.Unchecked: return selection = selected_atoms(self.session) if len(selection) == 0: return models = {} for atom in selection: if atom.structure not in models: models[atom.structure] = [atom] else: models[atom.structure].append(atom) if len(models.keys()) == 0: return if self.vector_option.currentText() == "axis": if self.axis.currentText() == "z": vector = np.array([0., 0., 1.]) elif self.axis.currentText() == "y": vector = np.array([0., 1., 0.]) elif self.axis.currentText() == "x": vector = np.array([1., 0., 0.]) elif self.vector_option.currentText() == "view axis": if self.view_axis.currentText() == "z": vector = self.session.view.camera.get_position().axes()[2] elif self.view_axis.currentText() == "y": vector = self.session.view.camera.get_position().axes()[1] elif self.view_axis.currentText() == "x": vector = self.session.view.camera.get_position().axes()[0] elif self.vector_option.currentText() == "bond": vector = self.bonds elif self.vector_option.currentText() == "perpendicular to plane": vector = self.perpendiculars elif self.vector_option.currentText() == "centroid of atoms": vector = self.groups elif self.vector_option.currentText() == "custom": x = self.vector_x.value() y = self.vector_y.value() z = self.vector_z.value() vector = np.array([x, y, z]) center = {} for model in models: atoms = models[model] coords = np.array([atom.coord for atom in atoms]) center[model] = np.mean(coords, axis=0) if self.cor_button.currentText() == "automatic": if self.vector_option.currentText() == "perpendicular to plane": center = self.perp_centers elif self.vector_option.currentText() == "bond": center = self.bond_centers elif self.cor_button.currentText() == "select atoms": center = self.manual_center else: center = self.session.main_view.center_of_rotation for model in models: if isinstance(vector, dict): if model not in vector.keys(): continue else: v = vector[model] else: v = vector if isinstance(center, dict): if model not in center.keys(): continue else: c = center[model] else: c = center if self.vector_option.currentText( ) == "centroid of atoms" and self.cor_button.currentText( ) != "automatic": v = v - c if np.linalg.norm(v) == 0: continue residues = [] for atom in models[model]: if atom.residue not in residues: residues.append(atom.residue) v_c = c + v s = ".color red\n" s += ".arrow %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f 0.2 0.4 0.7\n" % ( *c, *v_c) stream = BytesIO(bytes(s, 'utf-8')) bild_obj, status = read_bild(self.session, stream, "rotation vector") self.session.models.add(bild_obj, parent=model) def delete(self): self.session.triggers.remove_handler(self._show_rot_vec) global_triggers = get_triggers() global_triggers.remove_handler(self._changes) for model in self.session.models.list(type=Generic3DModel): if model.name == "rotation vector": model.delete() return super().delete() def close(self): self.session.triggers.remove_handler(self._show_rot_vec) global_triggers = get_triggers() global_triggers.remove_handler(self._changes) for model in self.session.models.list(type=Generic3DModel): if model.name == "rotation vector": model.delete() return super().close()
def _build_ui(self): layout = QFormLayout() self.cone_option = QComboBox() self.cone_option.addItems(["Tolman (Unsymmetrical)", "Exact"]) ndx = self.cone_option.findText(self.settings.cone_option, Qt.MatchExactly) self.cone_option.setCurrentIndex(ndx) layout.addRow("method:", self.cone_option) self.radii_option = QComboBox() self.radii_option.addItems(["Bondi", "UMN"]) ndx = self.radii_option.findText(self.settings.radii, Qt.MatchExactly) self.radii_option.setCurrentIndex(ndx) layout.addRow("radii:", self.radii_option) self.display_cone = QCheckBox() self.display_cone.setChecked(self.settings.display_cone) layout.addRow("show cone:", self.display_cone) self.display_radii = QCheckBox() self.display_radii.setChecked(self.settings.display_radii) layout.addRow("show radii:", self.display_radii) set_ligand_button = QPushButton("set ligand to current selection") set_ligand_button.clicked.connect(self.set_ligand) layout.addRow(set_ligand_button) self.set_ligand_button = set_ligand_button calc_cone_button = QPushButton( "calculate cone angle for ligand on selected center") calc_cone_button.clicked.connect(self.calc_cone) layout.addRow(calc_cone_button) self.calc_cone_button = calc_cone_button remove_cone_button = QPushButton("remove cone visualizations") remove_cone_button.clicked.connect(self.del_cone) layout.addRow(remove_cone_button) self.remove_cone_button = remove_cone_button self.table = QTableWidget() self.table.setColumnCount(3) self.table.setHorizontalHeaderLabels([ 'model', 'center', 'cone angle (°)', ]) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.resizeColumnToContents(0) self.table.resizeColumnToContents(1) self.table.resizeColumnToContents(2) self.table.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) layout.addRow(self.table) menu = QMenuBar() export = menu.addMenu("&Export") clear = QAction("Clear data table", self.tool_window.ui_area) clear.triggered.connect(self.clear_table) export.addAction(clear) copy = QAction("&Copy CSV to clipboard", self.tool_window.ui_area) copy.triggered.connect(self.copy_csv) shortcut = QKeySequence(Qt.CTRL + Qt.Key_C) copy.setShortcut(shortcut) export.addAction(copy) save = QAction("&Save CSV...", self.tool_window.ui_area) save.triggered.connect(self.save_csv) #this shortcut interferes with main window's save shortcut #I've tried different shortcut contexts to no avail #thanks Qt... #shortcut = QKeySequence(Qt.CTRL + Qt.Key_S) #save.setShortcut(shortcut) #save.setShortcutContext(Qt.WidgetShortcut) export.addAction(save) delimiter = export.addMenu("Delimiter") comma = QAction("comma", self.tool_window.ui_area, checkable=True) comma.setChecked(self.settings.delimiter == "comma") comma.triggered.connect(lambda *args, delim="comma": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(comma) tab = QAction("tab", self.tool_window.ui_area, checkable=True) tab.setChecked(self.settings.delimiter == "tab") tab.triggered.connect(lambda *args, delim="tab": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(tab) space = QAction("space", self.tool_window.ui_area, checkable=True) space.setChecked(self.settings.delimiter == "space") space.triggered.connect(lambda *args, delim="space": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(space) semicolon = QAction("semicolon", self.tool_window.ui_area, checkable=True) semicolon.setChecked(self.settings.delimiter == "semicolon") semicolon.triggered.connect(lambda *args, delim="semicolon": self. settings.__setattr__("delimiter", delim)) delimiter.addAction(semicolon) add_header = QAction("&Include CSV header", self.tool_window.ui_area, checkable=True) add_header.setChecked(self.settings.include_header) add_header.triggered.connect(self.header_check) export.addAction(add_header) comma.triggered.connect( lambda *args, action=tab: action.setChecked(False)) comma.triggered.connect( lambda *args, action=space: action.setChecked(False)) comma.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) tab.triggered.connect( lambda *args, action=comma: action.setChecked(False)) tab.triggered.connect( lambda *args, action=space: action.setChecked(False)) tab.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) space.triggered.connect( lambda *args, action=comma: action.setChecked(False)) space.triggered.connect( lambda *args, action=tab: action.setChecked(False)) space.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=comma: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=tab: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=space: action.setChecked(False)) menu.setNativeMenuBar(False) self._menu = menu layout.setMenuBar(menu) menu.setVisible(True) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None)
def addAttributeSlot(self): """ Adds a new attribute (column) to the table """ dialog = QDialog(self) dialog.setModal(True) dialog.setWindowTitle('Add Attribute') layout = QVBoxLayout() dialog.setLayout(layout) form = QFormLayout() nameBox = QLineEdit() typeCombo = QComboBox() for attrType in _attrTypes: typeName = partio.TypeName(attrType) typeCombo.addItem(typeName) typeCombo.setCurrentIndex(partio.FLOAT) countBox = QLineEdit() countBox.setValidator(QIntValidator()) countBox.setText('1') fixedCheckbox = QCheckBox() valueBox = QLineEdit() valueBox.setText('0') form.addRow('Name:', nameBox) form.addRow('Type:', typeCombo) form.addRow('Count:', countBox) form.addRow('Fixed:', fixedCheckbox) form.addRow('Default Value:', valueBox) layout.addLayout(form) buttons = QHBoxLayout() layout.addLayout(buttons) add = QPushButton('Add') add.clicked.connect(dialog.accept) buttons.addWidget(add) cancel = QPushButton('Cancel') cancel.clicked.connect(dialog.reject) buttons.addWidget(cancel) if not dialog.exec_(): return name = str(nameBox.text()) if not name: print('Please supply a name for the new attribute') # TODO: prompt return attrType = typeCombo.currentIndex() count = int(countBox.text()) fixed = fixedCheckbox.isChecked() values = list(str(valueBox.text()).strip().split()) for i in range(count): if i < len(values): value = values[i] else: value = values[-1] if attrType == partio.INT or attrType == partio.INDEXEDSTR: values[i] = int(value) elif attrType == partio.FLOAT or attrType == partio.VECTOR: values[i] = float(value) # pylint:disable=R0204 else: values[i] = 0.0 # pylint:disable=R0204 value = tuple(values) self.data.addAttribute(name, attrType, count, fixed, value)
def __init__(self): super(Second, self).__init__() self.setWindowTitle("Add new device") self.nameLabel = QLabel(self) self.nameLabel.move(10, 10) self.nameLabel.setText("Device name") self.nameEntry = QLineEdit(self) self.nameEntry.move(10, 40) self.nameEntry.resize(100, 30) self.colorLabel = QLabel(self) self.colorLabel.move(120, 10) self.colorLabel.setText("Color cameras") self.colorEntry = QLineEdit(self) self.colorEntry.move(140, 40) self.colorEntry.resize(70, 30) self.colorEntry.setValidator(QtGui.QIntValidator()) self.monoLabel = QLabel(self) self.monoLabel.move(230, 10) self.monoLabel.setText("Mono cameras") self.monoEntry = QLineEdit(self) self.monoEntry.move(250, 40) self.monoEntry.resize(70, 30) self.monoEntry.setValidator(QtGui.QIntValidator()) self.depthPresent = QCheckBox("Include depth", self) self.depthPresent.move(10, 80) self.depthPresent.resize(150, 30) self.depthPresent.stateChanged.connect(self.toggle_depth) self.leftfovLabel = QLabel(self) self.leftfovLabel.move(10, 120) self.leftfovLabel.setText("Left FOV deg.") self.leftfovEntry = QLineEdit(self) self.leftfovEntry.move(180, 120) self.leftfovEntry.resize(140, 30) self.leftfovEntry.setValidator(QtGui.QDoubleValidator()) self.rightfovLabel = QLabel(self) self.rightfovLabel.move(10, 160) self.rightfovLabel.setText("Right FOV deg.") self.rightfovEntry = QLineEdit(self) self.rightfovEntry.move(180, 160) self.rightfovEntry.resize(140, 30) self.rightfovEntry.setValidator(QtGui.QDoubleValidator()) self.rgbfovLabel = QLabel(self) self.rgbfovLabel.move(10, 200) self.rgbfovLabel.setText("RGB FOV deg.") self.rgbfovEntry = QLineEdit(self) self.rgbfovEntry.move(180, 200) self.rgbfovEntry.resize(140, 30) self.rgbfovEntry.setValidator(QtGui.QDoubleValidator()) self.lrdistanceLabel = QLabel(self) self.lrdistanceLabel.move(10, 240) self.lrdistanceLabel.resize(200, 30) self.lrdistanceLabel.setText("Left - Right distance cm.") self.lrdistanceEntry = QLineEdit(self) self.lrdistanceEntry.move(180, 240) self.lrdistanceEntry.resize(140, 30) self.lrdistanceEntry.setValidator(QtGui.QDoubleValidator()) self.lrgbdistanceLabel = QLabel(self) self.lrgbdistanceLabel.move(10, 280) self.lrgbdistanceLabel.resize(200, 30) self.lrgbdistanceLabel.setText("Left - RGB distance cm.") self.lrgbdistanceEntry = QLineEdit(self) self.lrgbdistanceEntry.move(180, 280) self.lrgbdistanceEntry.resize(140, 30) self.lrgbdistanceEntry.setValidator(QtGui.QDoubleValidator()) self.saveButton = QPushButton("Save", self) self.saveButton.resize(100, 30) self.saveButton.clicked.connect(self.save) self.cancelButton = QPushButton("Cancel", self) self.cancelButton.resize(100, 30) self.cancelButton.clicked.connect(self.cancel) self.toggle_depth(False)
class LigandSterimol(ToolInstance): help = "https://github.com/QChASM/SEQCROW/wiki/Ligand-Sterimol-Tool" SESSION_ENDURING = False SESSION_SAVE = False def __init__(self, session, name): super().__init__(session, name) from chimerax.ui import MainToolWindow self.tool_window = MainToolWindow(self) self.settings = _SterimolSettings(self.session, name) self._build_ui() def _build_ui(self): layout = QFormLayout() self.radii_option = QComboBox() self.radii_option.addItems(["Bondi", "UMN"]) ndx = self.radii_option.findText(self.settings.radii, Qt.MatchExactly) self.radii_option.setCurrentIndex(ndx) layout.addRow("radii:", self.radii_option) self.L_option = QComboBox() self.L_option.addItems([ "to centroid of coordinating atoms", "bisect angle between coordinating atoms" ]) ndx = self.L_option.findText(self.settings.L_option, Qt.MatchExactly) self.L_option.setCurrentIndex(ndx) layout.addRow("L axis:", self.L_option) self.sterimol2vec = QGroupBox("Sterimol2Vec") sterimol2vec_layout = QFormLayout(self.sterimol2vec) self.at_L = QDoubleSpinBox() self.at_L.setRange(-10, 30) self.at_L.setDecimals(2) self.at_L.setSingleStep(0.25) self.at_L.setValue(self.settings.at_L) sterimol2vec_layout.addRow("L value:", self.at_L) layout.addRow(self.sterimol2vec) self.sterimol2vec.setCheckable(True) self.sterimol2vec.toggled.connect(lambda x: self.at_L.setEnabled(x)) self.sterimol2vec.setChecked(self.settings.sterimol2vec) self.display_vectors = QCheckBox() self.display_vectors.setChecked(self.settings.display_vectors) layout.addRow("show vectors:", self.display_vectors) self.display_radii = QCheckBox() self.display_radii.setChecked(self.settings.display_radii) layout.addRow("show radii:", self.display_radii) calc_sterimol_button = QPushButton( "calculate parameters for selected ligands") calc_sterimol_button.clicked.connect(self.calc_sterimol) layout.addRow(calc_sterimol_button) self.calc_sterimol_button = calc_sterimol_button remove_sterimol_button = QPushButton("remove Sterimol visualizations") remove_sterimol_button.clicked.connect(self.del_sterimol) layout.addRow(remove_sterimol_button) self.remove_sterimol_button = remove_sterimol_button self.table = QTableWidget() self.table.setColumnCount(8) self.table.setHorizontalHeaderLabels([ 'model', 'coord. atoms', 'B\u2081', 'B\u2082', 'B\u2083', 'B\u2084', 'B\u2085', 'L', ]) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setEditTriggers(QTableWidget.NoEditTriggers) self.table.resizeColumnToContents(0) self.table.resizeColumnToContents(1) self.table.resizeColumnToContents(2) self.table.resizeColumnToContents(3) self.table.resizeColumnToContents(4) self.table.resizeColumnToContents(5) self.table.resizeColumnToContents(6) self.table.resizeColumnToContents(7) self.table.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 3, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 4, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 5, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 6, QHeaderView.Stretch) self.table.horizontalHeader().setSectionResizeMode( 7, QHeaderView.Stretch) layout.addRow(self.table) menu = QMenuBar() export = menu.addMenu("&Export") clear = QAction("Clear data table", self.tool_window.ui_area) clear.triggered.connect(self.clear_table) export.addAction(clear) copy = QAction("&Copy CSV to clipboard", self.tool_window.ui_area) copy.triggered.connect(self.copy_csv) shortcut = QKeySequence(Qt.CTRL + Qt.Key_C) copy.setShortcut(shortcut) export.addAction(copy) save = QAction("&Save CSV...", self.tool_window.ui_area) save.triggered.connect(self.save_csv) #this shortcut interferes with main window's save shortcut #I've tried different shortcut contexts to no avail #thanks Qt... #shortcut = QKeySequence(Qt.CTRL + Qt.Key_S) #save.setShortcut(shortcut) #save.setShortcutContext(Qt.WidgetShortcut) export.addAction(save) delimiter = export.addMenu("Delimiter") comma = QAction("comma", self.tool_window.ui_area, checkable=True) comma.setChecked(self.settings.delimiter == "comma") comma.triggered.connect(lambda *args, delim="comma": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(comma) tab = QAction("tab", self.tool_window.ui_area, checkable=True) tab.setChecked(self.settings.delimiter == "tab") tab.triggered.connect(lambda *args, delim="tab": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(tab) space = QAction("space", self.tool_window.ui_area, checkable=True) space.setChecked(self.settings.delimiter == "space") space.triggered.connect(lambda *args, delim="space": self.settings. __setattr__("delimiter", delim)) delimiter.addAction(space) semicolon = QAction("semicolon", self.tool_window.ui_area, checkable=True) semicolon.setChecked(self.settings.delimiter == "semicolon") semicolon.triggered.connect(lambda *args, delim="semicolon": self. settings.__setattr__("delimiter", delim)) delimiter.addAction(semicolon) add_header = QAction("&Include CSV header", self.tool_window.ui_area, checkable=True) add_header.setChecked(self.settings.include_header) add_header.triggered.connect(self.header_check) export.addAction(add_header) comma.triggered.connect( lambda *args, action=tab: action.setChecked(False)) comma.triggered.connect( lambda *args, action=space: action.setChecked(False)) comma.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) tab.triggered.connect( lambda *args, action=comma: action.setChecked(False)) tab.triggered.connect( lambda *args, action=space: action.setChecked(False)) tab.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) space.triggered.connect( lambda *args, action=comma: action.setChecked(False)) space.triggered.connect( lambda *args, action=tab: action.setChecked(False)) space.triggered.connect( lambda *args, action=semicolon: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=comma: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=tab: action.setChecked(False)) semicolon.triggered.connect( lambda *args, action=space: action.setChecked(False)) menu.setNativeMenuBar(False) self._menu = menu layout.setMenuBar(menu) menu.setVisible(True) self.tool_window.ui_area.setLayout(layout) self.tool_window.manage(None) def clear_table(self): are_you_sure = QMessageBox.question( None, "Clear table?", "Are you sure you want to clear the data table?", ) if are_you_sure != QMessageBox.Yes: return self.table.setRowCount(0) def calc_sterimol(self, *args): self.settings.radii = self.radii_option.currentText() self.settings.display_radii = self.display_radii.checkState( ) == Qt.Checked self.settings.display_vectors = self.display_vectors.checkState( ) == Qt.Checked self.settings.at_L = self.at_L.value() self.settings.sterimol2vec = self.sterimol2vec.isChecked() self.settings.L_option = self.L_option.currentText() targets, neighbors, datas = sterimol_cmd( self.session, selected_atoms(self.session), radii=self.radii_option.currentText(), showVectors=self.display_vectors.checkState() == Qt.Checked, showRadii=self.display_radii.checkState() == Qt.Checked, return_values=True, at_L=self.at_L.value() if self.sterimol2vec.isChecked() else None, bisect_L=self.L_option.currentText() == "bisect angle between coordinating atoms", ) if len(targets) == 0: return if self.settings.delimiter == "comma": delim = "," elif self.settings.delimiter == "space": delim = " " elif self.settings.delimiter == "tab": delim = "\t" elif self.settings.delimiter == "semicolon": delim = ";" # self.table.setRowCount(0) for t, b, data in zip(targets, neighbors, datas): row = self.table.rowCount() self.table.insertRow(row) targ = QTableWidgetItem() targ.setData(Qt.DisplayRole, t) self.table.setItem(row, 0, targ) neigh = QTableWidgetItem() neigh.setData(Qt.DisplayRole, delim.join(b)) self.table.setItem(row, 1, neigh) l = np.linalg.norm(data["L"][1] - data["L"][0]) b1 = np.linalg.norm(data["B1"][1] - data["B1"][0]) b2 = np.linalg.norm(data["B2"][1] - data["B2"][0]) b3 = np.linalg.norm(data["B3"][1] - data["B3"][0]) b4 = np.linalg.norm(data["B4"][1] - data["B4"][0]) b5 = np.linalg.norm(data["B5"][1] - data["B5"][0]) li = QTableWidgetItem() li.setData(Qt.DisplayRole, "%.2f" % l) self.table.setItem(row, 7, li) b1i = QTableWidgetItem() b1i.setData(Qt.DisplayRole, "%.2f" % b1) self.table.setItem(row, 2, b1i) b2i = QTableWidgetItem() b2i.setData(Qt.DisplayRole, "%.2f" % b2) self.table.setItem(row, 3, b2i) b3i = QTableWidgetItem() b3i.setData(Qt.DisplayRole, "%.2f" % b3) self.table.setItem(row, 4, b3i) b4i = QTableWidgetItem() b4i.setData(Qt.DisplayRole, "%.2f" % b4) self.table.setItem(row, 5, b4i) b5i = QTableWidgetItem() b5i.setData(Qt.DisplayRole, "%.2f" % b5) self.table.setItem(row, 6, b5i) for i in range(0, 7): if i == 1: continue self.table.resizeColumnToContents(i) def header_check(self, state): """user has [un]checked the 'include header' option on the menu""" if state: self.settings.include_header = True else: self.settings.include_header = False def get_csv(self): if self.settings.delimiter == "comma": delim = "," elif self.settings.delimiter == "space": delim = " " elif self.settings.delimiter == "tab": delim = "\t" elif self.settings.delimiter == "semicolon": delim = ";" if self.settings.include_header: s = delim.join([ "substituent_atom", "bonded_atom", "B1", "B2", "B3", "B4", "B5", "L" ]) s += "\n" else: s = "" for i in range(0, self.table.rowCount()): s += delim.join([ item.data(Qt.DisplayRole) for item in [self.table.item(i, j) for j in range(0, 8)] ]) s += "\n" return s def copy_csv(self): app = QApplication.instance() clipboard = app.clipboard() csv = self.get_csv() clipboard.setText(csv) self.session.logger.status("copied to clipboard") def save_csv(self): """save data on current tab to CSV file""" filename, _ = QFileDialog.getSaveFileName(filter="CSV Files (*.csv)") if filename: s = self.get_csv() with open(filename, 'w') as f: f.write(s.strip()) self.session.logger.status("saved to %s" % filename) def del_sterimol(self): for model in self.session.models.list(type=Generic3DModel): if model.name.startswith("Sterimol"): model.delete() def display_help(self): """Show the help for this tool in the help viewer.""" from chimerax.core.commands import run run(self.session, 'open %s' % self.help if self.help is not None else "")