Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
class Divider(QWidget, object):

    _ALIGN_MAP = {Qt.AlignCenter: 50, Qt.AlignLeft: 20, Qt.AlignRight: 80}

    def __init__(self,
                 text=None,
                 shadow=True,
                 orientation=Qt.Horizontal,
                 alignment=Qt.AlignLeft,
                 parent=None):
        """
        Basic standard splitter with optional text
        :param str text: Optional text to include as title in the splitter
        :param bool shadow: True if you want a shadow above the splitter
        :param Qt.Orientation orientation: Orientation of the splitter
        :param Qt.Align alignment: Alignment of the splitter
        :param QWidget parent: Parent of the splitter
        """

        super(Divider, self).__init__(parent=parent)

        self._orient = orientation
        self._text = None

        main_layout = layouts.HorizontalLayout(spacing=0, margins=(0, 0, 0, 0))
        self.setLayout(main_layout)

        self._label = label.BaseLabel().strong(True)

        first_line = QFrame()
        self._second_line = QFrame()

        main_layout.addWidget(first_line)
        main_layout.addWidget(self._label)
        main_layout.addWidget(self._second_line)

        if orientation == Qt.Horizontal:
            first_line.setFrameShape(QFrame.HLine)
            first_line.setFrameShadow(QFrame.Sunken)
            first_line.setFixedHeight(
                2) if shadow else first_line.setFixedHeight(1)
            self._second_line.setFrameShape(QFrame.HLine)
            self._second_line.setFrameShadow(QFrame.Sunken)
            self._second_line.setFixedHeight(
                2) if shadow else self._second_line.setFixedHeight(1)
        else:
            self._label.setVisible(False)
            self._second_line.setVisible(False)
            first_line.setFrameShape(QFrame.VLine)
            first_line.setFrameShadow(QFrame.Plain)
            self.setFixedWidth(2)
            first_line.setFixedWidth(
                2) if shadow else first_line.setFixedWidth(1)

        main_layout.setStretchFactor(first_line,
                                     self._ALIGN_MAP.get(alignment, 50))
        main_layout.setStretchFactor(self._second_line,
                                     100 - self._ALIGN_MAP.get(alignment, 50))

        self.set_text(text)

    @classmethod
    def left(cls, text=''):
        """
        Creates an horizontal splitter with text at left
        :param text:
        :return:
        """

        return cls(text, alignment=Qt.AlignLeft)

    @classmethod
    def right(cls, text=''):
        """
        Creates an horizontal splitter with text at right
        :param text:
        :return:
        """

        return cls(text, alignment=Qt.AlignRight)

    @classmethod
    def center(cls, text=''):
        """
        Creates an horizontal splitter with text at center
        :param text:
        :return:
        """

        return cls(text, alignment=Qt.AlignCenter)

    @classmethod
    def vertical(cls):
        """
        Creates a vertical splitter
        :return:
        """

        return cls(orientation=Qt.Vertical)

    def get_text(self):
        """
        Returns splitter text
        :return: str
        """

        return self._label.text()

    def set_text(self, text):
        """
        Sets splitter text
        :param str text:
        """

        self._text = text
        self._label.setText(text)
        if self._orient == Qt.Horizontal:
            self._label.setVisible(bool(text))
            self._second_line.setVisible(bool(text))
Ejemplo n.º 3
0
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_()
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
class BaseSaveWidget(base.BaseWidget, object):
    def __init__(self, item, settings, temp_path=None, parent=None):

        # self._item = None
        self._settings = settings
        self._temp_path = temp_path
        self._options_widget = None

        # super(BaseSaveWidget, self).__init__(parent=parent)
        #
        # self.setObjectName('LibrarySaveWidget')
        # self.set_item(item)

    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 setup_signals(self):
        self.save_btn.clicked.connect(self._on_save)
        self.cancel_btn.clicked.connect(self._on_cancel)

    def settings(self):
        """
        Returns settings object
        :return: JSONSettings
        """

        return self._settings

    def set_settings(self, settings):
        """
        Sets save widget settings
        :param settings: JSONSettings
        """

        self._settings = settings

    # def item(self):
    #     """
    #     Returns the library item to be created
    #     :return: LibraryItem
    #     """
    #
    #     return self._item

    def set_item(self, item):
        """
        Sets the base item to be created
        :param item: LibraryItem
        """

        self._item = item

        self._title_lbl.setText(item.MenuName)
        self._icon_lbl.setPixmap(QPixmap(item.type_icon_path()))
        schema = item.save_schema()
        if schema:
            options_widget = formwidget.FormWidget(self)
            options_widget.set_schema(schema)
            options_widget.set_validator(item.save_validator)
            self._options_frame.layout().addWidget(options_widget)
            self._options_widget = options_widget
            self.load_settings()
            options_widget.stateChanged.connect(self._on_options_changed)
            options_widget.validate()
        else:
            self._options_frame.setVisible(False)

    def library_window(self):
        """
        Returns library widget window for the item
        :return: LibraryWindow
        """

        return self._item.library_window()

    def set_library_window(self, library_window):
        """
        Sets the library widget for the item
        :param library_window: LibraryWindow
        """

        self._item.set_library_window(library_window)

    def name(self):
        """
        Returns the name of the field
        :return: str
        """

        return self._title_lbl.text().strip()

    def description(self):
        """
        Returns the string from the comment field
        :return: str
        """

        return self._comment.toPlainText().strip()

    def folder_frame(self):
        """
        Returns the frame that contains the folder edit, label and button
        :return: QFrame
        """

        return self._folder_frame

    def folder_path(self):
        """
        Returns the folder path
        :return: str
        """

        return self._folder_widget.folder_line.text()

    def set_folder_path(self, path):
        """
        Sets the destination folder path
        :param path: str
        """

        self._folder_widget.folder_line.setText(path)

    def default_values(self):
        """
        Returns all the default values for the save fields
        :return: dict
        """

        values = dict()
        for option in self.item().save_schema():
            values[option.get('name')] = option.get('default')

        return values

    def save_settings(self):
        """
        Saves the current state of the widget to disk
        """

        state = self._options_widget.options_state()
        self.settings().set(self.item().__class__.__name__,
                            {'SaveOptions': state})

    def load_settings(self):
        """
        Returns the settings object for saving the state of the widget
        """

        option_settings = self.settings().get(self.item().__class__.__name__,
                                              {})
        options = option_settings.get('SaveOptions', dict())
        values = self.default_values()
        if options:
            for option in self.item().save_schema():
                name = option.get('name')
                persistent = option.get('persistent')
                if not persistent and name in options:
                    options[name] = values[name]
            self._options_widget.set_state_from_options(options)

    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()

        item.save(path=path, objects=objects, icon_path=icon_path, **options)

        self.close()

    def _save(self):
        if not self.library_window():
            return

        try:
            path = self.folder_path()
            options = self._options_widget.values()
            name = options.get('name')
            objects = dcc.selected_nodes(full_path=True) or list()
            if not path:
                raise Exception(
                    'No folder selected. Please select a destination folder')
            if not name:
                raise Exception(
                    'No name specified. Please set a name before saving')

            if not os.path.exists(self.icon_path()):
                btn = self.show_thumbnail_capture_dialog()
                if btn == QDialogButtonBox.Cancel:
                    return

            path += '/{}'.format(name)
            icon_path = self.icon_path()

            self.save(path=path, icon_path=icon_path, objects=objects)

        except Exception as e:
            messagebox.MessageBox.critical(self.library_window(),
                                           'Error while saving', str(e))
            LOGGER.error(traceback.format_exc())
            raise

        self.library_window().stack.slide_in_index(0)

    def _on_options_changed(self):
        """
        Internal callback function that is called when an option value changes
        """

        self.save_settings()

    def _on_save(self):
        if not self.library_window():
            return

        self._save()

        self.library_window().stack.slide_in_index(0)

    def _on_cancel(self):
        self.close()
        self.library_window().stack.slide_in_index(0)