class BreadcrumbWidget(base.BaseWidget, object): """ Widget that display current location withing a hierarchy It allows going back/forward inside a hierarchy """ def __init__(self, separator=None, parent=None): super(BreadcrumbWidget, self).__init__(parent=parent) current_theme = self.theme() separator_color = current_theme.accent_color if current_theme else '#E2AC2C' self._separator = separator or "<span style='color:{}'> ▸ </span>".format( separator_color) self._separators = list() self.setObjectName('BreadcrumbWidget') self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) def get_main_layout(self): main_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) main_layout.addStretch() return main_layout def ui(self): super(BreadcrumbWidget, self).ui() self._button_group = QButtonGroup() def set_items(self, data_list): """ Sets the the items of the breadcrumb cleaning old ones :param data_list: :return: """ for btn in self._button_group.buttons(): self._button_group.removeButton(btn) self.main_layout.removeWidget(btn) btn.setVisible(False) btn.deleteLater() for sep in self._separators: self.main_layout.removeWidget(sep) sep.setVisible(False) sep.deleteLater() python.clear_list(self._separators) for index, data_dict in enumerate(data_list): self.add_item(data_dict, index) def add_item(self, data_dict, index=None): """ Adds a new item to the breadcrumb :param data_dict: dict :param index: int """ btn = buttons.BaseToolButton() btn.setText(data_dict.get('text')) if data_dict.get('image'): btn.image(data_dict.get('image')) if data_dict.get('tooltip'): btn.setProperty('toolTip', data_dict.get('tooltip')) if data_dict.get('clicked'): btn.clicked.connect(data_dict.get('clicked')) if data_dict.get('text'): if data_dict.get('svg') or data_dict.get('icon'): btn.text_beside_icon() else: btn.text_only() else: btn.icon_only() if self._button_group.buttons(): separator = label.BaseLabel(self._separator).secondary() self._separators.append(separator) self.main_layout.insertWidget(self.main_layout.count() - 1, separator) self.main_layout.insertWidget(self.main_layout.count() - 1, btn) if index is None: self._button_group.addButton(btn) else: self._button_group.addButton(btn, index) def set_from_path(self, file_path): """ Creates a proper Breadcrumb list for given path and sets the text """ self._widgets = list() file_path = os.path.dirname(file_path) folders = path.get_folders_from_path(file_path) data_list = list() for folder in folders: data_list.append({'text': folder}) self.set_items(data_list) def get_breadcumbs(self): """ Returns current list of breadcumb texts :return: list(str) """ return [btn.text() for btn in self._button_group.buttons()]
class AlembicManagerView(tool.ArtellaToolWidget, object): def __init__(self, project, config, settings, parent): super(AlembicManagerView, self).__init__(project=project, config=config, settings=settings, parent=parent) def ui(self): super(AlembicManagerView, self).ui() export_icon = resources.icon('export') import_icon = resources.icon('import') buttons_layout = layouts.HorizontalLayout(spacing=2, margins=(2, 2, 2, 2)) self.main_layout.addLayout(buttons_layout) self.main_layout.addLayout(dividers.DividerLayout()) self._exporter_btn = buttons.BaseButton('Exporter', parent=self) self._exporter_btn.setIcon(export_icon) self._exporter_btn.setMinimumWidth(80) self._exporter_btn.setCheckable(True) self._importer_btn = buttons.BaseButton('Importer', parent=self) self._importer_btn.setIcon(import_icon) self._importer_btn.setMinimumWidth(80) self._importer_btn.setCheckable(True) buttons_layout.addStretch() buttons_layout.addWidget(self._exporter_btn) buttons_layout.addWidget(self._importer_btn) self._importer_btn.setCheckable(True) self._buttons_grp = QButtonGroup(self) self._buttons_grp.setExclusive(True) self._buttons_grp.addButton(self._exporter_btn) self._buttons_grp.addButton(self._importer_btn) self._exporter_btn.setChecked(True) self._stack = stack.SlidingStackedWidget() self.main_layout.addWidget(self._stack) self._alembic_exporter = exporter.AlembicExporter(project=self.project) self._alembic_importer = importer.AlembicImporter(project=self.project) self._stack.addWidget(self._alembic_exporter) self._stack.addWidget(self._alembic_importer) def setup_signals(self): self._stack.animFinished.connect(self._on_stack_anim_finished) self._exporter_btn.clicked.connect(partial(self._on_slide_stack, 0)) self._importer_btn.clicked.connect(partial(self._on_slide_stack, 1)) self._alembic_exporter.showWarning.connect(self._on_show_warning) self._alembic_exporter.showOk.connect(self._on_show_ok) self._alembic_importer.showOk.connect(self._on_show_ok) def _on_slide_stack(self, index): """ Internal callback function that is called when stack needs to change current widget :param index: int """ if index == self._stack.currentIndex(): return for btn in self._buttons_grp.buttons(): btn.setEnabled(False) self._stack.slide_in_index(index) def _on_stack_anim_finished(self): """ Internal callback function that is called when stack anim finish """ for btn in self._buttons_grp.buttons(): btn.setEnabled(True) if self._stack.currentWidget() == self._alembic_exporter: self._alembic_exporter.refresh() def _on_show_ok(self, warning_msg): """ Internal callback function that is called when an ok message should be showed :param warning_msg: str """ logger.debug(warning_msg) self.show_ok_message(warning_msg) def _on_show_warning(self, warning_msg): """ Internal callback function that is called when a warning message should be showed :param warning_msg: str """ logger.warning(warning_msg) self.show_warning_message(warning_msg)
class BaseButtonGroup(base.BaseWidget, object): def __init__(self, orientation=Qt.Horizontal, parent=None): self._orientation = 'horizontal' if orientation == Qt.Horizontal else 'vertical' super(BaseButtonGroup, self).__init__(parent=parent) def get_main_layout(self): main_layout = QBoxLayout(QBoxLayout.LeftToRight if self._orientation == 'horizontal' else QBoxLayout.TopToBottom) main_layout.setContentsMargins(0, 0, 0, 0) return main_layout def ui(self): super(BaseButtonGroup, self).ui() self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self._button_group = QButtonGroup() @decorators.abstractmethod def create_button(self, data_dict): """ Must be implemented in custom button groups Creates a new button for this group :param data_dict: dict :return: new button instance """ raise NotImplementedError( 'Function create_button for class "{}" is not implemented!'.format( self.__class__.__name__)) def get_button_group(self): """ Returns button group internal object :return: QButtonGroup """ return self._button_group def clear(self): """ Clears all buttons contained in this group """ for btn in self._button_group.buttons(): self._button_group.removeButton(btn) self.main_layout.removeWidget(btn) btn.setVisible(False) btn.deleteLater() def add_button(self, data_dict, index=None): """ Adds a new button to this group :param data_dict: dict :param index: int or None :return: new added button """ if python.is_string(data_dict): data_dict = {'text': data_dict} elif isinstance(data_dict, QIcon): data_dict = {'icon': data_dict} new_btn = self.create_button(data_dict) new_btn.setProperty('combine', self._orientation) if data_dict.get('text'): new_btn.setProperty('text', data_dict.get('text')) if data_dict.get('icon'): new_btn.setProperty('icon', data_dict.get('icon')) if data_dict.get('data'): new_btn.setProperty('data', data_dict.get('data')) if data_dict.get('checked'): new_btn.setProperty('checked', data_dict.get('checked')) if data_dict.get('shortcut'): new_btn.setProperty('shortcut', data_dict.get('shortcut')) if data_dict.get('tooltip'): new_btn.setProperty('toolTip', data_dict.get('tooltip')) if data_dict.get('clicked'): new_btn.clicked.connect(data_dict.get('clicked')) if data_dict.get('toggled'): new_btn.toggled.connect(data_dict.get('toggled')) if index is None: self._button_group.addButton(new_btn) else: self._button_group.addButton(new_btn, index) if self.main_layout.count() == 0: new_btn.setChecked(True) self.main_layout.insertWidget(self.main_layout.count(), new_btn) return new_btn def set_button_list(self, button_list): """ Empties group and add all buttons given in the list of buttons :param button_list: list(dict) """ self.clear() for index, data_dict in enumerate(button_list): new_btn = self.add_button(data_dict=data_dict, index=index) if index == 0: new_btn.setProperty('position', 'left') elif index == len(button_list) - 1: new_btn.setProperty('position', 'right') else: new_btn.setProperty('position', 'center')
class RenamerView(base.BaseWidget, object): def __init__(self, model, controller, parent=None): self._model = model self._controller = controller super(RenamerView, self).__init__(parent=parent) self.refresh() @property def model(self): return self._model @property def controller(self): return self._controller def ui(self): super(RenamerView, self).ui() top_layout = layouts.HorizontalLayout(spacing=2, margins=(2, 2, 2, 2)) top_layout.setAlignment(Qt.AlignLeft) self._buttons_grp = QButtonGroup(self) self._buttons_grp.setExclusive(True) self.main_layout.addLayout(top_layout) self.main_layout.addLayout(dividers.DividerLayout()) self._categories_layout = layouts.HorizontalLayout(spacing=2, margins=(2, 2, 2, 2)) self._categories_layout.setAlignment(Qt.AlignLeft) selection_layout = layouts.HorizontalLayout(spacing=2, margins=(4, 0, 4, 0)) top_layout.addLayout(selection_layout) self._all_radio = buttons.BaseRadioButton('All', parent=self) self._all_radio.setFixedHeight(19) self._all_radio.setAutoExclusive(True) self._selected_radio = buttons.BaseRadioButton('Selected', parent=self) self._selected_radio.setFixedHeight(19) self._selected_radio.setChecked(True) self._selected_radio.setAutoExclusive(True) self._hierarchy_cbx = checkbox.BaseCheckBox('Hierarchy', parent=self) self._hierarchy_cbx.setFixedHeight(19) self._node_types_combo = combobox.BaseComboBox(parent=self) self._auto_rename_shapes_cbx = None self._auto_rename_shapes_cbx = checkbox.BaseCheckBox( 'Auto Rename Shapes', parent=self) self._auto_rename_shapes_cbx.setChecked(True) if not dcc.client().is_maya(): self._auto_rename_shapes_cbx.setVisible(False) selection_layout.addWidget(self._selected_radio) selection_layout.addWidget(self._all_radio) selection_layout.addItem( QSpacerItem(10, 0, QSizePolicy.Fixed, QSizePolicy.Fixed)) selection_layout.addWidget(self._hierarchy_cbx) selection_layout.addItem( QSpacerItem(10, 0, QSizePolicy.Fixed, QSizePolicy.Fixed)) selection_layout.addWidget(self._node_types_combo) if self._auto_rename_shapes_cbx: selection_layout.addItem( QSpacerItem(10, 0, QSizePolicy.Fixed, QSizePolicy.Fixed)) selection_layout.addWidget(self._auto_rename_shapes_cbx) self._splitter = splitter.CollapsibleSplitter(parent=self) self._splitter.setOrientation(Qt.Horizontal) self._splitter.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) self._splitter.setMinimumHeight(750) self.main_layout.addWidget(self._splitter) self._rename_tab = tabs.BaseTabWidget(parent=self) self._splitter.addWidget(self._rename_tab) self._manual_rename_widget = manualrenamewidget.ManualRenameWidget( model=self._model, controller=self._controller, parent=self) self._auto_rename_widget = autorenamewidget.AutoRenameWidget( model=self._model, controller=self._controller, parent=self) self._rename_tab.addTab(self._manual_rename_widget, 'Manual') self._rename_tab.addTab(self._auto_rename_widget, 'Auto') self._stack = stack.SlidingStackedWidget() # splitter_right_widget = QWidget() # splitter_right_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) # splitter_right_layout.addLayout(self._categories_layout) # splitter_right_layout.addWidget(self._stack) # splitter_right_widget.setLayout(splitter_right_layout) # self._splitter.addWidget(splitter_right_widget) # # no_items_widget = QFrame() # no_items_widget.setFrameShape(QFrame.StyledPanel) # no_items_widget.setFrameShadow(QFrame.Sunken) # no_items_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) # no_items_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) # no_items_widget.setLayout(no_items_layout) # no_items_lbl = label.BaseLabel() # no_items_pixmap = tp.ResourcesMgr().pixmap('no_items') # no_items_lbl.setPixmap(no_items_pixmap) # no_items_lbl.setAlignment(Qt.AlignCenter) # no_items_layout.addItem(QSpacerItem(0, 10, QSizePolicy.Preferred, QSizePolicy.Expanding)) # no_items_layout.addWidget(no_items_lbl) # no_items_layout.addItem(QSpacerItem(0, 10, QSizePolicy.Preferred, QSizePolicy.Expanding)) # # self._stack.addWidget(no_items_widget) self._splitter.handle(0).collapse() self._splitter.setSizes([1, 0]) def setup_signals(self): self._stack.animFinished.connect(self._on_stack_anim_finished) self._rename_tab.currentChanged.connect(self._on_tab_changed) self._selected_radio.clicked.connect(self._controller.set_selected) self._all_radio.clicked.connect(self._controller.set_all_selection) self._hierarchy_cbx.toggled.connect( self._controller.hierarchy_check_toggle) self._node_types_combo.currentIndexChanged.connect( self._controller.set_filter_type) self._auto_rename_shapes_cbx.toggled.connect( self._controller.auto_rename_shapes_check_toggle) self._model.hierarchyCheckChanged.connect( self._on_toggle_hierarchy_cbx) self._model.renameShapeChanged.connect( self._on_toggle_auto_rename_shape_cbx) self._model.filterTypeChanged.connect(self._on_filter_type_changed) def refresh(self): """ Syncs view to the current state of its model """ self._hierarchy_cbx.setChecked(self._model.hierarchy_check) self._node_types_combo.clear() for btn in self._buttons_grp.buttons(): self._buttons_grp.removeButton(btn) node_types = self._model.node_types if not node_types: self._node_types_combo.setVisible(False) else: self._node_types_combo.setVisible(True) for node_type in node_types: self._node_types_combo.addItem(str(node_type).split('.')[-1]) categories = self._model.categories or dict() nodes_to_discard = self._model.nodes_to_discard types_to_discard = self._model.types_to_discard for node_type in types_to_discard: nodes_to_discard.extend(dcc.list_nodes(node_type=node_type)) for i, category in enumerate(categories): for category_name, category_data in category.items(): title = category_data.get('title', category) icon = category_data.get('icon', None) types = category_data.get('types', dict()) category_btn = buttons.BaseButton(title) category_btn.setCheckable(True) if icon: category_btn.setIcon(resources.icon(icon)) if i == 0: category_btn.setChecked(True) self._buttons_grp.addButton(category_btn) self._categories_layout.addWidget(category_btn) category_widget = categorywidget.CategoryWidget( types=types, nodes_to_discard=nodes_to_discard) self._stack.addWidget(category_widget) # category_widget.doRefresh.connect(self._on_refresh_category) # category_widget.doPreview.connect(self._set_preview_names) # category_widget.togglePreview.connect(self.update_current_items) # category_widget.doRename.connect(self._on_rename) # category_btn.clicked.connect(partial(self._on_category_selected, i + 1)) self._auto_rename_widget.refresh() self._controller.update_rules() def _on_stack_anim_finished(self, index): """ Internal callback function that is called when stack animation is completed :param index: :return: """ for btn in self._buttons_grp.buttons(): btn.setEnabled(True) category_widget = self._stack.current_widget if not category_widget: return # self._on_refresh_category(category_widget) def _on_tab_changed(self, tab_index): """ Internal callback function that is called when tab widget is changed by the user :param tab_index: int """ if tab_index != 1: return self._controller.update_rules() def _on_toggle_hierarchy_cbx(self, flag): """ Internal callback function called when the user toggles hierarchy check box :param flag: bool """ self._hierarchy_cbx.setChecked(flag) def _on_toggle_auto_rename_shape_cbx(self, flag): """ Internal callback function called when the user toggles auto rename shape check box :param flag: bool """ self._auto_rename_shapes_cbx.setChecked(flag) def _on_filter_type_changed(self, value): """ Internal callback function that is called when user changes the filter type combo box :param value: str """ self._node_types_combo.setCurrentText(value)