class PenSetWidget(QWidget): penSizeTrigger = Signal(int) penColorTrigger = Signal(str) fontChangeTrigger = Signal(QFont) def __init__(self, parent=None): super(PenSetWidget, self).__init__(parent) self.paddingX = 5 self.paddingY = 2 self.iconWidth = self.iconHeight = 24 self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) self.setWindowFlags(Qt.ToolTip) self.initWindows() self.prevSizeButton = self.penSize1 self.penSize1.setChecked(True) self.presentColor.setStyleSheet( 'QPushButton { background-color: %s; }' % PENCOLOR) def generateButtons(self, parent=None): """ Generate buttons due to colorDic """ self.colorButtons = [] for color in self.colorList: button = QPushButton(parent) button.setObjectName(color[0]) button.setStyleSheet('QPushButton { background-color: %s; }' % color[1]) button.setFixedSize(self.iconWidth / 2, self.iconHeight / 2) button.setCheckable(True) self.colorButtons.append(button) def initWindows(self): self.mainLayout = QHBoxLayout() self.setLayout(self.mainLayout) self.mainLayout.setSpacing(0) self.mainLayout.setContentsMargins(5, 2, 5, 2) self.initPenSizeButtons() self.initFontWidget() self.initPenColorButtons() self.separator = QFrame(self) self.separator.setFrameShape(QFrame.VLine) self.separator.setFrameShadow(QFrame.Sunken) self.mainLayout.addWidget(self.penSize) self.mainLayout.addWidget(self.changeFontButton) self.mainLayout.addWidget(self.separator) self.mainLayout.addWidget(self.colorSet) def initPenSizeButtons(self): self.penSize = QWidget(self) self.penSizeLayout = QHBoxLayout() self.penSize.setLayout(self.penSizeLayout) # adjust pen size self.penSize1 = QPushButton(self.penSize) self.penSize1.setIcon(QIcon(":/resource/icon/pensize1.png")) self.penSize1.setObjectName('1') self.penSize1.setFixedSize(self.iconWidth, self.iconHeight) self.penSize1.setCheckable(True) self.penSize2 = QPushButton(self.penSize) self.penSize2.setIcon(QIcon(":/resource/icon/pensize2.png")) self.penSize2.setObjectName('2') self.penSize2.setFixedSize(self.iconWidth, self.iconHeight) self.penSize2.setCheckable(True) self.penSize3 = QPushButton(self.penSize) self.penSize3.setIcon(QIcon(":/resource/icon/pensize3.png")) self.penSize3.setObjectName('3') self.penSize3.setFixedSize(self.iconWidth, self.iconHeight) self.penSize3.setCheckable(True) self.sizeButtonGroup = QButtonGroup(self.penSize) self.sizeButtonGroup.addButton(self.penSize1) self.sizeButtonGroup.addButton(self.penSize2) self.sizeButtonGroup.addButton(self.penSize3) self.sizeButtonGroup.buttonClicked.connect(self.sizeButtonToggled) self.penSizeLayout.addWidget(self.penSize1) self.penSizeLayout.addWidget(self.penSize2) self.penSizeLayout.addWidget(self.penSize3) self.penSizeLayout.setSpacing(5) self.penSizeLayout.setContentsMargins(0, 0, 0, 0) def initPenColorButtons(self): self.colorSet = QWidget(self) self.colorLayout = QHBoxLayout() self.colorLayout.setSpacing(5) self.colorLayout.setContentsMargins(5, 0, 5, 0) self.colorSet.setLayout(self.colorLayout) self.presentColor = QPushButton(self.colorSet) self.presentColor.setFixedSize(self.iconWidth, self.iconHeight) self.presentColor.setEnabled(False) # adjust pen color self.colorPick = QWidget(self.colorSet) self.colorGrid = QGridLayout() self.colorGrid.setSpacing(0) self.colorGrid.setContentsMargins(5, 0, 5, 0) self.colorPick.setLayout(self.colorGrid) self.colorList = [('white', '#ffffff'), ('red', '#ff0000'), ('green', '#00ff00'), ('blue', '#0000ff'), ('cyan', '#00ffff'), ('magenta', '#ff00ff'), ('yellow', '#ffff00'), ('gray', '#a0a0a4'), ('black', '#000000'), ('darkRed', '#800000'), ('darkGreen', '#008000'), ('darkBlue', '#000080'), ('darkCyan', '#008080'), ('darkMagenta', '#800080'), ('darkYellow', '#808000'), ('darkGray', '#808080')] self.generateButtons() self.colorButtonGroup = QButtonGroup(self) for button in self.colorButtons: self.colorButtonGroup.addButton(button) self.colorButtonGroup.buttonClicked.connect(self.colorButtonToggled) # set the layout tmp = 0 for x in range(0, 2): for y in range(0, int(len(self.colorList) / 2)): self.colorGrid.addWidget(self.colorButtons[tmp], x, y) tmp += 1 self.colorGrid.setSpacing(0) self.colorGrid.setContentsMargins(0, 0, 0, 0) self.colorLayout.addWidget(self.presentColor) self.colorLayout.addWidget(self.colorPick) def initFontWidget(self): self.fontDialog = QFontDialog() self.changeFontButton = QPushButton(self) self.fontDialog.setCurrentFont(QFont('Sans serif')) self.changeFontButton.setText('{0} {1}'.format( self.fontDialog.currentFont().family(), self.fontDialog.currentFont().pointSize())) self.changeFontButton.clicked.connect(self.fontButtonClicked) def showFontWidget(self): self.changeFontButton.show() self.penSize1.hide() self.penSize2.hide() self.penSize3.hide() def showPenWidget(self): self.changeFontButton.hide() self.penSize1.show() self.penSize2.show() self.penSize3.show() # slots def colorButtonToggled(self, button): self.presentColor.setStyleSheet( 'QPushButton { background-color: %s; }' % button.objectName()) self.penColorTrigger.emit(button.objectName()) def sizeButtonToggled(self, button): self.penSizeTrigger.emit(int(button.objectName()) * 2) def fontButtonClicked(self): ok = True font = QFontDialog.getFont(self) if font[1]: self.changeFontButton.setText('{0} {1}'.format( font[0].family(), font[0].pointSize())) self.fontChangeTrigger.emit(font[0])
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_()