def __init__(self, *args, **kwargs): super(ButtonGroupFieldWidget, self).__init__(*args, **kwargs) self._value = '' self._buttons = dict() items = self.data().get('items') widget = QFrame(self) layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) widget.setLayout(layout) index = 0 for item in items: index += 1 button = buttons.BaseButton(item, parent=self) button.setCheckable(True) button_callback = partial(self.set_value, item) button.clicked.connect(button_callback) self._buttons[item] = button if index == 1: button.setProperty('first', True) if index == len(items): button.setProperty('last', True) self.set_widget(widget)
def createWidget(self, menu): """ Overrides base QWidgetAction createWidget function :param menu: QMenu """ widget = QFrame(self.parent()) widget.setObjectName('filterByAction') title = self._name label = checkbox.BaseCheckBox(parent=widget) label.setText(title) label.setAttribute(Qt.WA_TransparentForMouseEvents) label.toggled.connect(self._on_triggered) label.setStyleSheet(""" #QCheckBox::indicator:checked { image: url(none.png) } QCheckBox::indicator:unchecked { image: url(none.png) } """) action_layout = layouts.HorizontalLayout(margins=(0, 0, 0, 0)) action_layout.addWidget(label, stretch=1) widget.setLayout(action_layout) return widget
def createWidget(self, menu): """ Overrides base QWidgetAction createWidget function :param menu: QMenu :return: QWidget """ widget = QFrame(self.parent()) widget.setObjectName('filterByAction') facet = self._facet name = facet.get('name', '') count = str(facet.get('count', 0)) title = name.replace('.', '').title() cbx = checkbox.BaseCheckBox(parent=widget) cbx.setAttribute(Qt.WA_TransparentForMouseEvents) cbx.setText(title) cbx.installEventFilter(self) cbx.setChecked(self._checked) label2 = label.BaseLabel(parent=widget) label2.setObjectName('actionCounter') label2.setText(count) layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) layout.addWidget(cbx, stretch=1) layout.addWidget(label2) widget.setLayout(layout) cbx.toggled.connect(self._on_triggered) return widget
def createWidget(self, menu): widget = QFrame(menu) widget.setObjectName('iconPickerAction') action_layout = layouts.HorizontalLayout(margins=(0, 0, 0, 0)) action_layout.addWidget(self.picker(), stretch=1) widget.setLayout(action_layout) return widget
def ui(self): super(BaseSaveWidget, self).ui() title_layout = layouts.HorizontalLayout() title_layout.setContentsMargins(2, 2, 0, 0) title_layout.setSpacing(2) self._icon_lbl = QLabel() self._icon_lbl.setMaximumSize(QSize(14, 14)) self._icon_lbl.setMinimumSize(QSize(14, 14)) self._icon_lbl.setScaledContents(True) self._title_lbl = QLabel() title_layout.addWidget(self._icon_lbl) title_layout.addWidget(self._title_lbl) self._folder_widget = directory.SelectFolder('Folder', use_app_browser=True) buttons_layout = layouts.HorizontalLayout() buttons_layout.setContentsMargins(4, 4, 4, 4) buttons_layout.setSpacing(4) buttons_frame = QFrame() buttons_frame.setFrameShape(QFrame.NoFrame) buttons_frame.setFrameShadow(QFrame.Plain) buttons_frame.setLayout(buttons_layout) buttons_layout.addStretch() self.save_btn = buttons.BaseButton('Save') self.cancel_btn = buttons.BaseButton('Cancel') buttons_layout.addWidget(self.save_btn, parent=self) buttons_layout.addWidget(self.cancel_btn, parent=self) buttons_layout.addStretch() self._options_layout = layouts.VerticalLayout() self._options_layout.setContentsMargins(0, 0, 0, 0) self._options_layout.setSpacing(2) self._options_frame = QFrame() self._options_frame.setFrameShape(QFrame.NoFrame) self._options_frame.setFrameShadow(QFrame.Plain) self._options_frame.setLineWidth(0) self._options_frame.setLayout(self._options_layout) self._extra_layout = layouts.VerticalLayout() self._extra_layout.setContentsMargins(0, 0, 0, 0) self._extra_layout.setSpacing(2) self.main_layout.addLayout(title_layout) self.main_layout.addWidget(self._folder_widget) self._extra_layout.addWidget(self._options_frame) self.main_layout.addWidget(dividers.Divider()) self.main_layout.addLayout(self._extra_layout) self.main_layout.addWidget(dividers.Divider()) self.main_layout.addWidget(buttons_frame)
def __init__(self, *args, **kwargs): super(StringDoubleFieldWidget, self).__init__(*args, **kwargs) widget = QFrame(self) layout = layouts.HorizontalLayout(spacing=4, margins=(0, 0, 0, 0)) widget.setLayout(layout) self._widget1 = lineedit.BaseLineEdit(parent=self) self._widget2 = lineedit.BaseLineEdit(parent=self) self._widget1.textChanged.connect(self._on_emit_value_changed) self._widget2.textChanged.connect(self._on_emit_value_changed) layout.addWidget(self._widget1) layout.addWidget(self._widget2) self.set_widget(widget)
def ui(self): super(DoubleSpinBoxAxis, self).ui() axis_widget = QFrame(parent=self) axis_widget.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) axis_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) axis_widget.setLayout(axis_layout) self._axis_btn = buttons.get_axis_button(axis_type=self._axis, parent=self) self._line = BaseDoubleSpinBox(parent=self) self._line.setRange(self._min, self._max) self._line.setValue(self._start) axis_layout.addWidget(self._axis_btn) axis_layout.addWidget(self._line) self.main_layout.addWidget(axis_widget)
def __init__(self, *args, **kwargs): super(RangeFieldWidget, self).__init__(*args, **kwargs) widget = QFrame(self) layout = layouts.HorizontalLayout(spacing=4, margins=(0, 0, 0, 0)) widget.setLayout(layout) validator = QIntValidator(-50000000, 50000000, self) self._min_widget = lineedit.BaseLineEdit(parent=self) self._min_widget.setValidator(validator) self._min_widget.textChanged.connect(self._on_emit_value_changed) widget.layout().addWidget(self._min_widget) self._max_widget = lineedit.BaseLineEdit(parent=self) self._max_widget.setValidator(validator) self._max_widget.textChanged.connect(self._on_emit_value_changed) widget.layout().addWidget(self._max_widget) self.set_widget(widget)
def ui(self): super(Project, self).ui() self.setMaximumWidth(qtutils.dpi_scale(160)) self.setMaximumHeight(qtutils.dpi_scale(200)) widget_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) main_frame = QFrame() main_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) main_frame.setLineWidth(1) main_frame.setLayout(widget_layout) self.main_layout.addWidget(main_frame) self.project_btn = QPushButton('', self) self.project_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.project_btn.setIconSize(QSize(120, 120)) project_lbl = label.BaseLabel(self.name, parent=self) project_lbl.setObjectName('projectLabel') project_lbl.setAlignment(Qt.AlignCenter) widget_layout.addWidget(self.project_btn) widget_layout.addWidget(project_lbl)
def ui(self): super(Template, self).ui() self.setMaximumWidth(160) self.setMaximumHeight(200) widget_layout = layouts.VerticalLayout(spacing=0, margins=(2, 2, 2, 2)) main_frame = QFrame() main_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) main_frame.setLineWidth(1) main_frame.setLayout(widget_layout) self.main_layout.addWidget(main_frame) self.template_btn = QPushButton('', self) self.template_btn.setCheckable(True) self.template_btn.setIcon(self.get_icon()) self.template_btn.setIconSize(QSize(120, 120)) template_lbl = label.BaseLabel(self.name, parent=self) template_lbl.setObjectName('templateLabel') template_lbl.setAlignment(Qt.AlignCenter) widget_layout.addWidget(self.template_btn) widget_layout.addWidget(template_lbl)
def ui(self): super(PopupMessage, self).ui() current_theme = self.theme() self.setObjectName('message') self.setWindowFlags(Qt.FramelessWindowHint | Qt.Dialog | Qt.WA_TranslucentBackground | Qt.WA_DeleteOnClose) # self.setAttribute(Qt.WA_TranslucentBackground) if self._theme_type == MessageTypes.LOADING: icon_label = loading.CircleLoading.tiny(parent=self) else: icon_label = avatar.Avatar.tiny() current_type = self._theme_type or MessageTypes.INFO if current_theme: icon_label.image = resources.pixmap( current_type, color=getattr(current_theme, '{}_color'.format(current_type))) main_frame = QFrame(self) main_frame_layout = layouts.HorizontalLayout(spacing=5, margins=(5, 5, 5, 5)) main_frame.setLayout(main_frame_layout) self.main_layout.addWidget(main_frame) self._content_label = label.BaseLabel(parent=self) self._content_label.setText(self._text) self._close_btn = buttons.BaseToolButton(parent=self).image( 'close', theme='window').icon_only().tiny() self._close_btn.setVisible(self._closable or False) main_frame_layout.addWidget(icon_label) main_frame_layout.addWidget(self._content_label) main_frame_layout.addStretch() main_frame_layout.addWidget(self._close_btn)
def ui(self): super(DragDoubleSpinBoxLineAxis, self).ui() axis_widget = QFrame() axis_widget.setFrameStyle(QFrame.StyledPanel | QFrame.Raised) axis_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) axis_widget.setLayout(axis_layout) self._axis_btn = buttons.get_axis_button(axis_type=self._axis, parent=self) self._line = DragDoubleSpinBoxLine(start=self._start, max=self._max, min=self._min, positive=self._positive, parent=self) self._reset_btn = buttons.BaseToolButton( parent=self).image('reset').icon_only() self._reset_btn.setVisible(self._reset) self._reset_btn.setEnabled(self._reset) axis_layout.addWidget(self._axis_btn) axis_layout.addWidget(self._line) axis_layout.addWidget(self._reset_btn) self.main_layout.addWidget(axis_widget)
def ui(self): super(NameIt, self).ui() tools_toolbar = toolbar.ToolBar('Main ToolBar', parent=self) tools_toolbar.setMovable(False) # tools_toolbar.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea) self.main_layout.addWidget(tools_toolbar) refresh_btn = buttons.BaseToolButton(parent=tools_toolbar) refresh_btn.setIcon(resources.icon('refresh')) refresh_btn.clicked.connect(self._on_refresh) self._refresh_btn = tools_toolbar.addWidget(refresh_btn) self._refresh_btn.setEnabled(False) save_btn = buttons.BaseToolButton(parent=tools_toolbar) save_btn.setIcon(resources.icon('save')) save_btn.clicked.connect(self._on_save) self._save_btn = tools_toolbar.addWidget(save_btn) self._save_btn.setEnabled(False) if self._is_renamer_tool_available(): renamer_btn = buttons.BaseToolButton(parent=tools_toolbar) renamer_btn.setIcon(resources.icon('rename')) renamer_btn.clicked.connect(self._on_open_renamer_tool) tools_toolbar.addWidget(renamer_btn) self._name_file_line = directory.SelectFile(label_text='Naming File', parent=tools_toolbar) tools_toolbar.addWidget(self._name_file_line) base_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self.main_layout.addLayout(base_layout) panels_splitter = QSplitter(parent=self) panels_splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) base_layout.addWidget(panels_splitter) left_panel_widget = QWidget() left_panel_layout = layouts.VerticalLayout(margins=(5, 0, 5, 0)) left_panel_widget.setLayout(left_panel_layout) panels_splitter.addWidget(left_panel_widget) # Tab Widget rules_tab = QWidget() tokens_tab = QWidget() templates_tab = QWidget() templates_tokens_tab = QWidget() self.tabs = tabs.BaseTabWidget() self.tabs.addTab(rules_tab, 'Rules') self.tabs.addTab(tokens_tab, 'Tokens') self.tabs.addTab(templates_tab, 'Templates') self.tabs.addTab(templates_tokens_tab, 'Templates Tokens') left_panel_layout.addWidget(self.tabs) # Rules Tab rules_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.rules_list = QListWidget() rules_main_layout.addWidget(self.rules_list) left_panel_buttons_layout_rules = layouts.HorizontalLayout(margins=(5, 5, 5, 0)) rules_main_layout.addLayout(left_panel_buttons_layout_rules) self.add_rule_btn = buttons.BaseButton(parent=self) self.remove_rule_btn = buttons.BaseButton(parent=self) self.add_rule_btn.setIcon(resources.icon('plus')) self.remove_rule_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_rules.addStretch() left_panel_buttons_layout_rules.addWidget(self.add_rule_btn) left_panel_buttons_layout_rules.addWidget(self.remove_rule_btn) rules_tab.setLayout(rules_main_layout) # Tokens Tab tokens_main_layout = layouts.VerticalLayout(margins=(5, 5, 5, 5)) tokens_main_layout.setSpacing(0) self.tokens_list = QListWidget() tokens_main_layout.addWidget(self.tokens_list) left_panel_buttons_layout_tokens = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) tokens_main_layout.addLayout(left_panel_buttons_layout_tokens) self.add_token_btn = buttons.BaseButton(parent=self) self.remove_token_btn = buttons.BaseButton(parent=self) self.add_token_btn.setIcon(resources.icon('plus')) self.remove_token_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_tokens.addStretch() left_panel_buttons_layout_tokens.addWidget(self.add_token_btn) left_panel_buttons_layout_tokens.addWidget(self.remove_token_btn) tokens_tab.setLayout(tokens_main_layout) # Templates Tab templates_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.templates_list = QListWidget() templates_main_layout.addWidget(self.templates_list) left_panel_buttons_layout_templates = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) templates_main_layout.addLayout(left_panel_buttons_layout_templates) self.add_template_btn = buttons.BaseButton(parent=self) self.remove_template_btn = buttons.BaseButton(parent=self) self.add_template_btn.setIcon(resources.icon('plus')) self.remove_template_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_templates.addStretch() left_panel_buttons_layout_templates.addWidget(self.add_template_btn) left_panel_buttons_layout_templates.addWidget(self.remove_template_btn) templates_tab.setLayout(templates_main_layout) # Template Tokens Tab templates_tokens_main_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) self.template_tokens_list = QListWidget() templates_tokens_main_layout.addWidget(self.template_tokens_list) left_panel_buttons_layout_templates_tokens = layouts.HorizontalLayout( margins=(5, 5, 5, 0)) left_panel_buttons_layout_templates_tokens.setContentsMargins( 5, 5, 5, 0) templates_tokens_main_layout.addLayout( left_panel_buttons_layout_templates_tokens) self.add_template_token_btn = buttons.BaseButton(parent=self) self.remove_template_token_btn = buttons.BaseButton(parent=self) self.add_template_token_btn.setIcon(resources.icon('plus')) self.remove_template_token_btn.setIcon(resources.icon('minus')) left_panel_buttons_layout_templates_tokens.addStretch() left_panel_buttons_layout_templates_tokens.addWidget( self.add_template_token_btn) left_panel_buttons_layout_templates_tokens.addWidget( self.remove_template_token_btn) templates_tokens_tab.setLayout(templates_tokens_main_layout) # === PROPERTIES === # main_group = QGroupBox('Properties') panels_splitter.addWidget(main_group) self.group_layout = layouts.VerticalLayout(spacing=0, margins=(5, 5, 5, 5)) main_group.setLayout(self.group_layout) # Rules Panel self.rules_widget = QWidget(self) rules_layout = layouts.GridLayout() self.rules_widget.setLayout(rules_layout) expression_lbl = label.BaseLabel('Expression', parent=self) self.expression_line = lineedit.BaseLineEdit(parent=self) self.expression_btn = buttons.BaseButton(' <', parent=self) self.expression_btn.setEnabled(False) self.expression_btn.setStyleSheet( "QPushButton::menu-indicator{image:url(none.jpg);}") self.expression_menu = QMenu(self) self.expression_btn.setMenu(self.expression_menu) iterator_lbl = label.BaseLabel('Iterator', parent=self) self.iterator_cbx = combobox.BaseComboBox(parent=self) for it_format in [ '@', '@^', '#', '##', '###', '####', '#####', 'None' ]: self.iterator_cbx.addItem(it_format) description_rule_lbl = label.BaseLabel('Description', parent=self) self.description_rule_text = QTextEdit(parent=self) self.group_layout.addWidget(self.rules_widget) rules_layout.addWidget(expression_lbl, 0, 0, Qt.AlignRight) rules_layout.addWidget(self.expression_line, 0, 1) rules_layout.addWidget(self.expression_btn, 0, 2) rules_layout.addWidget(iterator_lbl, 1, 0, Qt.AlignRight | Qt.AlignTop) rules_layout.addWidget(self.iterator_cbx, 1, 1, 1, 2) rules_layout.addWidget(description_rule_lbl, 2, 0, Qt.AlignRight | Qt.AlignTop) rules_layout.addWidget(self.description_rule_text, 2, 1, 1, 2) # Tokens Panel self.tokens_widget = QWidget(parent=self) tokens_layout = layouts.GridLayout() self.tokens_widget.setLayout(tokens_layout) values_lbl = label.BaseLabel('Values') data = {'key': list(), 'value': list()} self.values_table = TokensTable(data, 0, 2) self.values_table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) values_buttons_layout = layouts.HorizontalLayout(margins=(5, 5, 5, 0)) self.add_key_value_btn = buttons.BaseButton(parent=self) self.remove_key_value_btn = buttons.BaseButton(parent=self) self.add_key_value_btn.setIcon(resources.icon('plus')) self.remove_key_value_btn.setIcon(resources.icon('minus')) values_buttons_layout.addWidget(self.add_key_value_btn) values_buttons_layout.addWidget(self.remove_key_value_btn) values_buttons_layout.addStretch() default_lbl = label.BaseLabel('Default', parent=self) self.default_cbx = combobox.BaseComboBox(parent=self) description_tokens_lbl = label.BaseLabel('Description', parent=self) self.description_token_text = QTextEdit(parent=self) self.group_layout.addWidget(self.tokens_widget) self.tokens_widget.hide() tokens_layout.addWidget(values_lbl, 0, 0, Qt.AlignRight | Qt.AlignTop) tokens_layout.addWidget(self.values_table, 0, 1, 2, 1) tokens_layout.addLayout(values_buttons_layout, 0, 2) tokens_layout.addWidget(default_lbl, 4, 0, Qt.AlignRight) tokens_layout.addWidget(self.default_cbx, 4, 1, 1, 2) tokens_layout.addWidget(description_tokens_lbl, 5, 0, Qt.AlignRight | Qt.AlignTop) tokens_layout.addWidget(self.description_token_text, 5, 1, 1, 2) # Templates Panel self.templates_widget = QWidget(parent=self) templates_layout = layouts.VerticalLayout() self.templates_widget.setLayout(templates_layout) pattern_layout = layouts.HorizontalLayout(spacing=5, margins=(5, 5, 5, 5)) pattern_lbl = label.BaseLabel('Pattern: ', parent=self) self.pattern_line = lineedit.BaseLineEdit(parent=self) pattern_layout.addWidget(pattern_lbl) pattern_layout.addWidget(self.pattern_line) templates_layout.addLayout(pattern_layout) templates_layout.addLayout(dividers.DividerLayout()) self.template_tokens_layout = layouts.GridLayout() self.template_tokens_layout.setAlignment(Qt.AlignTop) template_tokens_frame = QFrame(parent=self) template_tokens_frame.setFrameShape(QFrame.StyledPanel) template_tokens_frame.setFrameShadow(QFrame.Sunken) template_tokens_frame.setLayout(self.template_tokens_layout) templates_layout.addWidget(template_tokens_frame) self.group_layout.addWidget(self.templates_widget) self.templates_widget.hide() # Templates Tokens Panel self.templates_tokens_widget = QWidget(parent=self) templates_tokens_layout = layouts.VerticalLayout() self.templates_tokens_widget.setLayout(templates_tokens_layout) description_templates_token_layout = layouts.HorizontalLayout( spacing=5, margins=(5, 5, 5, 5)) description_tokens_layout = layouts.VerticalLayout() description_templates_token_lbl = label.BaseLabel('Description: ', parent=self) description_tokens_layout.addWidget(description_templates_token_lbl) description_tokens_layout.addStretch() self.description_templates_token_text = QTextEdit(parent=self) description_templates_token_layout.addLayout(description_tokens_layout) description_templates_token_layout.addWidget( self.description_templates_token_text) templates_tokens_layout.addLayout(description_templates_token_layout) self.group_layout.addWidget(self.templates_tokens_widget) self.templates_tokens_widget.hide() # Initialize database self._init_db() # First update of the UI self.update_expression_menu() self.update_tokens_properties_state() self.update_rules_properties_state() self.update_templates_properties_state()
class SidebarWidget(base.BaseWidget): itemDropped = Signal(object) itemRenamed = Signal(str, str) settingsMenuRequested = Signal(object) def __init__(self, parent=None): self._library = None self._previous_filter_text = '' super(SidebarWidget, self).__init__(parent=parent) # ============================================================================================================ # OVERRIDES # ============================================================================================================ def get_main_layout(self): return layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) def ui(self): super(SidebarWidget, self).ui() self._title_widget = QFrame(self) title_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._title_widget.setLayout(title_layout) buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._title_button = buttons.BaseButton(parent=self) self._title_button.setIcon(resources.icon('reset')) self._menu_button = buttons.BaseButton(parent=self) self._menu_button.setIcon(resources.icon('menu_dots')) buttons_layout.addWidget(self._title_button) buttons_layout.addStretch() buttons_layout.addWidget(self._menu_button) self._filter_search = search.SearchFindWidget(parent=self) self._filter_search.setVisible(False) title_layout.addLayout(buttons_layout) title_layout.addWidget(self._filter_search) self._tree_widget = SidebarTree(self) self._tree_widget.installEventFilter(self) self._tree_widget.itemDropped = self.itemDropped self._tree_widget.itemRenamed = self.itemRenamed self.itemSelectionChanged = self._tree_widget.itemSelectionChanged self._filter_search.set_text(self._tree_widget.filter_text()) self.main_layout.addWidget(self._title_widget) self.main_layout.addWidget(self._tree_widget) def setup_signals(self): self._title_button.clicked.connect(self.clear_selection) self._menu_button.clicked.connect(self._on_show_settings_menu) self._filter_search.textChanged.connect(self._on_search_changed) def eventFilter(self, obj, event): if event.type() == QEvent.KeyPress: text = event.text().strip() if text.isalpha() or text.isdigit(): if text and not self._filter_search.search_line.hasFocus(): self._filter_search.set_text(text) self.set_filter_visible(True) self._previous_filter_text = text return super(SidebarWidget, self).eventFilter(obj, event) # ============================================================================================================ # BASE # ============================================================================================================ def library(self): """ Returns the library model for the menu :return: Library """ return self._library def set_library(self, library): """ Set the library model for the menu :param library: Library """ if library == self._library: return self._library = library # self._library.dataChanged.connect(self._on_data_changed) # self._on_data_changed() def set_filter_visible(self, flag): """ Sets whether or not filter widget is visible :param flag: bool """ self._filter_search.setVisible(flag) self._filter_search.search_line.setFocus() if not flag and bool(self._tree_widget.filter_text()): self._tree_widget.set_filter_text('') else: self.refresh_filter() def search(self): if not self.library(): LOGGER.info('No library found for sidebar widget') return self.library().add_query(self.query()) self.library().search() def query(self): """ Returns the query for the sidebar widget :return: dict """ filters = list() for path in self.selected_paths(): if self.is_recursive(): filter_ = ('path', 'startswith', path + '/') filters.append(filter_) filter_ = ('path', 'not', path) filters.append(filter_) unique_name = 'sidebar_widget_' + str(id(self)) return {'name': unique_name, 'operator': 'and', 'filters': filters} # ============================================================================================================ # TREE HELPERS FUNCTIONS # ============================================================================================================ def tree_widget(self): return self._tree_widget def set_dpi(self, dpi): self._tree_widget.set_dpi(dpi) def is_root_visible(self): return self._tree_widget.is_root_visible() def set_root_visible(self, flag): self._tree_widget.set_root_visible(flag) def filter_text(self): return self._tree_widget.filter_text() def set_filter_text(self, text): self._filter_search.set_text(text) def refresh_filter(self): self._tree_widget.set_filter_text(self._filter_search.get_text()) def is_filter_visible(self): return bool(self._tree_widget.filter_text()) or self._filter_search.isVisible() def icons_visible(self): return self._tree_widget.icons_visible() def set_icons_visible(self, flag): self._tree_widget.set_icons_visible(flag) def is_recursive(self): return self._tree_widget.is_recursive() def set_recursive(self, flag): self._tree_widget.set_recursive(flag) def is_locked(self): return self._tree_widget.is_locked() def set_locked(self, flag): self._tree_widget.set_locked(flag) def set_data(self, *args, **kwargs): self._tree_widget.set_data(*args, **kwargs) def set_item_data(self, item_id, item_data): self._tree_widget.set_path_settings(item_id, item_data) def selected_path(self): return self._tree_widget.selected_path() def selected_paths(self): return self._tree_widget.selected_paths() def select_paths(self, paths): self._tree_widget.select_paths(paths) def clear_selection(self): self._tree_widget.clearSelection() # ============================================================================================================ # SETTINGS # ============================================================================================================ def settings(self): """ Returns sidebar widget settings :return: dict """ settings = self._tree_widget.settings() settings['filterText'] = self.filter_text() settings['filterVisible'] = self.is_filter_visible() return settings def set_settings(self, settings_dict): """ Sets sidebar widget settings :param settings_dict: dict """ value = settings_dict.get('filterText') if value is not None: self.set_filter_text(value) self._tree_widget.set_settings(settings_dict) value = settings_dict.get('filterVisible') if value is not None: self.set_filter_visible(value) # ============================================================================================================ # INTERNAL # ============================================================================================================ def _filter_visible_trigger(self, flag): """ Internal function that sets filter visible """ self.set_filter_visible(flag) self._filter_search.select_all() def _create_settings_menu(self, settings_menu): """ Internal function that creates sidebar settings context menu :param settings_menu: Menu :return: Menu """ show_filter_action = settings_menu.addAction(resources.icon('filter'), 'Show Filter') show_filter_action.setCheckable(True) show_filter_action.setChecked(self.is_filter_visible()) show_filter_action_callback = partial(self._filter_visible_trigger, not self.is_filter_visible()) show_filter_action.triggered.connect(show_filter_action_callback) show_icons_action = settings_menu.addAction(resources.icon('icons'), 'Show Icons') show_icons_action.setCheckable(True) show_icons_action.setChecked(self.icons_visible()) show_icons_action_callback = partial(self.set_icons_visible, not self.icons_visible()) show_icons_action.triggered.connect(show_icons_action_callback) show_root_folder_action = settings_menu.addAction(resources.icon('folder_tree'), 'Show Root Folder') show_root_folder_action.setCheckable(True) show_root_folder_action.setChecked(self.is_root_visible()) show_root_folder_action_callback = partial(self.set_root_visible, not self.is_root_visible()) show_root_folder_action.triggered.connect(show_root_folder_action_callback) return settings_menu # ============================================================================================================ # CALLBACKS # ============================================================================================================ def _on_data_changed(self): """ Internal callback function that is triggered when the library data changes """ pass def _on_search_changed(self, text): """ Internal callback function called when the search filter has changed :param text: str """ self.refresh_filter() if text: self.set_filter_visible(True) else: self._tree_widget.setFocus() self.set_filter_visible(False) def _on_show_settings_menu(self): settings_menu = menu.Menu(self) self.settingsMenuRequested.emit(settings_menu) self._create_settings_menu(settings_menu) point = QCursor.pos() point.setX(point.x() + 3) point.setY(point.y() + 3) settings_menu.exec_(point) settings_menu.close()
class StackItem(QFrame, object): updateRequested = Signal() def __init__(self, title, parent, icon=None, title_editable=True, icon_size=12, title_frame=None, show_item_icon=True): super(StackItem, self).__init__(parent) self._stack_widget = parent self._icon_size = icon_size self._title = title self._title_editable = title_editable self._icon = icon self._color = consts.DARK_BG_COLOR self._contents_margins = (0, 0, 0, 0) self._contents_spacing = 0 self._title_frame = title_frame self._show_item_icon = show_item_icon self.ui() self.setup_signals() @property def contents_layout(self): return self._contents_layout def ui(self): # theme = tpDcc.ToolsMgr().get_tool_theme('tpDcc-tools-hub') # border_width = qtutils.dpi_scale_divide(theme.STACK_BORDER_WIDTH) margin = qtutils.dpi_scale_divide(1) self.main_layout = layouts.VerticalLayout(margins=(margin, margin, margin, margin), spacing=0) # self.main_layout = layouts.VerticalLayout(margins=(0, 0, 0, 0), spacing=0) self.setLayout(self.main_layout) if not self._title_frame: self._title_frame = StackTitleFrame( title=self._title, icon=self._icon, title_editable=self._title_editable, item_icon_size=self._icon_size) if not self._show_item_icon: self._title_frame.item_icon_button.hide() self._contents_widget = QFrame(parent=self) self._contents_layout = layouts.VerticalLayout( spacing=self._contents_spacing, margins=self._contents_margins) self._contents_widget.setLayout(self._contents_layout) self.main_layout.addWidget(self._title_frame) self.main_layout.addWidget(self._contents_widget) def setup_signals(self): self._title_frame.updateRequested.connect(self.updateRequested.emit) def title_text_widget(self): """ Returns title text widget :return: QLineEdit """ return self._title_frame.line_edit def get_title(self): """ Returns title text :return: str """ return self._title_frame.line_edit.text() def set_title(self, text): """ Function that sets title text :param text: str """ self._title_frame.line_edit.setText(text) def add_widget(self, widget): """ Adds a new widget to the contents layout :param widget: QWidget """ self._contents_layout.addWidget(widget) def add_layout(self, layout): """ Adds a new layout to the contents layout :param layout: QLayout """ self._contents_layout.addLayout(layout) def set_title_text_mouse_transparent(self, flag): """ Sets whether or not title text mouse is transparent :param flag: bool """ self._title_frame.line_edit.setAttribute( Qt.WA_TransparentForMouseEvents, flag) def set_item_icon_color(self, color): """ Sets the color of the item in title :param color: tuple(int, int, int), RGB color in 0-255 range """ self._title_frame.set_item_icon_color(color) def set_item_icon(self, icon): """ Sets the icon of the item in title :param icon: QIcon """ self.title_frame.set_item_icon(icon) def update_size(self): """ Updates the size of the widget, to fit to new size depending on its contents """ self.updateRequested.emit()
class PreviewWidget(base.BaseWidget, object): def __init__(self, item_view, parent=None): self._item_view = item_view self._sequence_widget = None self._form_widget = None super(PreviewWidget, self).__init__(parent=parent) self._create_sequence_widget() # ============================================================================================================ # OVERRIDES # ============================================================================================================ def get_main_layout(self): main_layout = layouts.VerticalLayout(spacing=2, margins=(0, 0, 0, 0)) main_layout.setAlignment(Qt.AlignTop) return main_layout def ui(self): super(PreviewWidget, self).ui() title_frame = QFrame(self) title_frame_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) title_frame.setLayout(title_frame_layout) title_widget = QWidget(self) title_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_widget.setLayout(title_layout) buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) title_layout.addLayout(buttons_layout) self._title_icon = label.BaseLabel(parent=self) self._title_button = label.ElidedLabel(parent=self) self._title_button.setText(self.item().name()) self._menu_button = buttons.BaseButton(parent=self) self._menu_button.setIcon(resources.icon('menu_dots')) buttons_layout.addWidget(self._title_icon) buttons_layout.addStretch() buttons_layout.addWidget(self._title_button) buttons_layout.addStretch() buttons_layout.addWidget(self._menu_button) title_frame_layout.addWidget(title_widget) item_icon_name = self.item().icon() or 'tpDcc' item_icon = resources.icon(item_icon_name) if not item_icon: item_icon = resources.icon('tpDcc') self._title_icon.setPixmap(item_icon.pixmap(QSize(20, 20))) icon_title_frame = QFrame(self) icon_title_frame_layout = layouts.VerticalLayout(spacing=0, margins=(4, 2, 4, 2)) icon_title_frame.setLayout(icon_title_frame_layout) self._icon_group_frame = QFrame(self) icon_group_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._icon_group_frame.setLayout(icon_group_layout) self._icon_frame = QFrame(self) icon_frame_layout = layouts.VerticalLayout(spacing=0, margins=(4, 2, 4, 2)) self._icon_frame.setLayout(icon_frame_layout) icon_group_layout.addWidget(self._icon_frame) icon_group_box_widget = group.GroupBoxWidget('Icon', widget=self._icon_group_frame, parent=self) icon_title_frame_layout.addWidget(icon_group_box_widget) form_frame = QFrame(self) form_frame_layout = layouts.VerticalLayout(spacing=0, margins=(4, 2, 4, 2)) form_frame.setLayout(form_frame_layout) buttons_frame = QFrame(self) buttons_frame_layout = layouts.HorizontalLayout(spacing=4, margins=(4, 4, 4, 4)) buttons_frame.setLayout(buttons_frame_layout) self._accept_button = buttons.BaseButton('Load') self._accept_button.setIcon(resources.icon('open')) self._accept_button.setVisible(False) buttons_frame_layout.addWidget(self._accept_button) schema = self._item_view.item.load_schema() if self._item_view else None if schema: self._form_widget = formwidget.FormWidget(self) self._form_widget.setObjectName('{}Form'.format(self._item_view.__class__.__name__)) self._form_widget.set_schema(schema) self._form_widget.set_validator(self.validator) form_frame_layout.addWidget(self._form_widget) self.main_layout.addWidget(title_frame) self.main_layout.addWidget(icon_title_frame) self.main_layout.addWidget(self._icon_group_frame) self.main_layout.addWidget(form_frame) self.main_layout.addStretch() self.main_layout.addWidget(buttons_frame) def setup_signals(self): self._menu_button.clicked.connect(self._on_show_menu) self._accept_button.clicked.connect(self.on_load) def resizeEvent(self, event): """ Overrides base BaseWidget resizeEvent function to make sure the icon image size is updated :param event: QSizeEvent """ self.update_thumbnail_size() def close(self): """ Overrides base BaseWidget close function to save widget persistent data """ # TODO: It is not sure this function it is going to be called when the app is closed # TODO: Find a better approach if self._form_widget: self._form_widget.save_persistent_values() super(PreviewWidget, self).close() # ============================================================================================================ # BASE # ============================================================================================================ def item(self): """ Returns the library item to load :return: LibraryItem """ return self._item_view def validator(self, **kwargs): """ Returns validator used for validating the item loaded arguments :param kwargs: dict """ self._item_view.load_validator(**kwargs) self.update_icon() def update_icon(self): """ Updates item thumbnail icon """ # Updates static icon icon = self._item_view.thumbnail_icon() if icon: self._sequence_widget.setIcon(icon) # Update images sequence (if exist) if self._item_view.image_sequence_path(): self._sequence_widget.set_dirname(self._item_view.image_sequence_path()) def update_thumbnail_size(self): """ Updates the thumbnail button to the size of the widget """ width = self.width() - 5 width = width if width <= 150 else 150 size = QSize(width, width) self._icon_frame.setMaximumSize(size) self._icon_group_frame.setMaximumSize(size) if self._sequence_widget: self._sequence_widget.setIconSize(size) self._sequence_widget.setMinimumSize(size) self._sequence_widget.setMaximumSize(size) # ============================================================================================================ # INTERNAL # ============================================================================================================ def _create_sequence_widget(self): """ Internal function that creates sequence widget for the item """ self._sequence_widget = sequence.ImageSequenceWidget(self._icon_frame) self._icon_frame.layout().insertWidget(0, self._sequence_widget) self.update_icon() # ============================================================================================================ # CALLBACKS # ============================================================================================================ def _on_item_data_changed(self, *args, **kwargs): """ Internal callback function that is called when item data changes :param args: :param kwargs: """ self.update_icon() def _on_show_menu(self): """ Internal callback function triggered when item menu should be opened :return: QAction """ menu = QMenu(self) self.item().context_edit_menu(menu) point = QCursor.pos() point.setX(point.x() + 3) point.setY(point.y() + 3) return menu.exec_(point) def on_load(self): """ Internal callback function that is triggered when load button is pressed by the user """ kwargs = self._form_widget.values() self._item_view.load(**kwargs)
class IndexedStringsWidget(QWidget): """ Holds the list of indexed string attributes """ def __init__(self, data, parent=None): QWidget.__init__(self, parent) self.data = data vbox = QVBoxLayout() self.setLayout(vbox) title = QLabel('Indexed Strings') vbox.addWidget(title) self.frame = QFrame() vbox.addWidget(self.frame) self.vbox = QVBoxLayout() self.frame.setLayout(self.vbox) self.frame.setFrameShape(QFrame.Panel) self.frame.setFrameShadow(QFrame.Sunken) self.table = QTableWidget() self.table.horizontalHeader().hide() self.vbox.addWidget(self.table) self.table.hide() self.noStringsLabel = QLabel('<i>No indexed strings</i>') self.vbox.addWidget(self.noStringsLabel) self.widgets = [] self.populate() self.data.attributeAdded.connect(self.attributeAddedSlot) self.data.dataReset.connect(self.dataResetSlot) self.data.dirtied.connect(self.dataDirtiedSlot) def dataDirtiedSlot(self, dirty): """ SLOT when the particle data is dirtied or cleaned.""" if not dirty: for widget in self.widgets: widget.drawBorder(False) def dataResetSlot(self): """ SLOT when particle data is reconstructed """ self.populate() def attributeAddedSlot(self, name): #pylint:disable=W0613 """ SLOT when an attribute is added to the particle set """ attr = self.data.attributeInfo(name) if attr.type == partio.INDEXEDSTR: self.populate() def populate(self): """ Populates the table of indexed strings """ self.widgets = [] # If no widgets, just drop that in attrs = [] for anum in range(self.data.numAttributes()): attr = self.data.attributeInfo(anum) if attr.type == partio.INDEXEDSTR: attrs.append(attr) if not attrs: self.table.hide() self.noStringsLabel.show() return self.table.show() self.noStringsLabel.hide() self.table.setColumnCount(1) self.table.setRowCount(len(attrs)) for row, attr in enumerate(attrs): item = QTableWidgetItem(attr.name) self.table.setVerticalHeaderItem(row, item) strings = self.data.indexedStrs(attr) table = QTableWidget() table.setColumnCount(1) table.setRowCount(len(strings)) table.horizontalHeader().hide() table.setVerticalHeaderLabels( [str(i) for i in range(len(strings))]) for i, string in enumerate(strings): widget = QLabel(string) table.setCellWidget(i, 0, widget) self.widgets.append(widget) self.table.setCellWidget(row, 0, table) self.table.horizontalHeader().setStretchLastSection(False) self.table.setTabKeyNavigation(True) self.table.horizontalHeader().setSectionsMovable(False) self.table.horizontalHeader().resizeSections( QHeaderView.ResizeToContents) self.table.verticalHeader().resizeSections( QHeaderView.ResizeToContents)
class BaseSaveWidget(base.BaseWidget, object): cancelled = Signal() saved = Signal() ENABLE_THUMBNAIL_CAPTURE = True def __init__(self, item_view, client=None, *args, **kwargs): self._item_view = item_view self._client = client self._form_widget = None self._sequence_widget = None super(BaseSaveWidget, self).__init__(*args, **kwargs) self.setObjectName('LibrarySaveWidget') self._create_sequence_widget() self.update_thumbnail_size() self.set_item_view(item_view) # ============================================================================================================ # OVERRIDES # ============================================================================================================ def get_main_layout(self): return layouts.VerticalLayout(spacing=4, margins=(0, 0, 0, 0)) def ui(self): super(BaseSaveWidget, self).ui() self.setWindowTitle('Save Item') title_frame = QFrame(self) title_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_frame.setLayout(title_frame_layout) title_widget = QFrame(self) title_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_widget.setLayout(title_layout) title_buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) title_layout.addLayout(title_buttons_layout) title_icon = label.BaseLabel(parent=self) title_button = label.BaseLabel(self.item().menu_name(), parent=self) title_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._menu_button = buttons.BaseButton(parent=self) self._menu_button.setIcon(resources.icon('menu_dots')) self._menu_button.setVisible(False) # Hide by default title_buttons_layout.addWidget(title_icon) title_buttons_layout.addSpacing(5) title_buttons_layout.addWidget(title_button) title_buttons_layout.addWidget(self._menu_button) title_frame_layout.addWidget(title_widget) item_icon_name = self.item().icon() or 'tpDcc' item_icon = resources.icon(item_icon_name) if not item_icon: item_icon = resources.icon('tpDcc') title_icon.setPixmap(item_icon.pixmap(QSize(20, 20))) thumbnail_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._thumbnail_frame = QFrame(self) thumbnail_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 2, 0, 2)) self._thumbnail_frame.setLayout(thumbnail_frame_layout) thumbnail_layout.addWidget(self._thumbnail_frame) self._options_frame = QFrame(self) options_frame_layout = layouts.VerticalLayout(spacing=0, margins=(4, 2, 4, 2)) self._options_frame.setLayout(options_frame_layout) preview_buttons_frame = QFrame(self) self._preview_buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(4, 2, 4, 2)) preview_buttons_frame.setLayout(self._preview_buttons_layout) self._save_button = buttons.BaseButton('Save', parent=self) self._save_button.setIcon(resources.icon('save')) self._cancel_button = buttons.BaseButton('Cancel', parent=self) self._cancel_button.setIcon(resources.icon('cancel')) self._preview_buttons_layout.addStretch() self._preview_buttons_layout.addWidget(self._save_button) self._preview_buttons_layout.addStretch() self._preview_buttons_layout.addWidget(self._cancel_button) self._preview_buttons_layout.addStretch() self.main_layout.addWidget(title_frame) self.main_layout.addLayout(thumbnail_layout) self.main_layout.addWidget(self._options_frame) self.main_layout.addWidget(preview_buttons_frame) def setup_signals(self): self._menu_button.clicked.connect(self._on_show_menu) self._save_button.clicked.connect(self._on_save) self._cancel_button.clicked.connect(self._on_cancel) def resizeEvent(self, event): """ Overrides base QWidget resizeEvent function :param event: QResizeEvent """ self.update_thumbnail_size() def close(self): """ Overrides base QWidget close function to disable script job when its is done """ if self._form_widget: self._form_widget.save_persistent_values() super(BaseSaveWidget, self).close() # ============================================================================================================ # BASE # ============================================================================================================ def folder_path(self): """ Returns the folder path :return: str """ return self.form_widget().value('folder') def set_folder_path(self, path): """ Sets the destination folder path :param path: str """ self.form_widget().set_value('folder', path) def set_thumbnail_path(self, path): """ Sets the path to the thumbnail image or the image sequence directory :param path: str """ file_name, extension = os.path.splitext(path) target = utils.temp_path('thumbnail{}'.format(extension)) utils.copy_path(path, target, force=True) self._sequence_widget.set_path(target) def library_window(self): """ Returns library widget window for the item :return: LibraryWindow """ return self.item_view().library_window() def set_library_window(self, library_window): """ Sets the library widget for the item :param library_window: LibraryWindow """ self.item_view().set_library_window(library_window) def form_widget(self): """ Returns the form widget instance :return: FormWidget """ return self._form_widget def item(self): """ Returns current item :return: """ return self.item_view().item def item_view(self): """ Returns the current item view :return: LibraryItem """ return self._item_view def set_item_view(self, item_view): """ Sets the base item to be created :param item_view: LibraryItem """ self._item_view = item_view if os.path.exists(item_view.image_sequence_path()): self.set_thumbnail_path(item_view.image_sequence_path()) elif not item_view.is_default_thumbnail_path(): self.set_thumbnail_path(item_view.thumbnail_path()) schema = self.item().save_schema() if schema: form_widget = formwidget.FormWidget(self) form_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) form_widget.set_schema(schema) form_widget.set_validator(self.item().save_validator) # item_name = os.path.basename(item.path()) # form_widget.set_values({'name': item_name}) self._options_frame.layout().addWidget(form_widget) form_widget.validate() self._form_widget = form_widget else: self._options_frame.setVisible(False) def update_thumbnail_size(self): """ Updates the thumbnail button to teh size of the widget """ width = self.width() - 10 if width > 250: width = 250 size = QSize(width, width) if self._sequence_widget: self._sequence_widget.setIconSize(size) self._sequence_widget.setMaximumSize(size) self._thumbnail_frame.setMaximumSize(size) def show_thumbnail_capture_dialog(self): """ Asks the user if they would like to capture a thumbnail :return: int """ buttons = QDialogButtonBox.Yes | QDialogButtonBox.Ignore | QDialogButtonBox.Cancel parent = self.item_view().library_window() btn = messagebox.MessageBox.question( parent, 'Create a thumbnail', 'Would you like to capture a thumbnail?', buttons=buttons) if btn == QDialogButtonBox.Yes: self.thumbnail_capture() return btn def show_by_frame_dialog(self): """ Show the by frame dialog """ help_text = """ To help speed up the playblast you can set the "by frame" to another greater than 1. For example if the "by frame" is set to 2 it will playblast every second frame """ result = None options = self.form_widget().values() by_frame = options.get('byFrame', 1) start_frame, end_frame = options.get('frameRange', [None, None]) duration = end_frame - start_frame if start_frame is not None and end_frame is not None else 1 if duration > 100 and by_frame == 1: buttons = QDialogButtonBox.Ok | QDialogButtonBox.Cancel result = messagebox.MessageBox.question( self.library_window(), title='Tip', text=help_text, buttons=buttons, enable_dont_show_checkbox=True) return result def thumbnail_capture(self, show=False): """ Captures a playblast and saves it to the temporal thumbnail path :param show: bool """ options = self.form_widget().values() start_frame, end_frame = options.get('frameRange', [None, None]) step = options.get('byFrame', 1) if not qtutils.is_control_modifier(): result = self.show_by_frame_dialog() if result == QDialogButtonBox.Cancel: return path = utils.temp_path('sequence', 'thumbnail.jpg') try: snapshot.SnapshotWindow(path=path, on_save=self._on_thumbnail_captured) # thumbnail.ThumbnailCaptureDialog.thumbnail_capture( # path=self._temp_path, # show=show, # start_frame=start_frame, # end_frame=end_frame, # step=step, # clear_cache=False, # captured=self._on_thumbnail_captured # ) except Exception as e: messagebox.MessageBox.critical(self.library_window(), 'Error while capturing thumbnail', str(e)) LOGGER.error(traceback.format_exc()) def save(self, path, thumbnail): """ Saves the item with the given objects to the given disk location path :param path: str :param thumbnail: str """ kwargs = self.form_widget().values() sequence_path = self._sequence_widget.dirname() item_view = self.item_view() item_view.item_view.path = path library_window = self.library_window() valid_save = item_view.safe_save(thumbnail=thumbnail, sequence_path=sequence_path, **kwargs) if valid_save: if library_window: library_window.refresh() library_window.select_folder_path(path) self.saved.emit() self.close() # ============================================================================================================ # INTERNAL # ============================================================================================================ def _create_sequence_widget(self): """ Internal function that creates a sequence widget to replace the static thumbnail widget """ self._sequence_widget = sequence.ImageSequenceWidget(self) self._sequence_widget.setObjectName('thumbnailButton') self._thumbnail_frame.layout().insertWidget(0, self._sequence_widget) self._sequence_widget.clicked.connect(self._on_thumbnail_capture) self._sequence_widget.setToolTip( 'Click to capture a thumbnail from the current model panel.\n' 'CTRL + Click to show the capture window for better framing.') camera_icon = resources.get('icons', self.theme().style(), 'camera.png') expand_icon = resources.get('icons', self.theme().style(), 'full_screen.png') folder_icon = resources.get('icons', self.theme().style(), 'folder.png') self._sequence_widget.addAction(camera_icon, 'Capture new image', 'Capture new image', self._on_thumbnail_capture) self._sequence_widget.addAction(expand_icon, 'Show Capture window', 'Show Capture window', self._on_show_capture_window) self._sequence_widget.addAction(folder_icon, 'Load image from disk', 'Load image from disk', self._on_show_browse_image_dialog) self._sequence_widget.setIcon(resources.icon('tpdcc')) # ============================================================================================================ # CALLBACKS # ============================================================================================================ def _on_show_menu(self): """ Internal callback function that is called when menu button is clicked byu the user :return: QAction """ pass def _on_save(self): if not self.library_window(): return False library = self.library_window().library() if not library: return False try: self.form_widget().validate() if self.form_widget().has_errors(): raise Exception('\n'.join(self.form_widget().errors())) has_frames = self._sequence_widget.has_frames() if not has_frames and self.ENABLE_THUMBNAIL_CAPTURE: button = self.show_thumbnail_capture_dialog() if button == QDialogButtonBox.Cancel: return False name = self.form_widget().value('name') folder = self.form_widget().value('folder') comment = self.form_widget().value('comment') or '' extension = self.item().extension() if extension and not name.endswith(extension): name = '{}{}'.format(name, extension) path = folder + '/' + name thumbnail = self._sequence_widget.first_frame() save_item = library.get(path, only_extension=True) save_function = save_item.functionality().get('save') if not save_function: LOGGER.warning( 'Item "{}" does not supports save operation'.format( save_item)) return False library_path = self.item().library.identifier if not library_path or not os.path.isfile(library_path): LOGGER.warning( 'Impossible to save data "{}" because its library does not exists: "{}"' .format(self.item(), library_path)) return values = self.form_widget().values() try: if self._client: success, message, dependencies = self._client().save_data( library_path=library_path, data_path=path, values=values) if not success: messagebox.MessageBox.critical(self.library_window(), 'Error while saving', str(message)) LOGGER.error(str(message)) return False else: dependencies = save_function(**values) except Exception as exc: messagebox.MessageBox.critical(self.library_window(), 'Error while saving', str(exc)) LOGGER.error(traceback.format_exc()) return False except Exception as exc: messagebox.MessageBox.critical(self.library_window(), 'Error while saving', str(exc)) LOGGER.error(traceback.format_exc()) raise new_item_path = save_item.format_identifier() if not new_item_path or not os.path.isfile(new_item_path): LOGGER.warning( 'Although saving process for item "{}" was completed, ' 'it seems no new data has been generated!'.format(save_item)) self.saved.emit() return False save_item.library.add(new_item_path) # # TODO: Instead of creating a local version, we will use a git system to upload our data to our project repo # # TODO: Should we save new versions of dependencies too? # valid = save_item.create_version(comment=comment) # if not valid: # LOGGER.warning('Impossible to store new version for data "{}"'.format(save_item)) if thumbnail and os.path.isfile(thumbnail): save_item.store_thumbnail(thumbnail) self.library_window().sync() save_item.update_dependencies(dependencies=dependencies) self.saved.emit() return True def _on_cancel(self): self.cancelled.emit() self.close() def _on_thumbnail_capture(self): """ Internal callback function that is called when a thumbnail capture must be done """ self.thumbnail_capture(show=False) def _on_thumbnail_captured(self, captured_path): """ Internal callback function that is called when thumbnail is captured :param captured_path: str """ thumb_path = os.path.dirname(captured_path) self.set_thumbnail_path(thumb_path) def _on_show_capture_window(self): """ Internal callback function that shows the capture window for framing """ self.thumbnail_capture(show=True) def _on_show_browse_image_dialog(self): """ Internal callback function that shows a file dialog for choosing an image from disk """ file_dialog = QFileDialog(self, caption='Open Image', filter='Image Files (*.png *.jpg)') file_dialog.fileSelected.connect(self.set_thumbnail_path) file_dialog.exec_()
class RadioFieldWidget(FieldWidget, object): def __init__(self, *args, **kwargs): super(RadioFieldWidget ,self).__init__(*args, **kwargs) self._radio_buttons = list() layout = layouts.VerticalLayout(margins=(0, 0, 0, 0)) self._radio_frame = QFrame(self) self._radio_frame.setLayout(layout) self.set_widget(self._radio_frame) self.label().setStyleSheet('margin-top: 2px;') self.label().setAlignment(Qt.AlignRight | Qt.AlignTop) self.widget().setStyleSheet('margin-top: 2px;') # ================================================================================================================= # OVERRIDES # ================================================================================================================= def value(self): """ Implements FieldWidget value function Returns the path of the image in disk :return: str """ for radio_button in self._radio_buttons: if radio_button.isChecked(): return radio_button.text() return '' def set_value(self, value): """ Overrides FileWidget set_value function Sets the path of the image in disk :param value: str """ for radio_button in self._radio_buttons: checked = radio_button.text() == value radio_button.setChecked(checked) def set_items(self, items): """ Sets the items for the field widget :param items: list(str) """ self.clear() for item in items: widget = buttons.BaseRadioButton(parent=self) widget.setText(item) widget.clicked.connect(self._on_emit_value_changed) self._radio_buttons.append(widget) self._radio_frame.layout().addWidget(widget) # ================================================================================================================= # BASE # ================================================================================================================= def clear(self): """ Destroy all radio buttons """ for radio_button in self._radio_buttons: radio_button.destroy() radio_button.close() radio_button.hide()
def ui(self): super(BaseLoadWidget, self).ui() self.setWindowTitle('Load Item') title_frame = QFrame(self) title_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_frame.setLayout(title_frame_layout) title_widget = QFrame(self) title_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_widget.setLayout(title_layout) title_buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) title_layout.addLayout(title_buttons_layout) title_icon = label.BaseLabel(parent=self) title_button = label.BaseLabel(self.item().label(), parent=self) title_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._menu_button = buttons.BaseButton(parent=self) self._menu_button.setIcon(resources.icon('menu_dots')) title_buttons_layout.addWidget(title_icon) title_buttons_layout.addWidget(title_button) title_buttons_layout.addWidget(self._menu_button) title_frame_layout.addWidget(title_widget) item_icon_name = self.item().icon() or 'tpDcc' item_icon = resources.icon(item_icon_name) if not item_icon: item_icon = resources.icon('tpDcc') title_icon.setPixmap(item_icon.pixmap(QSize(20, 20))) main_frame = QFrame(self) main_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) main_frame.setLayout(main_frame_layout) icon_frame = QFrame(self) icon_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_frame.setLayout(icon_frame_layout) icon_title_frame = QFrame(self) icon_title_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_frame_layout.addWidget(icon_title_frame) icon_title_frame.setLayout(icon_title_frame_layout) icon_frame2 = QFrame(self) icon_frame2_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_frame2.setLayout(icon_frame2_layout) thumbnail_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._thumbnail_frame = QFrame(self) thumbnail_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._thumbnail_frame.setLayout(thumbnail_frame_layout) icon_frame_layout.addWidget(icon_frame2) icon_frame2_layout.addLayout(thumbnail_layout) thumbnail_layout.addWidget(self._thumbnail_frame) form_frame = QFrame(self) form_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) form_frame.setLayout(form_frame_layout) main_frame_layout.addWidget(icon_frame) main_frame_layout.addWidget(form_frame) version_frame = QFrame(self) version_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) version_frame.setLayout(version_frame_layout) self._versions_widget = version.VersionHistoryWidget(parent=self) version_frame_layout.addWidget(self._versions_widget) self._custom_widget_frame = QFrame(self) custom_widget_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._custom_widget_frame.setLayout(custom_widget_layout) self._preview_buttons_frame = QFrame(self) preview_buttons_layout = layouts.HorizontalLayout(spacing=2, margins=(0, 0, 0, 0)) self._preview_buttons_frame.setLayout(preview_buttons_layout) self._accept_button = buttons.BaseButton('Load', parent=self) self._accept_button.setIcon(resources.icon('open')) preview_buttons_layout.addStretch() preview_buttons_layout.addWidget(self._accept_button) preview_buttons_layout.addStretch() self._export_btn = buttons.BaseButton('Export', parent=self) self._export_btn.setIcon(resources.icon('export')) self._import_btn = buttons.BaseButton('Import', parent=self) self._import_btn.setIcon(resources.icon('import')) self._reference_btn = buttons.BaseButton('Reference', parent=self) self._reference_btn.setIcon(resources.icon('reference')) for btn in [self._export_btn, self._import_btn, self._reference_btn]: btn.setToolTip(btn.text()) extra_buttons_frame = QFrame(self) extra_buttons_layout = layouts.HorizontalLayout(spacing=2, margins=(0, 0, 0, 0)) extra_buttons_frame.setLayout(extra_buttons_layout) extra_buttons_layout.addWidget(self._export_btn) extra_buttons_layout.addWidget(self._import_btn) extra_buttons_layout.addWidget(self._reference_btn) group_box = group.GroupBoxWidget('Icon', icon_frame) group_box.set_persistent(True) group_box.set_checked(True) self._version_box = group.GroupBoxWidget('Version', version_frame) self._version_box.set_persistent(True) self._version_box.set_checked(True) self._sequence_widget = sequence.ImageSequenceWidget(self) thumbnail_frame_layout.insertWidget(0, self._sequence_widget) if os.path.exists(self._item_view.image_sequence_path()): self._sequence_widget.set_path( self._item_view.image_sequence_path()) elif os.path.exists(self._item_view.thumbnail_path()): self._sequence_widget.set_path(self._item_view.thumbnail_path()) self._form_widget = formwidget.FormWidget(self) self._form_widget.set_schema(self.item().load_schema()) self._form_widget.set_validator(self.item().load_validator) form_frame_layout.addWidget(self._form_widget) self.main_layout.addWidget(title_frame) self.main_layout.addWidget(group_box) self.main_layout.addWidget(main_frame) self.main_layout.addWidget(self._version_box) self.main_layout.addWidget(version_frame) self.main_layout.addWidget(self._custom_widget_frame) self.main_layout.addStretch() self.main_layout.addWidget(dividers.Divider()) self.main_layout.addWidget(self._preview_buttons_frame) self.main_layout.addWidget(extra_buttons_frame)
class BaseLoadWidget(base.BaseWidget, object): def __init__(self, item_view, client=None, parent=None): self._item_view = item_view self._client = client self._form_widget = None self._sequence_widget = None super(BaseLoadWidget, self).__init__(parent=parent) self.setObjectName('LoadWidget') try: self.form_widget().validate() except NameError as error: LOGGER.exception(error) self.update_thumbnail_size() item = self.item() if item: self._accept_button.setVisible( bool(item.functionality().get('load', False))) self._update_version_info() # ============================================================================================================ # OVERRIDES # ============================================================================================================ def get_main_layout(self): return layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) def ui(self): super(BaseLoadWidget, self).ui() self.setWindowTitle('Load Item') title_frame = QFrame(self) title_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_frame.setLayout(title_frame_layout) title_widget = QFrame(self) title_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_widget.setLayout(title_layout) title_buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) title_layout.addLayout(title_buttons_layout) title_icon = label.BaseLabel(parent=self) title_button = label.BaseLabel(self.item().label(), parent=self) title_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._menu_button = buttons.BaseButton(parent=self) self._menu_button.setIcon(resources.icon('menu_dots')) title_buttons_layout.addWidget(title_icon) title_buttons_layout.addWidget(title_button) title_buttons_layout.addWidget(self._menu_button) title_frame_layout.addWidget(title_widget) item_icon_name = self.item().icon() or 'tpDcc' item_icon = resources.icon(item_icon_name) if not item_icon: item_icon = resources.icon('tpDcc') title_icon.setPixmap(item_icon.pixmap(QSize(20, 20))) main_frame = QFrame(self) main_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) main_frame.setLayout(main_frame_layout) icon_frame = QFrame(self) icon_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_frame.setLayout(icon_frame_layout) icon_title_frame = QFrame(self) icon_title_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_frame_layout.addWidget(icon_title_frame) icon_title_frame.setLayout(icon_title_frame_layout) icon_frame2 = QFrame(self) icon_frame2_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_frame2.setLayout(icon_frame2_layout) thumbnail_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._thumbnail_frame = QFrame(self) thumbnail_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._thumbnail_frame.setLayout(thumbnail_frame_layout) icon_frame_layout.addWidget(icon_frame2) icon_frame2_layout.addLayout(thumbnail_layout) thumbnail_layout.addWidget(self._thumbnail_frame) form_frame = QFrame(self) form_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) form_frame.setLayout(form_frame_layout) main_frame_layout.addWidget(icon_frame) main_frame_layout.addWidget(form_frame) version_frame = QFrame(self) version_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) version_frame.setLayout(version_frame_layout) self._versions_widget = version.VersionHistoryWidget(parent=self) version_frame_layout.addWidget(self._versions_widget) self._custom_widget_frame = QFrame(self) custom_widget_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._custom_widget_frame.setLayout(custom_widget_layout) self._preview_buttons_frame = QFrame(self) preview_buttons_layout = layouts.HorizontalLayout(spacing=2, margins=(0, 0, 0, 0)) self._preview_buttons_frame.setLayout(preview_buttons_layout) self._accept_button = buttons.BaseButton('Load', parent=self) self._accept_button.setIcon(resources.icon('open')) preview_buttons_layout.addStretch() preview_buttons_layout.addWidget(self._accept_button) preview_buttons_layout.addStretch() self._export_btn = buttons.BaseButton('Export', parent=self) self._export_btn.setIcon(resources.icon('export')) self._import_btn = buttons.BaseButton('Import', parent=self) self._import_btn.setIcon(resources.icon('import')) self._reference_btn = buttons.BaseButton('Reference', parent=self) self._reference_btn.setIcon(resources.icon('reference')) for btn in [self._export_btn, self._import_btn, self._reference_btn]: btn.setToolTip(btn.text()) extra_buttons_frame = QFrame(self) extra_buttons_layout = layouts.HorizontalLayout(spacing=2, margins=(0, 0, 0, 0)) extra_buttons_frame.setLayout(extra_buttons_layout) extra_buttons_layout.addWidget(self._export_btn) extra_buttons_layout.addWidget(self._import_btn) extra_buttons_layout.addWidget(self._reference_btn) group_box = group.GroupBoxWidget('Icon', icon_frame) group_box.set_persistent(True) group_box.set_checked(True) self._version_box = group.GroupBoxWidget('Version', version_frame) self._version_box.set_persistent(True) self._version_box.set_checked(True) self._sequence_widget = sequence.ImageSequenceWidget(self) thumbnail_frame_layout.insertWidget(0, self._sequence_widget) if os.path.exists(self._item_view.image_sequence_path()): self._sequence_widget.set_path( self._item_view.image_sequence_path()) elif os.path.exists(self._item_view.thumbnail_path()): self._sequence_widget.set_path(self._item_view.thumbnail_path()) self._form_widget = formwidget.FormWidget(self) self._form_widget.set_schema(self.item().load_schema()) self._form_widget.set_validator(self.item().load_validator) form_frame_layout.addWidget(self._form_widget) self.main_layout.addWidget(title_frame) self.main_layout.addWidget(group_box) self.main_layout.addWidget(main_frame) self.main_layout.addWidget(self._version_box) self.main_layout.addWidget(version_frame) self.main_layout.addWidget(self._custom_widget_frame) self.main_layout.addStretch() self.main_layout.addWidget(dividers.Divider()) self.main_layout.addWidget(self._preview_buttons_frame) self.main_layout.addWidget(extra_buttons_frame) def setup_signals(self): self._menu_button.clicked.connect(self._on_show_menu) self._accept_button.clicked.connect(self._on_load) self._export_btn.clicked.connect(self._on_export) self._import_btn.clicked.connect(self._on_import) self._reference_btn.clicked.connect(self._on_reference) self._item_view.loadValueChanged.connect(self._on_item_value_changed) def resizeEvent(self, event): """ Overrides base QWidget resizeEvent function :param event: QResizeEvent """ self.update_thumbnail_size() def close(self): """ Overrides base QWidget close function to disable script job when its is done """ if self.form_widget(): self.form_widget().save_persistent_values() super(BaseLoadWidget, self).close() # ============================================================================================================ # BASE # ============================================================================================================ def item(self): """ Returns the library item to load :return: """ return self._item_view.item def item_view(self): """ Returns the library item view to load :return: LibraryItem """ return self._item_view def form_widget(self): """ Returns form widget instance :return: FormWidget """ return self._form_widget def set_custom_widget(self, widget): """ Sets custom widget to use when loading items :param widget: QQWidget """ self._custom_widget_frame.layout().addWidget(widget) def update_thumbnail_size(self): """ Updates the thumbnail button to teh size of the widget """ width = self.width() - 10 if width > 250: width = 250 size = QSize(width, width) self._sequence_widget.setIconSize(size) self._sequence_widget.setMaximumSize(size) self._thumbnail_frame.setMaximumSize(size) # ============================================================================================================ # INTERNAL # ============================================================================================================ def _update_version_info(self): item_view = self.item_view() if not item_view or not item_view.library_window(): return library_window = item_view.library_window() if not library_window: return repository_type = library_window.get_repository_type() repository_path = library_window.get_repository_path() if not repository_type or not repository_path: return try: repository_type = int(repository_type) except Exception: repository_type = 0 if repository_type == 0: repository_type = None elif repository_type == 1: repository_type = data_version.GitVersionControl self._versions_widget.set_version_control_class(repository_type) self._versions_widget.set_repository_path(repository_path) # If not valid version control is defined, we hide version control valid_version_control = self._versions_widget.set_directory( self.item().format_identifier()) self._versions_widget.setVisible(valid_version_control) self._version_box.setVisible(valid_version_control) # ============================================================================================================ # CALLBACKS # ============================================================================================================ def _on_show_menu(self): """ Internal callback function that is called when menu button is clicked byu the user :return: QAction """ menu = QMenu(self) self._item_view.context_edit_menu(menu) point = QCursor.pos() point.setX(point.x() + 3) point.setY(point.y() + 3) return menu.exec_(point) def _on_load(self): """ Internal callback function that is called when Load button is pressed by the user """ load_function = self.item().functionality().get('load') if not load_function: LOGGER.warning( 'Load functionality is not available for data: "{}"'.format( self.item())) return library_path = self.item().library.identifier if not library_path or not os.path.isfile(library_path): LOGGER.warning( 'Impossible to load data "{}" because its library does not exists: "{}"' .format(self.item(), library_path)) return if self._client: self._client().load_data(library_path=library_path, data_path=self.item().format_identifier()) else: load_function() def _on_export(self): """ Internal callback function that is called when export button is pressed by the user """ item = self.item() if not item: return library_window = self.item_view().library_window() if not library_window: return base_data.BaseDataItemView.show_export_widget(item.__class__, item.format_identifier(), library_window) def _on_import(self): """ Internal callback function that is called when import button is pressed by the user """ import_function = self.item().functionality().get('import_data') if not import_function: LOGGER.warning( 'Import functionality is not available for data: "{}"'.format( self.item())) return library_path = self.item().library.identifier if not library_path or not os.path.isfile(library_path): LOGGER.warning( 'Impossible to load data "{}" because its library does not exists: "{}"' .format(self.item(), library_path)) return if self._client: self._client().import_data( library_path=library_path, data_path=self.item().format_identifier()) else: import_function() def _on_reference(self): """ Internal callback function that is called when reference button is pressed by the user """ reference_function = self.item().functionality().get('reference_data') if not reference_function: LOGGER.warning( 'Reference functionality is not available for data: "{}"'. format(self.item())) return library_path = self.item().library.identifier if not library_path or not os.path.isfile(library_path): LOGGER.warning( 'Impossible to load data "{}" because its library does not exists: "{}"' .format(self.item(), library_path)) return if self._client: self._client().reference_data( library_path=library_path, data_path=self.item().format_identifier()) else: reference_function() def _on_item_value_changed(self, field, value): """ Internal callback function that is called each time an item field value changes :param field: str :param value: object """ self._form_widget.set_value(field, value)
class GroupBoxWidget(base.BaseFrame): toggled = Signal(bool) def __init__(self, title, widget, persistent=False, settings=None, *args, **kwargs): self._title = title self._widget = None self._persistent = None self._settings = settings super(GroupBoxWidget, self).__init__(*args, **kwargs) if widget: self.set_widget(widget) # We force the update of the check status to make sure that the wrapped widget visibility is updated self.set_checked(self.is_checked()) self.set_persistent(persistent) # ============================================================================================================ # OVERRIDES # ============================================================================================================ def get_main_layout(self): return layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) def ui(self): super(GroupBoxWidget, self).ui() self._title_widget = buttons.BaseButton(self._title, parent=self) self._title_widget.setCheckable(True) self._on_icon = resources.icon('down_button') self._off_icon = resources.icon('right_button') self._title_widget.setIcon(self._off_icon) self._widget_frame = QFrame(self) self._widget_frame.setObjectName('contentsWidget') widget_frame_layout = layouts.VerticalLayout(spacing=2, margins=(0, 0, 0, 0)) self._widget_frame.setLayout(widget_frame_layout) self.main_layout.addWidget(self._title_widget) self.main_layout.addWidget(self._widget_frame) def setup_signals(self): self._title_widget.toggled.connect(self._on_toggled_title) # ============================================================================================================ # BASE # ============================================================================================================ def is_checked(self): """ Returns whether or not group box is checked :return: bool """ return self._title_widget.isChecked() def set_checked(self, flag): """ Sets the check statue of the group box :param flag: bool """ self._title_widget.setChecked(flag) self._title_widget.setIcon(self._on_icon if flag else self._off_icon) self._widget_frame.setVisible(flag) if self._widget: self._widget.setVisible(flag) def is_persistent(self): """ Returns whether or not widget state is stored in settings :return: bool """ return self._persistent def set_persistent(self, flag): """ Sets whether or not widget state is stored in settings :param flag: bool """ self._persistent = flag self.load_settings() def title(self): """ Returns group box title :return: str """ return self._title_widget.text() def set_widget(self, widget): """ Sets the widget to hide when the user clicks the title :param widget: QWidget """ self._widget = widget self._widget.setParent(self._widget_frame) self._widget_frame.layout().addWidget(self._widget) # ============================================================================================================ # SETTINGS # ============================================================================================================ def load_settings(self): """ Loads widget state from given settings """ if not self._settings or not self._persistent: return if not self.objectName(): raise NameError( 'Impossible to save "{}" widget state because no objectName is defined!' .format(self)) data = {self.objectName(): {'checked': self.is_checked()}} self._settings.save(data) def save_settings(self): """ Saves current widget state into settings """ if not self._settings or not self._persistent: return if not self.objectName(): raise NameError( 'Impossible to load "{}" widget state because no objectName is defined!' .format(self)) data = self._settings.read() data = data.get(self.objectName(), dict()) if data and isinstance(data, dict): checked = data.get('checked', True) self.set_checked(checked) # ============================================================================================================ # CALLBACKS # ============================================================================================================ def _on_toggled_title(self, flag): """ Internal callback function that is called each time title group widget is toggled :param flag: bool """ self.save_settings() self.set_checked(flag) self.toggled.emit(flag)
class MessageBox(QDialog, object): MAX_WIDTH = 320 MAX_HEIGHT = 220 @staticmethod def input(parent, title, text, input_text='', width=None, height=None, buttons=None, header_pixmap=None, header_color=None, theme_to_apply=None): """ Helper dialog function to get a single text value from the user :param parent: QWidget :param title: str :param text: str :param input_text: str :param width: int :param height: int :param buttons: list(QDialogButtonBox.StandardButton) :param header_pixmap: QPixmap :param header_color: str :param theme_to_apply: Theme :return: QMessageBox.StandardButton """ buttons = buttons or QDialogButtonBox.Ok | QDialogButtonBox.Cancel dialog = create_message_box(parent=parent, title=title, text=text, width=width, height=height, buttons=buttons, header_pixmap=header_pixmap, header_color=header_color, enable_input_edit=True, theme_to_apply=theme_to_apply) dialog.set_input_text(input_text) dialog.exec_() clicked_btn = dialog.clicked_standard_button() return dialog.input_text(), clicked_btn @staticmethod def question(parent, title, text, width=None, height=None, buttons=None, header_pixmap=None, header_color=None, enable_dont_show_checkbox=False, theme_to_apply=None): """ Helper dialog function to get a single text value from the user :param parent: QWidget :param title: str :param text: str :param width: int :param height: int :param buttons: list(QDialogButtonBox.StandardButton) :param header_pixmap: QPixmap :param header_color: str :param enable_dont_show_checkbox: bool :param theme_to_apply: Theme :return: QDialogButtonBox.StandardButton """ buttons = buttons or QDialogButtonBox.Yes | QDialogButtonBox.No | QDialogButtonBox.Cancel clicked_btn = show_message_box( parent=parent, title=title, text=text, width=width, height=height, buttons=buttons, header_pixmap=header_pixmap, header_color=header_color, enable_dont_show_checkbox=enable_dont_show_checkbox, theme_to_apply=theme_to_apply) return clicked_btn @staticmethod def warning(parent, title, text, width=None, height=None, buttons=None, header_pixmap=None, header_color='rgb(250, 160, 0)', enable_dont_show_checkbox=False, force=False): """ Helper dialog function to open a warning message box with the given options :param parent: QWidget :param title: str :param text: str :param width: int :param height: int :param buttons: list(QDialogButtonBox.StandardButton) :param header_pixmap: QPixmap :param header_color: str :param enable_dont_show_checkbox: bool :param force: bool :return: QDialogButtonBox.StandardButton """ buttons = buttons or QDialogButtonBox.Yes | QDialogButtonBox.No clicked_btn = show_message_box( parent=parent, title=title, text=text, width=width, height=height, buttons=buttons, header_pixmap=header_pixmap, header_color=header_color, enable_dont_show_checkbox=enable_dont_show_checkbox, force=force) return clicked_btn @staticmethod def critical(parent, title, text, width=None, height=None, buttons=None, header_pixmap=None, header_color='rgb(230, 80, 80)'): """ Helper dialog function to open a critical/error message box with the given options :param parent: QWidget :param title: str :param text: str :param width: int :param height: int :param buttons: list(QDialogButtonBox.StandardButton) :param header_pixmap: QPixmap :param header_color: str :return: QDialogButtonBox.StandardButton """ buttons = buttons or QDialogButtonBox.Ok | QDialogButtonBox.Cancel clicked_btn = show_message_box(parent=parent, title=title, text=text, width=width, height=height, buttons=buttons, header_pixmap=header_pixmap, header_color=header_color) return clicked_btn def __init__(self, name='messageBox', width=None, height=None, enable_input_edit=False, enable_dont_show_checkbox=False, parent=None): super(MessageBox, self).__init__(parent=parent) self._frame = None self._animation = None self._dont_show_checkbox = False self._clicked_button = None self._clicked_standard_button = None self.setMinimumWidth(width or self.MAX_WIDTH) self.setMinimumHeight(height or self.MAX_HEIGHT) self.setObjectName(name) self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) self.setAttribute(Qt.WA_TranslucentBackground) # self.setStyleSheet('background-color: rgb(68, 68, 68, 255);') parent = self.parent() self._frame = None if parent and parent != dcc.get_main_window(): parent.installEventFilter(self) self._frame = QFrame(parent) self._frame.setStyleSheet( 'background-color: rgba(25, 25, 25, 150);') self._frame.setObjectName('messageBoxFrame') self._frame.show() self.setParent(self._frame) self.main_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self.setLayout(self.main_layout) self._header = QFrame(self) self._header.setFixedHeight(46) self._header.setObjectName('messageBoxHeaderFrame') self._header.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self._icon = label.BaseLabel(parent=self._header) self._icon.hide() self._icon.setFixedHeight(32) self._icon.setFixedHeight(32) self._icon.setScaledContents(True) self._icon.setAlignment(Qt.AlignTop) self._icon.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self._title = label.BaseLabel(parent=self._header) self._title.setObjectName('messageBoxHeaderLabel') self._title.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) hlayout = layouts.HorizontalLayout(spacing=10, margins=(15, 7, 15, 10)) hlayout.addWidget(self._icon) hlayout.addWidget(self._title) self._header.setLayout(hlayout) body_layout = layouts.VerticalLayout() self._body = QFrame(self) self._body.setObjectName('messageBoxBody') self._body.setLayout(body_layout) self._message = label.BaseLabel(parent=self._body) self._message.setWordWrap(True) self._message.setMinimumHeight(15) self._message.setAlignment(Qt.AlignLeft) self._message.setTextInteractionFlags(Qt.TextSelectableByMouse) self._message.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) body_layout.addWidget(self._message) body_layout.setContentsMargins(15, 15, 15, 15) if enable_input_edit: self._input_edit = lineedit.BaseLineEdit(parent=self._body) self._input_edit.setObjectName('messageBoxInputEdit') self._input_edit.setMinimumHeight(32) self._input_edit.setFocus() body_layout.addStretch(1) body_layout.addWidget(self._input_edit) body_layout.addStretch(10) if enable_dont_show_checkbox: msg = 'Do not show this message again' self._dont_show_checkbox = checkbox.BaseCheckBox(msg, parent=self._body) body_layout.addStretch(10) body_layout.addWidget(self._dont_show_checkbox) body_layout.addStretch(2) self._button_box = QDialogButtonBox(None, Qt.Horizontal, self) self._button_box.clicked.connect(self._on_clicked) self._button_box.accepted.connect(self._on_accept) self._button_box.rejected.connect(self._on_reject) self.main_layout.addWidget(self._header) self.main_layout.addWidget(self._body) body_layout.addWidget(self._button_box) self.updateGeometry() def eventFilter(self, object, event): """ Overrides base QDialog eventFilter function Updates the geometry when the parnet widget changes size :param object: QWidget :param event: QEvent """ if event.type() == QEvent.Resize: self.updateGeometry() return super(MessageBox, self).eventFilter(object, event) def showEvent(self, event): """ Overrides base QDialog showEvent function Fade in hte dialog on show :param event: QEvent """ self.updateGeometry() self._fade_in() def updateGeometry(self): """ Overrides base QDialog updateGeometry function Updates the geometry to be in the center of it's parent """ frame = self._frame if frame: frame.setGeometry(self._frame.parent().geometry()) frame.move(0, 0) geometry = self.geometry() center_point = frame.geometry().center() geometry.moveCenter(center_point) geometry.setY(geometry.y() - 50) self.move(geometry.topLeft()) def exec_(self): """ Overrides base QDialog exec_ function Shows the dialog as a modal dialog :return: variant, int or None """ super(MessageBox, self).exec_() return self.clicked_index() def button_box(self): """ Returns the button box widget for the dialog :return: QDialogButtonBox """ return self._button_box def header(self): """ Returns the header frame :return: QFrame """ return self._header def set_header_color(self, color): """ Sets the header color for the message box :param color: str """ self.header().setStyleSheet('background-color: {}'.format(color)) def set_title_text(self, text): """ Sets the title text to be displayed :param text: str """ self._title.setText(text) def set_text(self, text): """ Sets the text message to be displayed :param text: str """ self._message.setText(str(text)) def input_text(self): """ Returns the text that the user has given in the input edit :return: str """ return self._input_edit.text() def set_input_text(self, text): """ Sets the input text :param text: str """ self._input_edit.setText(text) def add_button(self, *args): """ Adds a new upsh button with the given text and roled """ self.button_box().addButton(*args) def set_buttons(self, buttons): """ Sets the buttons to be displayed in message box :param buttons: QMessageBox.StandardButton """ self.button_box().setStandardButtons(buttons) def set_pixmap(self, pixmap): """ Sets the pixmap for the message box :param pixmap: QPixmap """ self._icon.setPixmap(pixmap) self._icon.show() def clicked_button(self): """ Returns the button that was clicked :return: variant, QPushButton or None """ return self._clicked_button def clicked_index(self): """ Returns the button that was clicked by its index :return: variant, int or None """ for i, btn in enumerate(self.button_box().buttons()): if btn == self.clicked_button(): return i def clicked_standard_button(self): """ Returns the button that was clicked by the user :return: variant, QMessageBox.StandardButton or None """ return self._clicked_standard_button def is_dont_show_checkbox_checked(self): """ Returns the checked state of the dont show again checkbox :return: bool """ if self._dont_show_checkbox: return self._dont_show_checkbox.isChecked() else: return False def _fade_in(self, duration=200): """ Internal function that fade in the dialog using opacity effect :param duration: int :return: QPropertyAnimation """ if self._frame: self._animation = animation.fade_in_widget(self._frame, duration=duration) return self._animation def _fade_out(self, duration=200): """ Internal function that fade out the dialog using opacity effect :param duration: int :return: QPropertyAnimation """ if self._frame: self._animation = animation.fade_out_widget(self._frame, duration=duration) return self._animation def _on_clicked(self, button): """ Internal callback function triggered when the user clicks a button :param button: QPushButton """ self._clicked_button = button self._clicked_standard_button = self.button_box().standardButton( button) def _on_accept(self): """ Internal callback function triggered when the DialogButtonBox has been accepted """ anim = self._fade_out() if anim: anim.finished.connect(self._on_accept_animation_finished) else: self._on_accept_animation_finished() def _on_reject(self): """ Internal callback function triggered when the DialogButtonBox has been rejected """ anim = self._fade_out() if anim: anim.finished.connect(self._on_reject_animation_finished) else: self._on_reject_animation_finished() def _on_accept_animation_finished(self): """ Internal callback function triggered when the animation has finished on accepted """ parent = self._frame or self parent.close() self.accept() def _on_reject_animation_finished(self): parent = self._frame or self parent.close() self.reject()
class IndexedStringsWidget(QWidget): """ Holds the list of indexed string attributes """ def __init__(self, data, parent=None): QWidget.__init__(self, parent) self.data = data vbox = QVBoxLayout() self.setLayout(vbox) title = QLabel('Indexed Strings') vbox.addWidget(title) self.frame = QFrame() vbox.addWidget(self.frame) self.vbox = QVBoxLayout() self.frame.setLayout(self.vbox) self.frame.setFrameShape(QFrame.Panel) self.frame.setFrameShadow(QFrame.Sunken) self.table = QTableWidget() self.table.horizontalHeader().hide() self.vbox.addWidget(self.table) self.table.hide() self.noStringsLabel = QLabel('<i>No indexed strings</i>') self.vbox.addWidget(self.noStringsLabel) self.widgets = [] self.populate() self.data.attributeAdded.connect(self.attributeAddedSlot) self.data.dataReset.connect(self.dataResetSlot) self.data.dirtied.connect(self.dataDirtiedSlot) def dataDirtiedSlot(self, dirty): """ SLOT when the particle data is dirtied or cleaned.""" if not dirty: for widget in self.widgets: widget.drawBorder(False) def dataResetSlot(self): """ SLOT when particle data is reconstructed """ self.populate() def attributeAddedSlot(self, name): #pylint:disable=W0613 """ SLOT when an attribute is added to the particle set """ attr = self.data.attributeInfo(name) if attr.type == partio.INDEXEDSTR: self.populate() def populate(self): """ Populates the table of indexed strings """ self.widgets = [] # If no widgets, just drop that in attrs = [] for anum in range(self.data.numAttributes()): attr = self.data.attributeInfo(anum) if attr.type == partio.INDEXEDSTR: attrs.append(attr) if not attrs: self.table.hide() self.noStringsLabel.show() return self.table.show() self.noStringsLabel.hide() self.table.setColumnCount(1) self.table.setRowCount(len(attrs)) for row, attr in enumerate(attrs): item = QTableWidgetItem(attr.name) self.table.setVerticalHeaderItem(row, item) strings = self.data.indexedStrs(attr) table = QTableWidget() table.setColumnCount(1) table.setRowCount(len(strings)) table.horizontalHeader().hide() table.setVerticalHeaderLabels([str(i) for i in range(len(strings))]) for i, string in enumerate(strings): widget = QLabel(string) table.setCellWidget(i, 0, widget) self.widgets.append(widget) self.table.setCellWidget(row, 0, table) self.table.horizontalHeader().setStretchLastSection(False) self.table.setTabKeyNavigation(True) self.table.horizontalHeader().setSectionsMovable(False) self.table.horizontalHeader().resizeSections(QHeaderView.ResizeToContents) self.table.verticalHeader().resizeSections(QHeaderView.ResizeToContents)
def ui(self): super(PreviewWidget, self).ui() title_frame = QFrame(self) title_frame_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) title_frame.setLayout(title_frame_layout) title_widget = QWidget(self) title_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_widget.setLayout(title_layout) buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) title_layout.addLayout(buttons_layout) self._title_icon = label.BaseLabel(parent=self) self._title_button = label.ElidedLabel(parent=self) self._title_button.setText(self.item().name()) self._menu_button = buttons.BaseButton(parent=self) self._menu_button.setIcon(resources.icon('menu_dots')) buttons_layout.addWidget(self._title_icon) buttons_layout.addStretch() buttons_layout.addWidget(self._title_button) buttons_layout.addStretch() buttons_layout.addWidget(self._menu_button) title_frame_layout.addWidget(title_widget) item_icon_name = self.item().icon() or 'tpDcc' item_icon = resources.icon(item_icon_name) if not item_icon: item_icon = resources.icon('tpDcc') self._title_icon.setPixmap(item_icon.pixmap(QSize(20, 20))) icon_title_frame = QFrame(self) icon_title_frame_layout = layouts.VerticalLayout(spacing=0, margins=(4, 2, 4, 2)) icon_title_frame.setLayout(icon_title_frame_layout) self._icon_group_frame = QFrame(self) icon_group_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._icon_group_frame.setLayout(icon_group_layout) self._icon_frame = QFrame(self) icon_frame_layout = layouts.VerticalLayout(spacing=0, margins=(4, 2, 4, 2)) self._icon_frame.setLayout(icon_frame_layout) icon_group_layout.addWidget(self._icon_frame) icon_group_box_widget = group.GroupBoxWidget('Icon', widget=self._icon_group_frame, parent=self) icon_title_frame_layout.addWidget(icon_group_box_widget) form_frame = QFrame(self) form_frame_layout = layouts.VerticalLayout(spacing=0, margins=(4, 2, 4, 2)) form_frame.setLayout(form_frame_layout) buttons_frame = QFrame(self) buttons_frame_layout = layouts.HorizontalLayout(spacing=4, margins=(4, 4, 4, 4)) buttons_frame.setLayout(buttons_frame_layout) self._accept_button = buttons.BaseButton('Load') self._accept_button.setIcon(resources.icon('open')) self._accept_button.setVisible(False) buttons_frame_layout.addWidget(self._accept_button) schema = self._item_view.item.load_schema() if self._item_view else None if schema: self._form_widget = formwidget.FormWidget(self) self._form_widget.setObjectName('{}Form'.format(self._item_view.__class__.__name__)) self._form_widget.set_schema(schema) self._form_widget.set_validator(self.validator) form_frame_layout.addWidget(self._form_widget) self.main_layout.addWidget(title_frame) self.main_layout.addWidget(icon_title_frame) self.main_layout.addWidget(self._icon_group_frame) self.main_layout.addWidget(form_frame) self.main_layout.addStretch() self.main_layout.addWidget(buttons_frame)
class FixedAttributesWidget(QWidget): """ A widget for viewing/editing fixed attributes (non-varying) """ def __init__(self, data, parent=None): QWidget.__init__(self, parent) self.data = data vbox = QVBoxLayout() self.setLayout(vbox) title = QLabel('Fixed Attributes') vbox.addWidget(title) self.frame = QFrame() vbox.addWidget(self.frame) self.vbox = QVBoxLayout() self.frame.setLayout(self.vbox) self.frame.setFrameShape(QFrame.Panel) self.frame.setFrameShadow(QFrame.Sunken) self.table = QTableWidget() self.table.horizontalHeader().hide() self.vbox.addWidget(self.table) self.table.hide() self.noAttrLabel = QLabel('<i>No fixed attributes</i>') self.vbox.addWidget(self.noAttrLabel) self.widgets = [] self.populate() self.data.fixedAttributeAdded.connect(self.fixedAttributeAddedSlot) self.data.dataReset.connect(self.dataResetSlot) self.data.dirtied.connect(self.dataDirtiedSlot) def dataDirtiedSlot(self, dirty): """ SLOT when the particle data is dirtied or cleaned.""" if not dirty: for widget in self.widgets: widget.drawBorder(False) def dataResetSlot(self): """ SLOT when particle data is reconstructed """ self.populate() def fixedAttributeAddedSlot(self, name): #pylint:disable=W0613 """ SLOT when a fixed attribute is added to the particle set """ self.populate() def populate(self): """ Populates the table of fixed attributes """ self.widgets = [] # If no widgets, just drop that in numAttrs = self.data.numFixedAttributes() if not numAttrs: self.table.hide() self.noAttrLabel.show() return self.table.show() self.noAttrLabel.hide() self.table.setColumnCount(1) self.table.setRowCount(numAttrs) self.attrs = getAttrs(self.data.numFixedAttributes, self.data.fixedAttributeInfo, True) for row, (_, attr) in enumerate(self.attrs): item = QTableWidgetItem(attr.name) tooltip = '<p><tt> Name: {}<br> Type: {}<br>Count: {}</tt></p>'.\ format(attr.name, partio.TypeName(attr.type), attr.count) item.setToolTip(tooltip) self.table.setVerticalHeaderItem(row, item) value = self.data.getFixed(attr) widget = getWidget(value, self.data, attr) self.table.setCellWidget(row, 0, widget) self.widgets.append(widget) self.table.horizontalHeader().setStretchLastSection(False) self.table.setTabKeyNavigation(True) self.table.horizontalHeader().setSectionsMovable(False) self.table.horizontalHeader().resizeSections(QHeaderView.ResizeToContents) self.table.verticalHeader().resizeSections(QHeaderView.ResizeToContents)
class FixedAttributesWidget(QWidget): """ A widget for viewing/editing fixed attributes (non-varying) """ def __init__(self, data, parent=None): QWidget.__init__(self, parent) self.data = data vbox = QVBoxLayout() self.setLayout(vbox) title = QLabel('Fixed Attributes') vbox.addWidget(title) self.frame = QFrame() vbox.addWidget(self.frame) self.vbox = QVBoxLayout() self.frame.setLayout(self.vbox) self.frame.setFrameShape(QFrame.Panel) self.frame.setFrameShadow(QFrame.Sunken) self.table = QTableWidget() self.table.horizontalHeader().hide() self.vbox.addWidget(self.table) self.table.hide() self.noAttrLabel = QLabel('<i>No fixed attributes</i>') self.vbox.addWidget(self.noAttrLabel) self.widgets = [] self.populate() self.data.fixedAttributeAdded.connect(self.fixedAttributeAddedSlot) self.data.dataReset.connect(self.dataResetSlot) self.data.dirtied.connect(self.dataDirtiedSlot) def dataDirtiedSlot(self, dirty): """ SLOT when the particle data is dirtied or cleaned.""" if not dirty: for widget in self.widgets: widget.drawBorder(False) def dataResetSlot(self): """ SLOT when particle data is reconstructed """ self.populate() def fixedAttributeAddedSlot(self, name): #pylint:disable=W0613 """ SLOT when a fixed attribute is added to the particle set """ self.populate() def populate(self): """ Populates the table of fixed attributes """ self.widgets = [] # If no widgets, just drop that in numAttrs = self.data.numFixedAttributes() if not numAttrs: self.table.hide() self.noAttrLabel.show() return self.table.show() self.noAttrLabel.hide() self.table.setColumnCount(1) self.table.setRowCount(numAttrs) self.attrs = getAttrs(self.data.numFixedAttributes, self.data.fixedAttributeInfo, True) for row, (_, attr) in enumerate(self.attrs): item = QTableWidgetItem(attr.name) tooltip = '<p><tt> Name: {}<br> Type: {}<br>Count: {}</tt></p>'.\ format(attr.name, partio.TypeName(attr.type), attr.count) item.setToolTip(tooltip) self.table.setVerticalHeaderItem(row, item) value = self.data.getFixed(attr) widget = getWidget(value, self.data, attr) self.table.setCellWidget(row, 0, widget) self.widgets.append(widget) self.table.horizontalHeader().setStretchLastSection(False) self.table.setTabKeyNavigation(True) self.table.horizontalHeader().setSectionsMovable(False) self.table.horizontalHeader().resizeSections( QHeaderView.ResizeToContents) self.table.verticalHeader().resizeSections( QHeaderView.ResizeToContents)
def ui(self): super(BaseSaveWidget, self).ui() self.setWindowTitle('Save Item') title_frame = QFrame(self) title_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_frame.setLayout(title_frame_layout) title_widget = QFrame(self) title_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) title_widget.setLayout(title_layout) title_buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) title_layout.addLayout(title_buttons_layout) title_icon = label.BaseLabel(parent=self) title_button = label.BaseLabel(self.item().menu_name(), parent=self) title_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._menu_button = buttons.BaseButton(parent=self) self._menu_button.setIcon(resources.icon('menu_dots')) self._menu_button.setVisible(False) # Hide by default title_buttons_layout.addWidget(title_icon) title_buttons_layout.addSpacing(5) title_buttons_layout.addWidget(title_button) title_buttons_layout.addWidget(self._menu_button) title_frame_layout.addWidget(title_widget) item_icon_name = self.item().icon() or 'tpDcc' item_icon = resources.icon(item_icon_name) if not item_icon: item_icon = resources.icon('tpDcc') title_icon.setPixmap(item_icon.pixmap(QSize(20, 20))) thumbnail_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) self._thumbnail_frame = QFrame(self) thumbnail_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 2, 0, 2)) self._thumbnail_frame.setLayout(thumbnail_frame_layout) thumbnail_layout.addWidget(self._thumbnail_frame) self._options_frame = QFrame(self) options_frame_layout = layouts.VerticalLayout(spacing=0, margins=(4, 2, 4, 2)) self._options_frame.setLayout(options_frame_layout) preview_buttons_frame = QFrame(self) self._preview_buttons_layout = layouts.HorizontalLayout(spacing=0, margins=(4, 2, 4, 2)) preview_buttons_frame.setLayout(self._preview_buttons_layout) self._save_button = buttons.BaseButton('Save', parent=self) self._save_button.setIcon(resources.icon('save')) self._cancel_button = buttons.BaseButton('Cancel', parent=self) self._cancel_button.setIcon(resources.icon('cancel')) self._preview_buttons_layout.addStretch() self._preview_buttons_layout.addWidget(self._save_button) self._preview_buttons_layout.addStretch() self._preview_buttons_layout.addWidget(self._cancel_button) self._preview_buttons_layout.addStretch() self.main_layout.addWidget(title_frame) self.main_layout.addLayout(thumbnail_layout) self.main_layout.addWidget(self._options_frame) self.main_layout.addWidget(preview_buttons_frame)
class LoadWidget(BaseLoadWidget, object): HISTORY_WIDGET = history.HistoryFileWidget OPTIONS_WIDGET = None def __init__(self, item, parent=None): self._icon_path = '' self._script_job = None self._options_widget = None super(LoadWidget, self).__init__(item, parent=parent) self.load_settings() self.create_sequence_widget() self.update_thumbnail_size() def ui(self): super(LoadWidget, self).ui() tabs_widget = tabs.BaseTabWidget(parent=self) info_widget = QWidget() info_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) info_widget.setLayout(info_layout) if self.OPTIONS_WIDGET: self._options_widget = self.OPTIONS_WIDGET(parent=self) title_layout = layouts.HorizontalLayout(spacing=1, margins=(1, 1, 0, 0)) self._icon_lbl = label.BaseLabel('', parent=self) self._icon_lbl.setMaximumSize(QSize(14, 14)) self._icon_lbl.setMinimumSize(QSize(14, 14)) self._icon_lbl.setScaledContents(True) self._title_lbl = label.BaseLabel('Title', parent=self) title_layout.addWidget(self._icon_lbl) title_layout.addWidget(self._title_lbl) icon_toggle_box = QFrame() icon_toggle_box.setFrameShape(QFrame.NoFrame) icon_toggle_box.setFrameShadow(QFrame.Plain) icon_toggle_box_lyt = layouts.VerticalLayout(spacing=1, margins=(0, 1, 0, 0)) icon_toggle_box.setLayout(icon_toggle_box_lyt) icon_toggle_box_header = QFrame() icon_toggle_box_header.setFrameShape(QFrame.NoFrame) icon_toggle_box_header.setFrameShadow(QFrame.Plain) icon_toggle_box_header_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_toggle_box_header.setLayout(icon_toggle_box_header_lyt) self._icon_toggle_box_btn = buttons.BaseButton('ICON', parent=self) self._icon_toggle_box_btn.setObjectName('iconButton') self._icon_toggle_box_btn.setCheckable(True) self._icon_toggle_box_btn.setChecked(True) icon_toggle_box_header_lyt.addWidget(self._icon_toggle_box_btn) self._icon_toggle_box_frame = QFrame() self._icon_toggle_box_frame.setFrameShape(QFrame.NoFrame) self._icon_toggle_box_frame.setFrameShadow(QFrame.Plain) icon_toggle_box_frame_lyt = layouts.VerticalLayout(spacing=1, margins=(0, 1, 0, 0)) self._icon_toggle_box_frame.setLayout(icon_toggle_box_frame_lyt) thumbnail_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_toggle_box_frame_lyt.addLayout(thumbnail_layout) thumbnail_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._thumbnail_frame = QFrame() self._thumbnail_frame.setFrameShape(QFrame.NoFrame) self._thumbnail_frame.setFrameShadow(QFrame.Plain) self._thumbnail_frame.setLayout(thumbnail_frame_layout) thumbnail_layout.addWidget(self._thumbnail_frame) self._thumbnail_btn = QToolButton() self._thumbnail_btn.setObjectName('thumbnailButton') self._thumbnail_btn.setMinimumSize(QSize(0, 0)) self._thumbnail_btn.setMaximumSize(QSize(150, 150)) self._thumbnail_btn.setStyleSheet( 'color: rgb(40, 40, 40);\nborder: 1px solid rgb(0, 0, 0, 0);\nbackground-color: rgb(254, 255, 230, 0);' ) self._thumbnail_btn.setLayoutDirection(Qt.LeftToRight) self._thumbnail_btn.setText('Snapshot') self._thumbnail_btn.setIcon(resources.icon('thumbnail')) thumbnail_frame_layout.addWidget(self._thumbnail_btn) icon_toggle_box_lyt.addWidget(icon_toggle_box_header) icon_toggle_box_lyt.addWidget(self._icon_toggle_box_frame) info_toggle_box = QFrame() info_toggle_box.setFrameShape(QFrame.NoFrame) info_toggle_box.setFrameShadow(QFrame.Plain) info_toggle_box_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) info_toggle_box.setLayout(info_toggle_box_lyt) info_toggle_box_header = QFrame() info_toggle_box_header.setFrameShape(QFrame.NoFrame) info_toggle_box_header.setFrameShadow(QFrame.Plain) info_toggle_box_header_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) info_toggle_box_header.setLayout(info_toggle_box_header_lyt) self._info_toggle_box_btn = buttons.BaseButton('INFO', parent=self) self._info_toggle_box_btn.setObjectName('infoButton') self._info_toggle_box_btn.setCheckable(True) self._info_toggle_box_btn.setChecked(True) info_toggle_box_header_lyt.addWidget(self._info_toggle_box_btn) self._info_toggle_box_frame = QFrame() self._info_toggle_box_frame.setFrameShape(QFrame.NoFrame) self._info_toggle_box_frame.setFrameShadow(QFrame.Plain) info_toggle_box_frame_lyt = layouts.VerticalLayout(spacing=1, margins=(0, 1, 0, 0)) self._info_toggle_box_frame.setLayout(info_toggle_box_frame_lyt) self._info_frame = QFrame() self._info_frame.setFrameShape(QFrame.NoFrame) self._info_frame.setFrameShadow(QFrame.Plain) info_frame_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._info_frame.setLayout(info_frame_lyt) info_toggle_box_frame_lyt.addWidget(self._info_frame) info_toggle_box_lyt.addWidget(info_toggle_box_header) info_toggle_box_lyt.addWidget(self._info_toggle_box_frame) version_toggle_box = QFrame() version_toggle_box.setFrameShape(QFrame.NoFrame) version_toggle_box.setFrameShadow(QFrame.Plain) version_toggle_box_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) version_toggle_box.setLayout(version_toggle_box_lyt) version_toggle_box_header = QFrame() version_toggle_box_header.setFrameShape(QFrame.NoFrame) version_toggle_box_header.setFrameShadow(QFrame.Plain) version_toggle_box_header_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) version_toggle_box_header.setLayout(version_toggle_box_header_lyt) self._version_toggle_box_btn = buttons.BaseButton('VERSION', parent=self) self._version_toggle_box_btn.setObjectName('versionButton') self._version_toggle_box_btn.setCheckable(True) self._version_toggle_box_btn.setChecked(True) version_toggle_box_header_lyt.addWidget(self._version_toggle_box_btn) self._version_toggle_box_frame = QFrame() self._version_toggle_box_frame.setFrameShape(QFrame.NoFrame) self._version_toggle_box_frame.setFrameShadow(QFrame.Plain) version_toggle_box_frame_lyt = layouts.VerticalLayout(spacing=1, margins=(0, 1, 0, 0)) self._version_toggle_box_frame.setLayout(version_toggle_box_frame_lyt) self._version_frame = QFrame() self._version_frame.setFrameShape(QFrame.NoFrame) self._version_frame.setFrameShadow(QFrame.Plain) version_frame_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._version_frame.setLayout(version_frame_lyt) version_toggle_box_frame_lyt.addWidget(self._version_frame) self._history_widget = self.HISTORY_WIDGET() version_frame_lyt.addWidget(self._history_widget) version_toggle_box_lyt.addWidget(version_toggle_box_header) version_toggle_box_lyt.addWidget(self._version_toggle_box_frame) preview_buttons_frame = QFrame() preview_buttons_frame.setObjectName('previewButtons') preview_buttons_frame.setFrameShape(QFrame.NoFrame) preview_buttons_frame.setFrameShadow(QFrame.Plain) self._preview_buttons_frame_lyt = layouts.VerticalLayout(spacing=0, margins=(2, 2, 2, 2)) self._preview_buttons_lyt = layouts.HorizontalLayout(spacing=2, margins=(0, 0, 0, 0)) self._load_btn = buttons.BaseButton('Load', parent=self) self._load_btn.setObjectName('loadButton') self._load_btn.setMinimumSize(QSize(60, 35)) self._load_btn.setMaximumSize(QSize(125, 35)) self._preview_buttons_frame_lyt.addStretch() self._preview_buttons_frame_lyt.addLayout(self._preview_buttons_lyt) self._preview_buttons_frame_lyt.addStretch() self._preview_buttons_lyt.addWidget(self._load_btn) preview_buttons_frame.setLayout(self._preview_buttons_frame_lyt) info_layout.addLayout(title_layout) info_layout.addWidget(icon_toggle_box) info_layout.addWidget(info_toggle_box) info_layout.addWidget(version_toggle_box) tabs_widget.addTab(info_widget, 'Info') if self._options_widget: tabs_widget.addTab(self._options_widget, 'Options') self.main_layout.addWidget(tabs_widget) self.main_layout.addWidget(dividers.Divider()) self.main_layout.addWidget(preview_buttons_frame) self.main_layout.addItem( QSpacerItem(0, 250, QSizePolicy.Preferred, QSizePolicy.Expanding)) def setup_signals(self): self._info_toggle_box_btn.clicked.connect(self.save_settings) self._info_toggle_box_btn.toggled[bool].connect( self._info_toggle_box_frame.setVisible) self._icon_toggle_box_btn.clicked.connect(self.save_settings) self._icon_toggle_box_btn.toggled[bool].connect( self._icon_toggle_box_frame.setVisible) self._version_toggle_box_btn.clicked.connect(self.save_settings) self._version_toggle_box_btn.toggled[bool].connect( self._version_toggle_box_frame.setVisible) self._load_btn.clicked.connect(self.load) def resizeEvent(self, event): """ Function that overrides base.BaseWidget function :param event: QSizeEvent """ self.update_thumbnail_size() def set_item(self, item): """ Sets the library item to load :param item: LibraryItem """ super(LoadWidget, self).set_item(item) self._title_lbl.setText(item.MenuName) self._icon_lbl.setPixmap(QPixmap(item.type_icon_path())) info_widget = formwidget.FormWidget(self) info_widget.set_schema(item.info()) self._info_frame.layout().addWidget(info_widget) self.refresh() def load_btn(self): """ Returns button that loads the data :return: QPushButton """ return self._load_btn def icon_path(self): """ Returns the icon path to be used for the thumbnail :return: str """ return self._icon_path def set_icon_path(self, path): """ Sets the icon path to be used for the thumbnail :param path: str """ self._icon_path = path icon = QIcon(QPixmap(path)) self.set_icon(icon) self.update_thumbnail_size() self.item().update() def set_icon(self, icon): """ Sets the icon to be shown for the preview :param icon: QIcon """ self._thumbnail_btn.setIcon(icon) self._thumbnail_btn.setIconSize(QSize(200, 200)) self._thumbnail_btn.setText('') def refresh(self): """ Refreshes load widgetz """ self.update_history() self.update_options() def update_history(self): """ Updates history version of the current selected item """ if not self._item: return data_object = self._item.data_object() if not data_object: return self._history_widget.set_directory(data_object.directory) self._history_widget.refresh() def update_options(self): """ Updates options widget """ if not self._options_widget: return if not self._item: return data_object = self._item.data_object() if not data_object: return self._options_widget.set_data_object(data_object) def is_editable(self): """ Returns whether the user can edit the item or not :return: bool """ item = self.item() editable = True if item and item.library_window(): editable = not item.library_window().is_locked() return editable def create_sequence_widget(self): """ Creates a sequence widget to replace the static thumbnail widget """ self._sequence_widget = widgets.LibraryImageSequenceWidget(self) self._sequence_widget.setStyleSheet(self._thumbnail_btn.styleSheet()) self._sequence_widget.setToolTip(self._thumbnail_btn.toolTip()) self._thumbnail_frame.layout().insertWidget(0, self._sequence_widget) self._thumbnail_btn.hide() self._thumbnail_btn = self._sequence_widget path = self.item().thumbnail_path() if path and os.path.exists(path): self.set_icon_path(path) if self.item().image_sequence_path(): self._sequence_widget.set_dirname( self.item().image_sequence_path()) def update_thumbnail_size(self): """ Updates the thumbnail button to the size of the widget """ width = self.width() - 10 if width > 250: width = 250 size = QSize(width, width) self._thumbnail_btn.setIconSize(size) self._thumbnail_btn.setMaximumSize(size) self._thumbnail_frame.setMaximumSize(size) def settings(self): """ Returns the current state of the widget :return: dict """ settings = dict() settings['iconToggleBoxChecked'] = self._icon_toggle_box_btn.isChecked( ) settings['infoToggleBoxChecked'] = self._info_toggle_box_btn.isChecked( ) settings[ 'versionToggleBoxChecked'] = self._version_toggle_box_btn.isChecked( ) return settings def save_settings(self): pass def load_settings(self): pass def load(self): """ Loads current item """ if not self.item(): return self.item().load_from_current_options()
def ui(self): super(LoadWidget, self).ui() tabs_widget = tabs.BaseTabWidget(parent=self) info_widget = QWidget() info_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) info_widget.setLayout(info_layout) if self.OPTIONS_WIDGET: self._options_widget = self.OPTIONS_WIDGET(parent=self) title_layout = layouts.HorizontalLayout(spacing=1, margins=(1, 1, 0, 0)) self._icon_lbl = label.BaseLabel('', parent=self) self._icon_lbl.setMaximumSize(QSize(14, 14)) self._icon_lbl.setMinimumSize(QSize(14, 14)) self._icon_lbl.setScaledContents(True) self._title_lbl = label.BaseLabel('Title', parent=self) title_layout.addWidget(self._icon_lbl) title_layout.addWidget(self._title_lbl) icon_toggle_box = QFrame() icon_toggle_box.setFrameShape(QFrame.NoFrame) icon_toggle_box.setFrameShadow(QFrame.Plain) icon_toggle_box_lyt = layouts.VerticalLayout(spacing=1, margins=(0, 1, 0, 0)) icon_toggle_box.setLayout(icon_toggle_box_lyt) icon_toggle_box_header = QFrame() icon_toggle_box_header.setFrameShape(QFrame.NoFrame) icon_toggle_box_header.setFrameShadow(QFrame.Plain) icon_toggle_box_header_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_toggle_box_header.setLayout(icon_toggle_box_header_lyt) self._icon_toggle_box_btn = buttons.BaseButton('ICON', parent=self) self._icon_toggle_box_btn.setObjectName('iconButton') self._icon_toggle_box_btn.setCheckable(True) self._icon_toggle_box_btn.setChecked(True) icon_toggle_box_header_lyt.addWidget(self._icon_toggle_box_btn) self._icon_toggle_box_frame = QFrame() self._icon_toggle_box_frame.setFrameShape(QFrame.NoFrame) self._icon_toggle_box_frame.setFrameShadow(QFrame.Plain) icon_toggle_box_frame_lyt = layouts.VerticalLayout(spacing=1, margins=(0, 1, 0, 0)) self._icon_toggle_box_frame.setLayout(icon_toggle_box_frame_lyt) thumbnail_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0)) icon_toggle_box_frame_lyt.addLayout(thumbnail_layout) thumbnail_frame_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._thumbnail_frame = QFrame() self._thumbnail_frame.setFrameShape(QFrame.NoFrame) self._thumbnail_frame.setFrameShadow(QFrame.Plain) self._thumbnail_frame.setLayout(thumbnail_frame_layout) thumbnail_layout.addWidget(self._thumbnail_frame) self._thumbnail_btn = QToolButton() self._thumbnail_btn.setObjectName('thumbnailButton') self._thumbnail_btn.setMinimumSize(QSize(0, 0)) self._thumbnail_btn.setMaximumSize(QSize(150, 150)) self._thumbnail_btn.setStyleSheet( 'color: rgb(40, 40, 40);\nborder: 1px solid rgb(0, 0, 0, 0);\nbackground-color: rgb(254, 255, 230, 0);' ) self._thumbnail_btn.setLayoutDirection(Qt.LeftToRight) self._thumbnail_btn.setText('Snapshot') self._thumbnail_btn.setIcon(resources.icon('thumbnail')) thumbnail_frame_layout.addWidget(self._thumbnail_btn) icon_toggle_box_lyt.addWidget(icon_toggle_box_header) icon_toggle_box_lyt.addWidget(self._icon_toggle_box_frame) info_toggle_box = QFrame() info_toggle_box.setFrameShape(QFrame.NoFrame) info_toggle_box.setFrameShadow(QFrame.Plain) info_toggle_box_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) info_toggle_box.setLayout(info_toggle_box_lyt) info_toggle_box_header = QFrame() info_toggle_box_header.setFrameShape(QFrame.NoFrame) info_toggle_box_header.setFrameShadow(QFrame.Plain) info_toggle_box_header_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) info_toggle_box_header.setLayout(info_toggle_box_header_lyt) self._info_toggle_box_btn = buttons.BaseButton('INFO', parent=self) self._info_toggle_box_btn.setObjectName('infoButton') self._info_toggle_box_btn.setCheckable(True) self._info_toggle_box_btn.setChecked(True) info_toggle_box_header_lyt.addWidget(self._info_toggle_box_btn) self._info_toggle_box_frame = QFrame() self._info_toggle_box_frame.setFrameShape(QFrame.NoFrame) self._info_toggle_box_frame.setFrameShadow(QFrame.Plain) info_toggle_box_frame_lyt = layouts.VerticalLayout(spacing=1, margins=(0, 1, 0, 0)) self._info_toggle_box_frame.setLayout(info_toggle_box_frame_lyt) self._info_frame = QFrame() self._info_frame.setFrameShape(QFrame.NoFrame) self._info_frame.setFrameShadow(QFrame.Plain) info_frame_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._info_frame.setLayout(info_frame_lyt) info_toggle_box_frame_lyt.addWidget(self._info_frame) info_toggle_box_lyt.addWidget(info_toggle_box_header) info_toggle_box_lyt.addWidget(self._info_toggle_box_frame) version_toggle_box = QFrame() version_toggle_box.setFrameShape(QFrame.NoFrame) version_toggle_box.setFrameShadow(QFrame.Plain) version_toggle_box_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) version_toggle_box.setLayout(version_toggle_box_lyt) version_toggle_box_header = QFrame() version_toggle_box_header.setFrameShape(QFrame.NoFrame) version_toggle_box_header.setFrameShadow(QFrame.Plain) version_toggle_box_header_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) version_toggle_box_header.setLayout(version_toggle_box_header_lyt) self._version_toggle_box_btn = buttons.BaseButton('VERSION', parent=self) self._version_toggle_box_btn.setObjectName('versionButton') self._version_toggle_box_btn.setCheckable(True) self._version_toggle_box_btn.setChecked(True) version_toggle_box_header_lyt.addWidget(self._version_toggle_box_btn) self._version_toggle_box_frame = QFrame() self._version_toggle_box_frame.setFrameShape(QFrame.NoFrame) self._version_toggle_box_frame.setFrameShadow(QFrame.Plain) version_toggle_box_frame_lyt = layouts.VerticalLayout(spacing=1, margins=(0, 1, 0, 0)) self._version_toggle_box_frame.setLayout(version_toggle_box_frame_lyt) self._version_frame = QFrame() self._version_frame.setFrameShape(QFrame.NoFrame) self._version_frame.setFrameShadow(QFrame.Plain) version_frame_lyt = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._version_frame.setLayout(version_frame_lyt) version_toggle_box_frame_lyt.addWidget(self._version_frame) self._history_widget = self.HISTORY_WIDGET() version_frame_lyt.addWidget(self._history_widget) version_toggle_box_lyt.addWidget(version_toggle_box_header) version_toggle_box_lyt.addWidget(self._version_toggle_box_frame) preview_buttons_frame = QFrame() preview_buttons_frame.setObjectName('previewButtons') preview_buttons_frame.setFrameShape(QFrame.NoFrame) preview_buttons_frame.setFrameShadow(QFrame.Plain) self._preview_buttons_frame_lyt = layouts.VerticalLayout(spacing=0, margins=(2, 2, 2, 2)) self._preview_buttons_lyt = layouts.HorizontalLayout(spacing=2, margins=(0, 0, 0, 0)) self._load_btn = buttons.BaseButton('Load', parent=self) self._load_btn.setObjectName('loadButton') self._load_btn.setMinimumSize(QSize(60, 35)) self._load_btn.setMaximumSize(QSize(125, 35)) self._preview_buttons_frame_lyt.addStretch() self._preview_buttons_frame_lyt.addLayout(self._preview_buttons_lyt) self._preview_buttons_frame_lyt.addStretch() self._preview_buttons_lyt.addWidget(self._load_btn) preview_buttons_frame.setLayout(self._preview_buttons_frame_lyt) info_layout.addLayout(title_layout) info_layout.addWidget(icon_toggle_box) info_layout.addWidget(info_toggle_box) info_layout.addWidget(version_toggle_box) tabs_widget.addTab(info_widget, 'Info') if self._options_widget: tabs_widget.addTab(self._options_widget, 'Options') self.main_layout.addWidget(tabs_widget) self.main_layout.addWidget(dividers.Divider()) self.main_layout.addWidget(preview_buttons_frame) self.main_layout.addItem( QSpacerItem(0, 250, QSizePolicy.Preferred, QSizePolicy.Expanding))
class FormWidget(QFrame, object): accepted = Signal(object) stateChanged = Signal() validated = Signal() def __init__(self, *args, **kwargs): super(FormWidget, self).__init__(*args, **kwargs) self._schema = dict() self._widgets = list() self._validator = None main_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self.setLayout(main_layout) self._fields_frame = QFrame(self) self._fields_frame.setObjectName('fieldsFrame') options_layout = layouts.VerticalLayout(spacing=0, margins=(0, 0, 0, 0)) self._fields_frame.setLayout(options_layout) self._title_widget = buttons.BaseButton(parent=self) self._title_widget.setCheckable(True) self._title_widget.setObjectName('titleWidget') self._title_widget.toggled.connect(self._on_title_clicked) self._title_widget.hide() main_layout.addWidget(self._title_widget) main_layout.addWidget(self._fields_frame) # ================================================================================================================= # OVERRIDES # ================================================================================================================= def closeEvent(self, event): self.save_persistent_values() super(FormWidget, self).closeEvent(event) # ================================================================================================================= # BASE # ================================================================================================================= def title_widget(self): """ Returns the title widget :return: QWidget """ return self._title_widget def set_title(self, title): """ Sets the title text :param title: str """ self.title_widget().setText(title) def is_expanded(self): """ Returns whether the item is expanded or not :return: bool """ return self._title_widget.isChecked() def set_expanded(self, flag): """ Expands the options if True, otherwise collapses the options :param flag: bool """ with qt_contexts.block_signals(self._title_widget): self._title_widget.setChecked(flag) self._fields_frame.setVisible(flag) def set_title_visible(self, flag): """ Sets whether the title widget is visible or not :param flag: bool """ self.title_widget().setVisible(flag) def widget(self, name): """ Returns the widget for the given widget name :param name: str :return: FieldWidget """ for widget in self._widgets: if widget.data().get('name') == name: return widget def value(self, name): """ Returns the value for the given widget name :param name: str :return: object """ widget = self.widget(name) if not widget: return None return widget.value() def set_value(self, name, value): """ Sets the value for the given field name :param name: str :param value: variant """ widget = self.widget(name) widget.set_value(value) def values(self): """ Returns all the field values indexed by the field name :return: dict """ values = dict() for widget in self._widgets: name = widget.data().get('name') if name: values[name] = widget.value() return values def set_values(self, values): """ Sets the field values for the current form :param values: dict """ state = list() for name in values: state.append({'name': name, 'value': values[name]}) self._set_state(state) def default_values(self): """ Returns all teh default field values indexed by the field name :return: dict """ values = dict() for widget in self._widgets: name = widget.data().get('name') if name: values[name] = widget.default() return values def set_data(self, name, data): """ Sets the data for the given field name :param name: str :param data: dict """ widget = self.widget(name) if not widget: return widget.set_data(data) def fields(self): """ Returns fields data for the form :return: list(dict) """ options = list() for widget in self._widgets: options.append(widget.data()) return options def field_widgets(self): """ Returns all field widgets :return: list(FieleWidget) """ return self._widgets def state(self): """ Returns the current state :return: dict """ fields = list() for widget in self._widgets: fields.append(widget.state()) state = { 'fields': fields, 'expanded': self.is_expanded() } return state def set_state(self, state): """ Sets the current state :param state: dict """ expanded = state.get('expanded') if expanded is not None: self.set_expanded(expanded) fields = state.get('fields') if fields is not None: self._set_state(fields) self.validate() def schema(self): """ Returns form's schema :return: dict """ return self._schema def set_schema(self, schema, layout=None, errors_visible=False): """ Sets the schema for the widget :param schema: list(dict) :param layout: str :param errors_visible: str """ self._schema = self._sort_schema(schema) if not self._schema: return for field in schema: cls = formfields.FIELD_WIDGET_REGISTRY.get(field.get('type', 'label')) if not cls: LOGGER.warning('Cannot find widget for {}'.format(field)) continue if layout and not field.get('layout'): field['layout'] = layout enabled = field.get('enabled', True) read_only = field.get('readOnly', False) error_visible = field.get('errorVisible') field['errorVisible'] = error_visible if error_visible is not None else errors_visible widget = cls(data=field, parent=self._fields_frame, form_widget=self) data = widget.default_data() data.update(field) widget.set_data(data) value = field.get('value') default = field.get('default') if value is None and default is not None: widget.set_value(default) if not enabled or read_only: widget.setEnabled(False) self._widgets.append(widget) callback = partial(self._on_field_changed, widget) widget.valueChanged.connect(callback) self._fields_frame.layout().addWidget(widget) self.load_persistent_values() def validator(self): """ Returns the validator for the form :return: fn """ return self._validator def set_validator(self, validator): """ Sets the validator for the options :param validator: fn """ self._validator = validator def reset(self): """ Reset all option widget back to the ir default values """ for widget in self._widgets: widget.reset() self.validate() def validate(self, widget=None): """ Validates the current options using the validator """ if not self._validator: return values = dict() for name, value in self.values().items(): data = self.widget(name).data() if data.get('validate', True): values[name] = value if widget: values['fieldChanged'] = widget.name() fields = self._validator(**values) if fields is not None: self._set_state(fields) self.validated.emit() def errors(self): """ Returns all form errors :return: list(str) """ errors = list() for widget in self._widgets: error = widget.data().get('error') if error: errors.append(error) return errors def has_errors(self): """ Returns whether the form contains any error :return: bool """ return bool(self.errors()) def save_persistent_values(self): """ Saves form widget values Triggered when the user changes field values """ data = dict() for widget in self._widgets: name = widget.data().get('name') if name and widget.data().get('persistent'): key = self.objectName() or 'FormWidget' key = widget.data().get('persistentKey', key) data.setdefault(key, dict()) data[key][name] = widget.value() for key in data: settings.set(key, data[key]) def load_persistent_values(self): """ Returns the options from the user settings :return: dict """ values = dict() default_values = self.default_values() for field in self.schema(): name = field.get('name') persistent = field.get('persistent') if persistent: key = self.objectName() or 'FormWidget' key = field.get('persistentKey', key) value = settings.get(key, dict()).get(name) else: value = default_values.get(name) if value is not None: values[name] = value self.set_values(values) # ============================================================================================================ # INTERNAL # ============================================================================================================ def _sort_schema(self, schema): """ Internal function that sorts the schema depending on the group order :param schema: list(dict) :return: list(dict) """ def _key(field): return field['order'] order = 0 if not schema: return for i, field in enumerate(schema): if field.get('type') == 'group': order = field.get('order', order) field['order'] = order return sorted(schema, key=_key) def _set_state(self, fields): """ Internal function that sets fields state :param fields: list(dict) """ for widget in self._widgets: widget.blockSignals(True) try: for widget in self._widgets: widget.set_error('') for field in fields: if field.get('name') == widget.data().get('name'): widget.set_data(field) finally: for widget in self._widgets: widget.blockSignals(False) self.stateChanged.emit() # ============================================================================================================ # CALLBACKS # ============================================================================================================ def _on_title_clicked(self, toggle): """ Internal callback function that is triggered when the user clicks in the title widget """ self.set_expanded(toggle) self.stateChanged.emit() def _on_field_changed(self, widget): """ Internal callback function triggered when the given option widget changes its value :param widget: FieldWidget """ self.validate(widget=widget)
class SaveWidget(BaseSaveWidget, object): def __init__(self, item, settings, temp_path=None, parent=None): self._script_job = None self._sequence_path = None self._icon_path = '' super(SaveWidget, self).__init__(item=item, settings=settings, temp_path=temp_path, parent=parent) self.create_sequence_widget() self.update_thumbnail_size() try: self._on_selection_changed() # self.set_script_job_enabled(True) except NameError as e: LOGGER.error('{} | {}'.format(e, traceback.format_exc())) def ui(self): super(SaveWidget, self).ui() model_panel_layout = layouts.HorizontalLayout() model_panel_layout.setContentsMargins(0, 0, 0, 0) model_panel_layout.setSpacing(0) thumbnail_layout = layouts.VerticalLayout() thumbnail_layout.setContentsMargins(0, 0, 0, 0) thumbnail_layout.setSpacing(0) self._thumbnail_frame = QFrame() self._thumbnail_frame.setMinimumSize(QSize(50, 50)) self._thumbnail_frame.setMaximumSize(QSize(150, 150)) self._thumbnail_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._thumbnail_frame.setFrameShape(QFrame.NoFrame) self._thumbnail_frame.setFrameShadow(QFrame.Plain) self._thumbnail_frame.setLineWidth(0) self._thumbnail_frame.setLayout(thumbnail_layout) model_panel_layout.addWidget(self._thumbnail_frame) self._thumbnail_btn = QPushButton() self._thumbnail_btn.setMinimumSize(QSize(0, 0)) self._thumbnail_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._thumbnail_btn.setMaximumSize(QSize(150, 150)) self._thumbnail_btn.setToolTip('Take snapshot') self._thumbnail_btn.setStyleSheet( 'color: rgb(40, 40, 40);border: 0px solid rgb(0, 0, 0, 150);background-color: rgb(254, 255, 230, 200);' ) self._thumbnail_btn.setIcon(resources.icon('thumbnail')) self._thumbnail_btn.setToolTip(""" Click to capture a thumbnail from the current viewport.\n CTRL + Click to show the capture window for better framing """) thumbnail_layout.addWidget(self._thumbnail_btn) self._extra_layout.addLayout(model_panel_layout) def setup_signals(self): super(SaveWidget, self).setup_signals() self._thumbnail_btn.clicked.connect(self._on_thumbnail_capture) def resizeEvent(self, event): """ Overrides base QWidget resizeEvent function :param event: QResizeEvent """ self.update_thumbnail_size() def icon_path(self): """ Returns the icon path to be used for the thumbnail :return: str """ return self._icon_path def set_icon(self, icon): """ Sets the icon for the create widget thumbnail :param icon: QIcon """ self._thumbnail_btn.setIcon(icon) self._thumbnail_btn.setIconSize(QSize(200, 200)) self._thumbnail_btn.setText('') def sequence_path(self): """ Returns the playblast path :return: str """ return self._sequence_path def set_sequence_path(self, path): """ Sets the disk location for the image sequence to be saved :param path: str """ self._sequence_path = path self._thumbnail_btn.set_dirname(os.path.dirname(path)) def create_sequence_widget(self): """ Creates a sequence widget to replace the static thumbnail widget """ sequence_widget = widgets.LibraryImageSequenceWidget(self) sequence_widget.setObjectName('thumbnailButton') sequence_widget.setStyleSheet(self._thumbnail_btn.styleSheet()) sequence_widget.setToolTip(self._thumbnail_btn.toolTip()) camera_icon = resources.get('icons', 'camera.svg') expand_icon = resources.get('icons', 'expand.svg') folder_icon = resources.get('icons', 'folder.svg') sequence_widget.addAction(camera_icon, 'Capture new image', 'Capture new image', self._on_thumbnail_capture) sequence_widget.addAction(expand_icon, 'Show Capture window', 'Show Capture window', self._on_show_capture_window) sequence_widget.addAction(folder_icon, 'Load image from disk', 'Load image from disk', self._on_show_browse_image_dialog) sequence_widget.setIcon(resources.icon('thumbnail2')) self._thumbnail_frame.layout().insertWidget(0, sequence_widget) self._thumbnail_btn.hide() self._thumbnail_btn = sequence_widget self._thumbnail_btn.clicked.connect(self._on_thumbnail_capture) def set_sequence(self, source): """ Sets the sequenced path for the thumbnail widget :param source: str """ self.set_thumbnail(source, sequence=True) def set_thumbnail(self, source, sequence=False): """ Sets the thumbnail :param source: str :param sequence: bool """ source = os.path.normpath(source) # TODO: Find a way to remove temp folder afteer saving the file # filename, extension = os.path.splitext(source) # with path_utils.temp_dir() as dir_path: # dir_path = path_utils.temp_dir() # target = os.path.join(dir_path, 'thumbnail{}'.format(extension)) # shutil.copyfile(source, target) # tpQtLib.logger.debug('Source Thumbnail: {}'.format(source)) # tpQtLib.logger.debug('Target Thumbnail: {}'.format(target)) # self._icon_path = target # self._thumbnail_btn.set_path(target) self._icon_path = source self._thumbnail_btn.set_path(source) if sequence: self.set_sequence_path(source) def update_thumbnail_size(self): """ Updates the thumbnail button to teh size of the widget """ width = self.width() - 10 if width > 250: width = 250 size = QSize(width, width) self._thumbnail_btn.setIconSize(size) self._thumbnail_btn.setMaximumSize(size) self._thumbnail_frame.setMaximumSize(size) def show_by_frame_dialog(self): """ Show the by frame dialog """ help_text = """ To help speed up the playblast you can set the "by frame" to another greather than 1. For example if the "by frame" is set to 2 it will playblast every second frame """ options = self._options_widget.values() by_frame = options.get('byFrame', 1) start_frame, end_frame = options.get('frameRange', [None, None]) duration = 1 if start_frame is not None and end_frame is not None: duration = end_frame - start_frame if duration > 100 and by_frame == 1: buttons = QDialogButtonBox.Ok | QDialogButtonBox.Cancel result = messagebox.MessageBox.question( self.library_window(), title='Tip', text=help_text, buttons=buttons, enable_dont_show_checkbox=True) if result != QDialogButtonBox.Ok: raise Exception('Cancelled by user') def show_thumbnail_capture_dialog(self): """ Asks the user if they would like to capture a thumbnail :return: int """ buttons = QDialogButtonBox.Yes | QDialogButtonBox.Ignore | QDialogButtonBox.Cancel parent = self.item().library_window() btn = messagebox.MessageBox.question( None, 'Create a thumbnail', 'Would you like to capture a thumbnail?', buttons=buttons) if btn == QDialogButtonBox.Yes: self.thumbnail_capture() return btn def thumbnail_capture(self, show=False): """ Captures a playblast and saves it to the temporal thumbnail path :param show: bool """ options = self._options_widget.values() start_frame, end_frame = options.get('frameRange', [None, None]) step = options.get('byFrame', 1) if not qtutils.is_control_modifier(): self.show_by_frame_dialog() if not self._temp_path or not os.path.isdir(self._temp_path): self._temp_path = tempfile.mkdtemp() self._temp_path = os.path.join(self._temp_path, 'thumbnail.jpg') try: snapshot.SnapshotWindow(path=self._temp_path, on_save=self._on_thumbnail_captured) # thumbnail.ThumbnailCaptureDialog.thumbnail_capture( # path=self._temp_path, # show=show, # start_frame=start_frame, # end_frame=end_frame, # step=step, # clear_cache=False, # captured=self._on_thumbnail_captured # ) except Exception as e: messagebox.MessageBox.critical(self.library_window(), 'Error while capturing thumbnail', str(e)) LOGGER.error(traceback.format_exc()) def save(self, path, icon_path, objects=None): """ Saves the item with the given objects to the given disk location path :param path: list(str) :param icon_path: str :param objects: str """ item = self.item() options = self._options_widget.values() sequence_path = self.sequence_path() if sequence_path: sequence_path = os.path.dirname(sequence_path) item.save(path=path, objects=objects, icon_path=icon_path, sequence_path=sequence_path, **options) self.close() def _on_selection_changed(self): """ Internal callback functino that is called when DCC selection changes """ if self._options_widget: self._options_widget.validate() def _on_thumbnail_capture(self): self.thumbnail_capture(show=False) def _on_thumbnail_captured(self, captured_path): """ Internal callback function that is called when thumbnail is captured :param captured_path: str """ self.set_sequence(captured_path) def _on_show_capture_window(self): """ Internal callback function that shows the capture window for framing """ self.thumbnail_capture(show=True) def _on_show_browse_image_dialog(self): """ Internal callback function that shows a file dialog for choosing an image from disk """ file_dialog = QFileDialog(self, caption='Open Image', filter='Image Files (*.png *.jpg)') file_dialog.fileSelected.connect(self.set_thumbnail) file_dialog.exec_()