class ExpandableFrame(base.BaseFrame, object): def __init__(self, title='', icon=None, parent=None): self._is_collapsed = True self._title_frame = None self._content = None self._title = title self._icon = icon self._content_layout = None super(ExpandableFrame, self).__init__(parent=parent) def ui(self): super(ExpandableFrame, self).ui() title_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._title_frame = TitleFrame(title=self._title, icon=self._icon, collapsed=self._is_collapsed) self._icon_button = label.BaseLabel(parent=self) if self._icon: self._icon_button.setPixmap(self._icon.pixmap(QSize(20, 20))) else: self._icon_button.setVisible(False) title_layout.addWidget(self._icon_button) title_layout.addWidget(self._title_frame) title_layout.addStretch() self._content = QWidget() self._content_layout = layouts.VerticalLayout() self._content.setLayout(self._content_layout) self._content.setVisible(not self._is_collapsed) self.main_layout.addLayout(title_layout) self.main_layout.addWidget(self._content) def setup_signals(self): self._title_frame.clicked.connect(self._on_toggle_collapsed) self._icon_button.clicked.connect(self._on_toggle_collapsed) def addWidget(self, widget): self._content_layout.addWidget(widget) def addLayout(self, layout): self._content_layout.addLayout(layout) def set_title(self, title): self._title_frame.set_title(title) def _on_toggle_collapsed(self): self._content.setVisible(self._is_collapsed) self._is_collapsed = not self._is_collapsed self._title_frame._arrow.setArrow(self._is_collapsed)
class JointWidget(base.CommandRigToolBoxWidget, object): def __init__(self, client, commands_data, parent=None): self._model = JointWidgetModel() super(JointWidget, self).__init__( title='Joint', commands_data=commands_data, controller=JointWidgetController(model=self._model, client=client), parent=parent) self.refresh() @property def model(self): return self._model @property def controller(self): return self._controller def ui(self): super(JointWidget, self).ui() self._joints_to_insert_widget = QWidget() joints_to_insert_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._joints_to_insert_widget.setLayout(joints_to_insert_layout) joints_to_insert_lbl = label.BaseLabel('Num. Joints: ', parent=self) self._joints_to_insert_spn = spinbox.BaseSpinBox(parent=self) self._joints_to_insert_spn.setMinimum(1) self._joints_to_insert_spn.setMaximum(99999999) joints_to_insert_layout.addWidget(joints_to_insert_lbl) joints_to_insert_layout.addWidget(self._joints_to_insert_spn) self._create_joints_on_curve_widget = QWidget() create_joints_on_curve_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._create_joints_on_curve_widget.setLayout( create_joints_on_curve_layout) create_joints_on_curve_lbl = label.BaseLabel('Num. Joints: ', parent=self) self._create_joints_on_curve_spn = spinbox.BaseSpinBox(parent=self) self._create_joints_on_curve_spn.setMinimum(1) self._create_joints_on_curve_spn.setMaximum(99999999) create_joints_on_curve_layout.addWidget(create_joints_on_curve_lbl) create_joints_on_curve_layout.addWidget( self._create_joints_on_curve_spn) self._snap_joints_to_curve_widget = QWidget() snap_joints_to_curve_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._snap_joints_to_curve_widget.setLayout( snap_joints_to_curve_layout) snap_joints_to_curve_lbl = label.BaseLabel('Num. Joints: ', parent=self) self._snap_joints_to_curve_spn = spinbox.BaseSpinBox(parent=self) self._snap_joints_to_curve_spn.setMinimum(0) self._snap_joints_to_curve_spn.setMaximum(99999999) snap_joints_to_curve_layout.addWidget(snap_joints_to_curve_lbl) snap_joints_to_curve_layout.addWidget(self._snap_joints_to_curve_spn) self._joints_display_size_widget = QWidget() joint_display_size_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._joints_display_size_widget.setLayout(joint_display_size_layout) joint_display_size_lbl = label.BaseLabel('Joints Size: ', parent=self) self._joints_display_size_spn = spinbox.BaseDoubleSpinBox(parent=self) self._joints_display_size_spn.setSingleStep(0.5) self._joints_display_size_spn.setMinimum(0.1) self._joints_display_size_spn.setMaximum(999) self._joints_display_live_cbx = checkbox.BaseCheckBox('Live', parent=self) joint_display_size_layout.addWidget(joint_display_size_lbl) joint_display_size_layout.addWidget(self._joints_display_size_spn) joint_display_size_layout.addWidget(self._joints_display_live_cbx) self._joints_to_insert_widget.setVisible(False) self._create_joints_on_curve_widget.setVisible(False) self._snap_joints_to_curve_widget.setVisible(False) self._joints_display_size_widget.setVisible(False) self.main_layout.addWidget(self._joints_to_insert_widget) self.main_layout.addWidget(self._create_joints_on_curve_widget) self.main_layout.addWidget(self._snap_joints_to_curve_widget) self.main_layout.addWidget(self._joints_display_size_widget) def setup_signals(self): self._joints_to_insert_spn.valueChanged.connect( self._controller.change_joints_to_insert) self._create_joints_on_curve_spn.valueChanged.connect( self._controller.change_joints_on_curve) self._snap_joints_to_curve_spn.valueChanged.connect( self._controller.change_snap_joints_to_curve) self._joints_display_size_spn.valueChanged.connect( self._controller.change_joints_display_size) self._joints_display_live_cbx.toggled.connect( self._controller.change_joints_display_size_live) self._model.jointsToInsertChanged.connect( self._joints_to_insert_spn.setValue) self._model.jointsOnCurveChanged.connect( self._create_joints_on_curve_spn.setValue) self._model.snapJointsToCurveChanged.connect( self._snap_joints_to_curve_spn.setValue) self._model.jointsDisplaySizeChanged.connect( self._on_joints_display_size_changed) self._model.jointsDisplaySizeLiveChanged.connect( self._joints_display_live_cbx.setChecked) def refresh(self): self._joints_to_insert_spn.setValue(self._model.joints_to_insert) self._joints_display_size_spn.setValue(self._model.joints_display_size) self._joints_display_live_cbx.setChecked( self._model.joints_display_size_live) self._create_joints_on_curve_spn.setValue(self._model.joints_on_curve) self._snap_joints_to_curve_spn.setValue( self._model.snap_joints_to_curve) def _on_joints_display_size_changed(self, value): live = self._model.joints_display_size_live with qt_contexts.block_signals(self._joints_display_size_spn): self._joints_display_size_spn.setValue(value) if live: self._controller.joint_display_size()
class InfoMessage(base.BaseWidget, object): def __init__(self, name='', description='', instructions='', parent=None): self._name = '' self._description = '' self._instructions = instructions super(InfoMessage, self).__init__(parent) self.setAttribute(Qt.WA_StyledBackground) self.theme_type = message.MessageTypes.INFO self.style().polish(self) self.name = name self.description = description self.instructions = instructions # ================================================================================================================= # PROPERTIES # ================================================================================================================= def _get_name(self): return self._name def _set_name(self, name): self._name = str(name) self._expandable_frame.set_title(self._name) def _get_description(self): return self._description def _set_description(self, text): self._description = str(text) self._description_text.setPlainText(self._description) def _get_instructions(self): return self._instructions def _set_instructions(self, instructions): self._instructions = str(instructions) self._instructions_text.clear() self._instructions_text.insertHtml(instructions) self._instructions_widget.setVisible(bool(instructions)) name = Property(str, _get_name, _set_name) description = Property(str, _get_description, _set_description) instructions = Property(str, _get_instructions, _set_instructions) # ================================================================================================================= # OVERRIDES # ================================================================================================================= def ui(self): super(InfoMessage, self).ui() self.setMaximumHeight(150) info_icon = resources.icon('info') self._expandable_frame = expandables.ExpandableFrame(icon=info_icon, parent=self) self._expandable_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) expandable_layout = layouts.HorizontalLayout(margins=(2, 2, 2, 2)) texts_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._description_text = QPlainTextEdit(parent=self) self._description_text.setReadOnly(True) self._description_text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self._description_text.setFocusPolicy(Qt.NoFocus) self._description_text.setFrameShape(QFrame.NoFrame) self._instructions_widget = QWidget() instructions_layout = layouts.VerticalLayout(spacing=2, margins=(0, 0, 0, 0)) self._instructions_widget.setLayout(instructions_layout) self._instructions_text = QTextEdit(parent=self) self._instructions_text.setReadOnly(True) self._instructions_text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) self._instructions_text.setFocusPolicy(Qt.NoFocus) self._instructions_text.setFrameShape(QFrame.NoFrame) self._instructions_widget.setVisible(False) # self._instructions_text.insertHtml("<ul><li>text 1</li><li>text 2</li><li>text 3</li></ul> <br />") instructions_layout.addWidget(dividers.Divider('Instructions')) instructions_layout.addWidget(self._instructions_text) texts_layout.addWidget(self._description_text) texts_layout.addWidget(self._instructions_widget) content_layout = layouts.VerticalLayout() content_layout.addLayout(texts_layout) expandable_layout.addLayout(content_layout) self._expandable_frame.addLayout(expandable_layout) self.main_layout.addWidget(self._expandable_frame)
class SkinningWidget(base.CommandRigToolBoxWidget, object): def __init__(self, client, commands_data, parent=None): self._model = SkinningWidgetModel() super(SkinningWidget, self).__init__( title='Skinning', commands_data=commands_data, controller=SkinningWidgetController(model=self._model, client=client), parent=parent) self.refresh() @property def model(self): return self._model @property def controller(self): return self._controller def ui(self): super(SkinningWidget, self).ui() self._average_falloff_widget = QWidget() average_falloff_layout = layouts.VerticalLayout(spacing=2, margins=(5, 5, 5, 5)) self._average_falloff_widget.setLayout(average_falloff_layout) self._average_falloff_curve = fallofcurve.FallofCurveWidget( parent=self) average_falloff_layout.addWidget(self._average_falloff_curve) self._mirror_auto_assign_joints_labels_cbx = checkbox.BaseCheckBox( 'Auto Assign Labels', self) self._copy_skin_weights_auto_assign_joints_labels_cbx = checkbox.BaseCheckBox( 'Auto Assign Labels', self) self._transfer_skin_uvs_auto_assign_joints_labels_cbx = checkbox.BaseCheckBox( 'Auto Assign Labels', self) self._clean_skin_mesh_auto_assign_joints_labels_cbx = checkbox.BaseCheckBox( 'Auto Assign Labels', self) self._extract_skin_faces_auto_assign_joints_labels_cbx = checkbox.BaseCheckBox( 'Auto Assign Labels', self) self._distance_widget = QWidget() distance_layout = layouts.VerticalLayout(spacing=2, margins=(5, 5, 5, 5)) self._distance_widget.setLayout(distance_layout) self._distance_average_cbx = checkbox.BaseCheckBox('On Distance', self) self._average_falloff_curve = fallofcurve.FallofCurveWidget( parent=self) distance_layout.addWidget(self._distance_average_cbx) distance_layout.addWidget(self._average_falloff_curve) self._fast_delete_cbx = checkbox.BaseCheckBox('Fast Delete', self) self._average_falloff_widget.setVisible(False) self._mirror_auto_assign_joints_labels_cbx.setVisible(False) self._copy_skin_weights_auto_assign_joints_labels_cbx.setVisible(False) self._transfer_skin_uvs_auto_assign_joints_labels_cbx.setVisible(False) self._clean_skin_mesh_auto_assign_joints_labels_cbx.setVisible(False) self._extract_skin_faces_auto_assign_joints_labels_cbx.setVisible( False) self._distance_widget.setVisible(False) self._fast_delete_cbx.setVisible(False) self.main_layout.addWidget(self._average_falloff_widget) self.main_layout.addWidget(self._mirror_auto_assign_joints_labels_cbx) self.main_layout.addWidget( self._copy_skin_weights_auto_assign_joints_labels_cbx) self.main_layout.addWidget( self._transfer_skin_uvs_auto_assign_joints_labels_cbx) self.main_layout.addWidget( self._clean_skin_mesh_auto_assign_joints_labels_cbx) self.main_layout.addWidget( self._extract_skin_faces_auto_assign_joints_labels_cbx) self.main_layout.addWidget(self._distance_widget) self.main_layout.addWidget(self._fast_delete_cbx) def setup_signals(self): self._mirror_auto_assign_joints_labels_cbx.toggled.connect( self._controller.set_mirror_auto_assign_labels) self._copy_skin_weights_auto_assign_joints_labels_cbx.toggled.connect( self._controller.set_copy_skin_weights_auto_assign_labels) self._transfer_skin_uvs_auto_assign_joints_labels_cbx.toggled.connect( self._controller.set_transfer_skin_uvs_auto_assign_labels) self._clean_skin_mesh_auto_assign_joints_labels_cbx.toggled.connect( self._controller.set_clean_skin_mesh_auto_assign_labels) self._extract_skin_faces_auto_assign_joints_labels_cbx.toggled.connect( self._controller.set_extract_skin_faces_auto_assign_labels) self._distance_average_cbx.toggled.connect( self._controller.set_distance_average) self._fast_delete_cbx.toggled.connect(self._controller.set_fast_delete) self._average_falloff_curve.curveUpdated.connect( self._controller.set_average_weights_curve_points) self._model.mirrorAutoAssignLabelsChanged.connect( self._mirror_auto_assign_joints_labels_cbx.setChecked) self._model.copySkinWeightsAutoAssignLabelsChanged.connect( self._copy_skin_weights_auto_assign_joints_labels_cbx.setChecked) self._model.transferSkinUVsAutoAssignLabelsChanged.connect( self._transfer_skin_uvs_auto_assign_joints_labels_cbx.setChecked) self._model.cleanSkinMeshAutoAssignLabelsChanged.connect( self._clean_skin_mesh_auto_assign_joints_labels_cbx.setChecked) self._model.extractSkinFacesAutoAssignLabelsChanged.connect( self._extract_skin_faces_auto_assign_joints_labels_cbx.setChecked) self._model.useDistanceAverageChanged.connect( self._distance_average_cbx.setChecked) self._model.fastDeleteChanged.connect(self._fast_delete_cbx.setChecked) def _check_command_availability(self, command_name): client = self._controller.client if not client: return False if command_name == 'br_smooth_weights' or command_name == 'br_transfer_weights': for plugin_name in [ 'rampWeights.mll', 'weightsServer.mll', 'weightDriver.mll', 'brSmoothWeights.mll' ]: client.load_plugin(plugin_name, quiet=True) plugin_name = 'brSmoothWeights.mll' return client.is_plugin_loaded(plugin_name) elif command_name == 'ng_skin_tools': plugin_names = ['ngSkinTools2.mll', 'ngSkinTools.mll'] for plugin_name in plugin_names: client.load_plugin(plugin_name, quiet=True) if client.is_plugin_loaded(plugin_name): return True return False return True def _on_show_context_menu(self): super(SkinningWidget, self)._on_show_context_menu() self._average_falloff_curve.update_view() def refresh(self): self._mirror_auto_assign_joints_labels_cbx.setChecked( self._model.mirror_auto_assign_labels) self._copy_skin_weights_auto_assign_joints_labels_cbx.setChecked( self._model.copy_skin_weights_auto_assign_labels) self._transfer_skin_uvs_auto_assign_joints_labels_cbx.setChecked( self._model.transfer_uvs_auto_assign_labels) self._clean_skin_mesh_auto_assign_joints_labels_cbx.setChecked( self._model.clean_skin_mesh_auto_assign_labels) self._extract_skin_faces_auto_assign_joints_labels_cbx.setChecked( self._model.extract_skin_faces_auto_assign_labels) self._distance_average_cbx.setChecked(self._model.use_distance_average) self._fast_delete_cbx.setChecked(self._model.fast_delete) # NOTE: We do this to force model, to have point list value on startup self._controller.set_average_weights_curve_points( self._average_falloff_curve.curve_as_points())
def ui(self): super(BaseAlembicImporter, self).ui() buttons_layout = layouts.GridLayout() self.main_layout.addLayout(buttons_layout) shot_name_lbl = label.BaseLabel('Shot Name: ', parent=self) self._shot_line = lineedit.BaseLineEdit(parent=self) buttons_layout.addWidget(shot_name_lbl, 1, 0, 1, 1, Qt.AlignRight) buttons_layout.addWidget(self._shot_line, 1, 1) shot_name_lbl.setVisible(False) self._shot_line.setVisible(False) folder_icon = resources.icon('folder') alembic_path_layout = layouts.HorizontalLayout(spacing=2, margins=(2, 2, 2, 2)) alembic_path_widget = QWidget() alembic_path_widget.setLayout(alembic_path_layout) alembic_path_lbl = label.BaseLabel('Alembic File: ', parent=self) self._alembic_path_line = lineedit.BaseLineEdit(parent=self) self._alembic_path_line.setReadOnly(True) self._alembic_path_btn = buttons.BaseButton(parent=self) self._alembic_path_btn.setIcon(folder_icon) self._alembic_path_btn.setIconSize(QSize(18, 18)) self._alembic_path_btn.setStyleSheet( "background-color: rgba(255, 255, 255, 0); border: 0px solid rgba(255,255,255,0);" ) alembic_path_layout.addWidget(self._alembic_path_line) alembic_path_layout.addWidget(self._alembic_path_btn) buttons_layout.addWidget(alembic_path_lbl, 2, 0, 1, 1, Qt.AlignRight) buttons_layout.addWidget(alembic_path_widget, 2, 1) import_mode_layout = layouts.HorizontalLayout(spacing=2, margins=(2, 2, 2, 2)) import_mode_layout.setContentsMargins(2, 2, 2, 2) import_mode_layout.setSpacing(2) import_mode_widget = QWidget() import_mode_widget.setLayout(import_mode_layout) import_mode_lbl = label.BaseLabel('Import mode: ', parent=self) self._create_radio = buttons.BaseRadioButton('Create', parent=self) self._add_radio = buttons.BaseRadioButton('Add', parent=self) self._merge_radio = buttons.BaseRadioButton('Merge', parent=self) self._create_radio.setChecked(True) import_mode_layout.addWidget(self._create_radio) import_mode_layout.addWidget(self._add_radio) import_mode_layout.addWidget(self._merge_radio) buttons_layout.addWidget(import_mode_lbl, 3, 0, 1, 1, Qt.AlignRight) buttons_layout.addWidget(import_mode_widget, 3, 1) import_mode_lbl.setVisible(False) import_mode_widget.setVisible(False) self._auto_display_lbl = label.BaseLabel('Auto Display Smooth?: ', parent=self) self._auto_smooth_display = checkbox.BaseCheckBox(parent=self) self._auto_smooth_display.setChecked(True) buttons_layout.addWidget(self._auto_display_lbl, 4, 0, 1, 1, Qt.AlignRight) buttons_layout.addWidget(self._auto_smooth_display, 4, 1) if dcc.client().is_maya(): maya_gpu_cache_lbl = label.BaseLabel( 'Import Alembic as GPU Cache?', parent=self) self._maya_gpu_cache_cbx = checkbox.BaseCheckBox(parent=self) self._maya_gpu_cache_cbx.setChecked(True) buttons_layout.addWidget(maya_gpu_cache_lbl, 5, 0, 1, 1, Qt.AlignRight) buttons_layout.addWidget(self._maya_gpu_cache_cbx, 5, 1) elif dcc.client().is_houdini(): hou_archive_abc_node_lbl = label.BaseLabel( 'Import Alembic as Archive?', parent=self) self._hou_archive_abc_node_cbx = checkbox.BaseCheckBox(parent=self) buttons_layout.addWidget(hou_archive_abc_node_lbl, 5, 0, 1, 1, Qt.AlignRight) buttons_layout.addWidget(self._hou_archive_abc_node_cbx, 5, 1) self.main_layout.addStretch() self.main_layout.addLayout(dividers.DividerLayout()) buttons_layout = layouts.HorizontalLayout(spacing=2, margins=(2, 2, 2, 2)) self.main_layout.addLayout(buttons_layout) self._import_btn = buttons.BaseButton('Import', parent=self) self._import_btn.setIcon(resources.icon('import')) self._import_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._reference_btn = buttons.BaseButton('Reference', parent=self) self._reference_btn.setIcon(resources.icon('reference')) self._reference_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) buttons_layout.addWidget(self._import_btn) buttons_layout.addWidget(self._reference_btn) if dcc.client().is_houdini(): self._reference_btn.setEnabled(False)
class OptionsViewer(base.BaseWidget): OPTION_LIST_CLASS = optionlist.OptionList editModeChanged = Signal(bool) def __init__(self, option_object=None, settings=None, parent=None): self._option_object = None self._settings = settings self._edit_mode = False self._current_widgets = list() self._widget_to_copy = None super(OptionsViewer, self).__init__(parent) policy = self.sizePolicy() policy.setHorizontalPolicy(policy.Expanding) policy.setVerticalPolicy(policy.Expanding) self.main_layout.setContentsMargins(2, 2, 2, 2) self.main_layout.setSpacing(2) self.setSizePolicy(policy) if option_object: self.set_option_object(option_object=option_object) def ui(self): super(OptionsViewer, self).ui() edit_mode_icon = resources.icon('edit') move_up_icon = resources.icon('sort_up') move_down_icon = resources.icon('sort_down') remove_icon = resources.icon('delete') self._edit_widget = QWidget() top_layout = layouts.HorizontalLayout() top_layout.setContentsMargins(0, 0, 0, 0) top_layout.setSpacing(2) self._edit_widget.setLayout(top_layout) self.main_layout.addWidget(self._edit_widget) self._edit_mode_btn = buttons.BaseButton(parent=self) self._edit_mode_btn.setIcon(edit_mode_icon) self._edit_mode_btn.setCheckable(True) top_layout.addWidget(self._edit_mode_btn) horizontal_separator = QFrame() horizontal_separator.setFrameShape(QFrame.VLine) horizontal_separator.setFrameShadow(QFrame.Sunken) top_layout.addWidget(horizontal_separator) self._move_up_btn = buttons.BaseButton(parent=self) self.move_down_btn = buttons.BaseButton(parent=self) self.remove_btn = buttons.BaseButton(parent=self) self._move_up_btn.setIcon(move_up_icon) self.move_down_btn.setIcon(move_down_icon) self.remove_btn.setIcon(remove_icon) self._move_up_btn.setVisible(False) self.move_down_btn.setVisible(False) self.remove_btn.setVisible(False) top_layout.addWidget(self._move_up_btn) top_layout.addWidget(self.move_down_btn) top_layout.addWidget(self.remove_btn) top_layout.addStretch() self.main_layout.addWidget(dividers.Divider()) self._scroll = QScrollArea() self._scroll.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._scroll.setFocusPolicy(Qt.NoFocus) self._scroll.setWidgetResizable(True) self.setFocusPolicy(Qt.NoFocus) self._options_list = self.OPTION_LIST_CLASS(parent=self) self._scroll.setWidget(self._options_list) self.main_layout.addWidget(self._scroll) def setup_signals(self): self._edit_mode_btn.toggled.connect(self._on_edit_mode) self._move_up_btn.clicked.connect(self._on_move_up) self.move_down_btn.clicked.connect(self._on_move_down) self.remove_btn.clicked.connect(self._on_remove) def settings(self): """ Returns settings object :return: JSONSettings """ return self._settings def set_settings(self, settings): """ Sets save widget settings :param settings: JSONSettings """ self._settings = settings def get_option_object(self): """ Returns the option object linked to this widget :return: object """ return self._option_object def set_option_object(self, option_object, force_update=True): """ Sets option_object linked to this widget :param option_object: object :param force_update: bool """ self._option_object = option_object self._options_list.set_option_object(option_object) if option_object and force_update: self.update_options() def get_option_type(self): """ Returns option widget type :return: str """ return self._option_type def is_edit_mode(self): """ Returns whether current option is editable or not :return: bool """ return self._edit_mode def set_edit_mode(self, flag): """ Sets whether the current option is editable or not :param flag: bool """ self._on_edit_mode(flag) def is_widget_to_copy(self): """ Returns whether an option widget is being copied or not :return: bool """ return self._widget_to_copy def set_widget_to_copy(self, widget_to_copy): """ Sets widget that we want to copy :param QWidget """ self._widget_to_copy = widget_to_copy def show_edit_widget(self): self._edit_widget.setVisible(True) self._edit_splitter.setVisible(True) def hide_edit_widget(self): self._edit_widget.setVisible(False) self._edit_splitter.setVisible(False) def update_options(self): """ Function that updates the current options of the selected task """ if not self._option_object: self._options_list.clear_widgets() LOGGER.warning( 'Impossible to update options because option object is not defined!' ) return self._options_list.update_options() def clear_options(self): """ Clears all the options """ self._options_list.clear_widgets() if self._option_object: self._option_object = None def has_options(self): """ Checks if the current task has options or not :return: bool """ if not self._option_object: LOGGER.warning( 'Impossible to check options because option object is not defined!' ) return return self._option_object.has_options() def _edit_activate(self, edit_value): """ Internal function that updates widget states when edit button is pressed :param edit_value: bool """ self._edit_mode = edit_value self.move_down_btn.setVisible(edit_value) self._move_up_btn.setVisible(edit_value) self.remove_btn.setVisible(edit_value) if not edit_value: self._options_list.clear_selection() self._options_list.set_edit(edit_value) def _on_edit_mode(self, edit_value): """ Internal callback function that is called when the user presses edit mode button :param edit_value: bool """ self._edit_activate(edit_value) self.editModeChanged.emit(edit_value) def _on_move_up(self): """ Internal callback function that is called when the user pressed move up button Move selected items up in the list """ widgets = self._current_widgets if not widgets: return widgets = self._options_list.sort_widgets(widgets, widgets[0].get_parent()) if not widgets: return for w in widgets: w.move_up() def _on_move_down(self): """ Internal callback function that is called when the user pressed move down button Move selected items down in the list """ widgets = self._current_widgets if not widgets: return widgets = self._options_list.sort_widgets(widgets, widgets[0].get_parent()) if not widgets: return for w in widgets: w.move_down() def _on_remove(self): """ Internal callback function that is called when the user pressed remove button Remove selected options """ widgets = self._current_widgets if not widgets: return widgets = self._options_list.sort_widgets(widgets, widgets[0].get_parent()) if not widgets: return for w in widgets: w.remove()
class CollapsableGroup(BaseGroup, object): def __init__(self, name='', parent=None, collapsable=True): self._collapsable = collapsable super(CollapsableGroup, self).__init__(name, parent, layout_orientation=Qt.Horizontal) def ui(self): super(CollapsableGroup, self).ui() self._base_widget = QWidget() if self._layout_orientation == Qt.Vertical: manager_layout = layouts.VerticalLayout(spacing=2, margins=(4, 4, 4, 4)) else: manager_layout = layouts.HorizontalLayout(spacing=2, margins=(4, 4, 4, 4)) manager_layout.setAlignment(Qt.AlignCenter) self._base_widget.setLayout(manager_layout) self.main_layout.addWidget(self._base_widget) self.main_layout = manager_layout def mousePressEvent(self, event): super(CollapsableGroup, self).mousePressEvent(event) if not event.button() == Qt.LeftButton: return if self._collapsable: if event.y() < 30: if self._base_widget.isHidden(): self.expand_group() else: self.collapse_group() def set_collapsable(self, flag): """ Sets if the group can be collapsed or not :param flag: bool """ self._collapsable = flag def set_title(self, title): if not title.startswith('+ '): title = '+ ' + title self.setTitle(title) def expand_group(self): """ Expands the content of the group """ self.setVisible(True) title = self.title() title = title.replace('+', '-') self.setTitle(title) def collapse_group(self): """ Collapse the content of the group """ self._base_widget.setVisible(False) title = self.title() title = title.replace('-', '+') self.setTitle(title)
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 _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)