# -*- coding: utf-8 -*- import os import json import webbrowser from copy import deepcopy from functools import partial from .pyside import QtWidgets, QtCore, QtGui from maya import cmds import maya.OpenMaya as om from .appinfos import ( VERSION, RELEASE_DATE, DW_GITHUB, DW_WEBSITE, PICKER_DOCUMENTATION) from .compatibility import ensure_retro_compatibility from .document import PickerDocument from .designer.editor import PickerEditor from .dialog import ( question, get_image_path, NamespaceDialog) from .ingest import animschool from .hotkeys import get_hotkeys_config from .namespace import ( switch_namespace, selected_namespace, detect_picker_namespace, pickers_namespaces) from .optionvar import ( AUTO_FOCUS_BEHAVIOR, AUTO_SWITCH_TAB, AUTO_RESIZE_NAMESPACE_COMBO, CHECK_IMAGES_PATHS, AUTO_SET_NAMESPACE, DISABLE_IMPORT_CALLBACKS, DISPLAY_HIERARCHY_IN_PICKER, DISPLAY_QUICK_OPTIONS, INSERT_TAB_AFTER_CURRENT, LAST_OPEN_DIRECTORY, LAST_IMPORT_DIRECTORY, LAST_SAVE_DIRECTORY, NAMESPACE_TOOLBAR, USE_ICON_FOR_UNSAVED_TAB, WARN_ON_TAB_CLOSED, save_optionvar, append_recent_filename, save_opened_filenames) from .path import get_import_directory, get_open_directory, format_path from .picker import PickerStackedView, list_targets from .preference import PreferencesWindow from .qtutils import set_shortcut, icon, maya_main_window, DockableBase from .quick import QuickOptions from .references import ensure_images_path_exists from .scenedata import ( load_local_picker_data, store_local_picker_data, clean_stray_picker_holder_nodes) from .templates import PICKER, BACKGROUND ABOUT = """\ DreamWall Picker Licence MIT Version: {version} Release date: {release} Authors: Lionel Brouyère, Olivier Evers Contributor(s): Herizo Ran, fabiencollet, c-morten, kalemas (Konstantin Maslyuk), Markus Ng, Jerome Drese, Renaud Lessard Larouche Features: Animation picker widget. Quick picker creation. Advanced picker editing. Read AnimSchoolPicker files (december 2021 version and latest) Free and open source, today and forever. This tool is a fork of Hotbox Designer (Lionel Brouyère). A menus, markmenu and hotbox designer cross DCC. https://github.com/luckylyk/hotbox_designer """.format( version=".".join(str(n) for n in VERSION), release=RELEASE_DATE) WINDOW_TITLE = "DreamWall - Picker" WINDOW_CONTROL_NAME = "dwPickerWindow" CLOSE_CALLBACK_COMMAND = "import dwpicker;dwpicker._dwpicker.close_event()" CLOSE_TAB_WARNING = """\ Close the tab will remove completely the picker data from the scene. Are you sure to continue ?""" class DwPicker(DockableBase, QtWidgets.QWidget): def __init__( self, replace_namespace_function=None, list_namespaces_function=None): super(DwPicker, self).__init__(control_name=WINDOW_CONTROL_NAME) self.setWindowTitle(WINDOW_TITLE) self.shortcuts = {} self.replace_namespace_custom_function = replace_namespace_function self.list_namespaces_function = list_namespaces_function self.editable = True self.callbacks = [] self.stored_focus = None self.editors = [] self.pickers = [] self.preferences_window = PreferencesWindow( callback=self.load_ui_states, parent=maya_main_window()) self.preferences_window.need_update_callbacks.connect( self.reload_callbacks) self.preferences_window.hotkey_changed.connect(self.register_shortcuts) self.sub_panels_view = QtWidgets.QToolButton() self.sub_panels_view.setCheckable(True) self.sub_panels_view.setChecked(True) self.sub_panels_view.setIcon(icon("panels.png")) self.sub_panels_view.setFixedSize(17, 17) self.sub_panels_view.released.connect(self.update_panels_display_mode) self.sub_tabs_view = QtWidgets.QToolButton() self.sub_tabs_view.setCheckable(True) self.sub_tabs_view.setIcon(icon("tabs.png")) self.sub_tabs_view.setFixedSize(17, 17) self.sub_tabs_view.released.connect(self.update_panels_display_mode) self.panel_buttons = QtWidgets.QButtonGroup() self.panel_buttons.addButton(self.sub_panels_view, 0) self.panel_buttons.addButton(self.sub_tabs_view, 1) self.namespace_label = QtWidgets.QLabel("Namespace: ") self.namespace_combo = QtWidgets.QComboBox() self.namespace_combo.setMinimumWidth(200) method = self.change_namespace_combo self.namespace_combo.currentIndexChanged.connect(method) self.namespace_refresh = QtWidgets.QPushButton("") self.namespace_refresh.setIcon(icon("reload.png")) self.namespace_refresh.setFixedSize(17, 17) self.namespace_refresh.setIconSize(QtCore.QSize(15, 15)) self.namespace_refresh.released.connect(self.update_namespaces) self.namespace_picker = QtWidgets.QPushButton("") self.namespace_picker.setIcon(icon("picker.png")) self.namespace_picker.setFixedSize(17, 17) self.namespace_picker.setIconSize(QtCore.QSize(15, 15)) self.namespace_picker.released.connect(self.pick_namespace) self.namespace_widget = QtWidgets.QWidget() self.namespace_layout = QtWidgets.QHBoxLayout(self.namespace_widget) self.namespace_layout.setContentsMargins(10, 2, 2, 2) self.namespace_layout.setSpacing(0) self.namespace_layout.addWidget(self.namespace_label) self.namespace_layout.addSpacing(4) self.namespace_layout.addWidget(self.namespace_combo) self.namespace_layout.addSpacing(2) self.namespace_layout.addWidget(self.namespace_refresh) self.namespace_layout.addWidget(self.namespace_picker) self.namespace_layout.addStretch(1) self.namespace_layout.addWidget(self.sub_panels_view) self.namespace_layout.addWidget(self.sub_tabs_view) self.tab = QtWidgets.QTabWidget() self.tab.setTabsClosable(True) self.tab.setMovable(True) self.tab.tabBar().tabMoved.connect(self.tab_moved) self.tab.tabBar().tabBarDoubleClicked.connect(self.change_title) self.tab.currentChanged.connect(self.tab_index_changed) method = partial(self.close_tab, store=True) self.tab.tabCloseRequested.connect(method) self.quick_options = QuickOptions() self.menubar = DwPickerMenu(parent=self) self.menubar.new.triggered.connect(self.call_new) self.menubar.open.triggered.connect(self.call_open) self.menubar.save.triggered.connect(self.call_save) self.menubar.save_as.triggered.connect(self.call_save_as) self.menubar.exit.triggered.connect(self.close) self.menubar.import_.triggered.connect(self.call_import) self.menubar.undo.triggered.connect(self.call_undo) self.menubar.redo.triggered.connect(self.call_redo) self.menubar.advanced_edit.triggered.connect(self.call_edit) self.menubar.preferences.triggered.connect(self.call_preferences) self.menubar.change_title.triggered.connect(self.change_title) self.menubar.toggle_display.triggered.connect(self.toggle_display_mode) method = self.toggle_hierarchy_display self.menubar.toggle_hierarchy_display.triggered.connect(method) method = self.change_namespace_dialog self.menubar.change_namespace.triggered.connect(method) self.menubar.add_background.triggered.connect(self.add_background) self.menubar.tools.triggered.connect(self.call_tools) self.menubar.documentation.triggered.connect(self.call_documentation) self.menubar.dw.triggered.connect(self.call_dreamwall) self.menubar.about.triggered.connect(self.call_about) self.layout = QtWidgets.QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.layout.setMenuBar(self.menubar) self.layout.addWidget(self.namespace_widget) self.layout.addWidget(self.tab) self.layout.addWidget(self.quick_options) self.load_ui_states() self.register_shortcuts() def register_shortcuts(self): # Unregister all shortcuts before create new ones function_names_actions = { 'focus': (self.reset, None), 'new': (self.call_new, self.menubar.new), 'open': (self.call_open, self.menubar.open), 'save': (self.call_save, self.menubar.save), 'close': (self.close, self.menubar.exit), 'undo': (self.call_undo, self.menubar.undo), 'redo': (self.call_redo, self.menubar.redo), 'edit': (self.call_edit, self.menubar.advanced_edit), 'next_tab': (self.call_next_tab, None), 'previous_tab': (self.call_previous_tab, None), 'toggle_display': ( self.toggle_display_mode, self.menubar.toggle_display), 'display_hierarchy': ( self.toggle_hierarchy_display, self.menubar.toggle_hierarchy_display) } for function_name, sc in self.shortcuts.items(): sc.activated.disconnect(function_names_actions[function_name][0]) seq = QtGui.QKeySequence() action = function_names_actions[function_name][1] if not action: continue action.setShortcut(seq) self.shortcuts = {} shortcut_context = QtCore.Qt.WidgetWithChildrenShortcut for function_name, data in get_hotkeys_config().items(): if not data['enabled']: continue method = function_names_actions[function_name][0] ks = data['key_sequence'] if ks is None: continue sc = set_shortcut(ks, self, method, shortcut_context) self.shortcuts[function_name] = sc # HACK: Need to implement twice the shortcut to display key # sequence in the menu and keep it active when the view is docked. action = function_names_actions[function_name][1] if action is None: continue action.setShortcut(ks) action.setShortcutContext(shortcut_context) def show(self, *args, **kwargs): super(DwPicker, self).show( closeCallback=CLOSE_CALLBACK_COMMAND, *args, **kwargs) self.register_callbacks() def close_event(self): self.preferences_window.close() def list_scene_namespaces(self): if self.list_namespaces_function: ns = self.list_namespaces_function() else: ns = cmds.namespaceInfo(listOnlyNamespaces=True, recurse=True) ns = ns or [] namespaces = ns + pickers_namespaces(self.pickers) return sorted(list(set(namespaces))) def update_namespaces(self, *_): self.namespace_combo.blockSignals(True) self.namespace_combo.clear() self.namespace_combo.addItem("*Root*") namespaces = self.list_scene_namespaces() self.namespace_combo.addItems(namespaces) self.namespace_combo.blockSignals(False) # Auto update namespace combo to namespace size. if not cmds.optionVar(query=AUTO_RESIZE_NAMESPACE_COMBO): self.namespace_combo.setSizePolicy( QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum) self.namespace_combo.setMinimumWidth(200) return max_width = 0 for i in range(self.namespace_combo.count()): t = self.namespace_combo.itemText(i) width = self.namespace_combo.fontMetrics().horizontalAdvance(t) max_width = max(max_width, width) width = max_width + 20 # padding self.namespace_combo.setFixedWidth(max((200, width))) def toggle_display_mode(self): index = int(not bool(self.panel_buttons.checkedId())) self.panel_buttons.button(index).setChecked(True) self.update_panels_display_mode() self.setFocus() def toggle_hierarchy_display(self): state = not bool(cmds.optionVar(query=DISPLAY_HIERARCHY_IN_PICKER)) save_optionvar(DISPLAY_HIERARCHY_IN_PICKER, int(state)) self.update() self.setFocus() def update_panels_display_mode(self, *_): state = bool(self.panel_buttons.checkedId()) picker = self.tab.currentWidget() if picker is None: return focused_panel = picker.reset() picker.as_sub_tab = state picker.create_pickers() picker.create_panels(panel=focused_panel) QtCore.QTimer.singleShot(10, partial(picker.reset, force_all=True)) picker.update() def tab_index_changed(self, index): if not self.pickers: return picker = self.pickers[index] index = int(picker.as_sub_tab) self.panel_buttons.button(index).setChecked(True) if not picker: return namespace = detect_picker_namespace(picker.document.shapes) self.namespace_combo.blockSignals(True) if self.namespace_combo.findText(namespace) == -1 and namespace: self.namespace_combo.addItem(namespace) if namespace: self.namespace_combo.setCurrentText(namespace) else: self.namespace_combo.setCurrentIndex(0) self.namespace_combo.blockSignals(False) def tab_moved(self, newindex, oldindex): for l in (self.editors, self.pickers): l.insert(newindex, l.pop(oldindex)) self.store_local_pickers_data() def leaveEvent(self, _): mode = cmds.optionVar(query=AUTO_FOCUS_BEHAVIOR) if mode == 'off': return cmds.setFocus("MayaWindow") def enterEvent(self, _): mode = cmds.optionVar(query=AUTO_FOCUS_BEHAVIOR) if mode == 'bilateral': cmds.setFocus(self.objectName()) def dockCloseEventTriggered(self): save_opened_filenames([ p.document.filename for p in self.pickers if p.document.filename]) modified = [p.document.modified_state for p in self.pickers] if not any(modified): return super(DwPicker, self).dockCloseEventTriggered() msg = ( 'Some picker have unsaved modification. \n' 'Would you like to save them ?') result = QtWidgets.QMessageBox.question( None, 'Save ?', msg, buttons=( QtWidgets.QMessageBox.SaveAll | QtWidgets.QMessageBox.Close), defaultButton=QtWidgets.QMessageBox.SaveAll) if result == QtWidgets.QMessageBox.Close: return for i in range(self.tab.count()-1, -1, -1): self.save_tab(i) save_opened_filenames([p.document.filename for p in self.pickers]) return super(DwPicker, self).dockCloseEventTriggered() def reload_callbacks(self): self.unregister_callbacks() self.register_callbacks() def register_callbacks(self): self.unregister_callbacks() callbacks = { om.MSceneMessage.kBeforeNew: [ self.close_tabs, self.update_namespaces], om.MSceneMessage.kAfterOpen: [ self.load_saved_pickers, self.update_namespaces], om.MSceneMessage.kAfterCreateReference: [ self.load_saved_pickers, self.update_namespaces]} if not cmds.optionVar(query=DISABLE_IMPORT_CALLBACKS): callbacks[om.MSceneMessage.kAfterImport] = [ self.load_saved_pickers, self.update_namespaces] for event, methods in callbacks.items(): for method in methods: callback = om.MSceneMessage.addCallback(event, method) self.callbacks.append(callback) method = self.auto_switch_tab cb = om.MEventMessage.addEventCallback('SelectionChanged', method) self.callbacks.append(cb) method = self.auto_switch_namespace cb = om.MEventMessage.addEventCallback('SelectionChanged', method) self.callbacks.append(cb) for picker in self.pickers: picker.register_callbacks() def unregister_callbacks(self): for cb in self.callbacks: om.MMessage.removeCallback(cb) self.callbacks.remove(cb) for picker in self.pickers: picker.unregister_callbacks() def auto_switch_namespace(self, *_, **__): if not cmds.optionVar(query=AUTO_SET_NAMESPACE): return self.pick_namespace() def auto_switch_tab(self, *_, **__): if not cmds.optionVar(query=AUTO_SWITCH_TAB): return nodes = cmds.ls(selection=True) if not nodes: return picker = self.tab.currentWidget() if not picker: return targets = list_targets(picker.document.shapes) if nodes[-1] in targets: return for i, picker in enumerate(self.pickers): if nodes[-1] in list_targets(picker.document.shapes): self.tab.setCurrentIndex(i) return def load_saved_pickers(self, *_, **__): self.clear() pickers = load_local_picker_data() if cmds.optionVar(query=CHECK_IMAGES_PATHS): ensure_images_path_exists(pickers) for picker in pickers: self.add_picker(picker) clean_stray_picker_holder_nodes() def store_local_pickers_data(self): if not self.editable: return if not self.tab.count(): store_local_picker_data([]) return pickers = [self.document(i).data for i in range(self.tab.count())] store_local_picker_data(pickers) def save_tab(self, index): msg = ( 'Picker contain unsaved modification !\n' 'Woud you like to continue ?') result = QtWidgets.QMessageBox.question( None, 'Save ?', msg, buttons=( QtWidgets.QMessageBox.Save | QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel), defaultButton=QtWidgets.QMessageBox.Cancel) if result == QtWidgets.QMessageBox.Cancel: return False elif result == QtWidgets.QMessageBox.Save and not self.call_save(index): return False return True def close_tabs(self, *_): for i in range(self.tab.count()-1, -1, -1): self.close_tab(i) self.store_local_pickers_data() def clear(self): for i in range(self.tab.count()-1, -1, -1): self.close_tab(i, force=True) def close_tab(self, index, force=False, store=False): if self.document(index).modified_state and force is False: if not self.save_tab(index): return elif (cmds.optionVar(query=WARN_ON_TAB_CLOSED) and not question('Warning', CLOSE_TAB_WARNING)): return editor = self.editors.pop(index) if editor: editor.close() picker = self.pickers.pop(index) picker.unregister_callbacks() picker.close() self.tab.removeTab(index) if store: self.store_local_pickers_data() def load_ui_states(self): value = bool(cmds.optionVar(query=DISPLAY_QUICK_OPTIONS)) self.quick_options.setVisible(value) value = bool(cmds.optionVar(query=NAMESPACE_TOOLBAR)) self.namespace_widget.setVisible(value) self.update_namespaces() self.update_modified_states() def add_picker_from_file(self, filename): with open(filename, "r") as f: data = ensure_retro_compatibility(json.load(f)) ensure_images_path_exists([data]) self.add_picker(data, filename=filename) append_recent_filename(filename) def reset(self): picker = self.tab.currentWidget() if picker: picker.reset() def general_changed(self, origin, option): if option == 'name' and origin != 'main_window': self.update_names() if option == 'panels.as_sub_tab' and origin != 'main_window': index = int(self.document().data['general']['panels.as_sub_tab']) self.panel_buttons.button(index).setChecked(True) def update_names(self): for i in range(self.tab.count()): self.set_title(i, self.document(i).data['general']['name']) def create_picker(self, data): document = PickerDocument(data) document.changed.connect(self.store_local_pickers_data) document.general_option_changed.connect(self.general_changed) document.data_changed.connect(self.update_names) document.changed.connect(self.update_modified_states) picker = PickerStackedView(document, self.editable) picker.register_callbacks() return picker def add_picker(self, data, filename=None, modified_state=False): picker = self.create_picker(data) picker.document.filename = filename picker.document.modified_state = modified_state insert = cmds.optionVar(query=INSERT_TAB_AFTER_CURRENT) if not insert or self.tab.currentIndex() == self.tab.count() - 1: self.pickers.append(picker) self.editors.append(None) self.tab.addTab(picker, data['general']['name']) self.tab.setCurrentIndex(self.tab.count() - 1) else: index = self.tab.currentIndex() + 1 self.pickers.insert(index, picker) self.editors.insert(index, None) self.tab.insertTab(index, picker, data['general']['name']) self.tab.setCurrentIndex(index) picker.reset(force_all=True) def call_open(self): filenames = QtWidgets.QFileDialog.getOpenFileNames( None, "Open a picker...", get_open_directory(), filter="Dreamwall Picker (*.json)")[0] if not filenames: return save_optionvar(LAST_OPEN_DIRECTORY, os.path.dirname(filenames[0])) for filename in filenames: self.add_picker_from_file(filename) self.store_local_pickers_data() def call_preferences(self): self.preferences_window.show() def call_save(self, index=None): index = self.tab.currentIndex() if type(index) is not int else index filename = self.document(index).filename if not filename: return self.call_save_as(index=index) return self.save_picker(index, filename) def call_save_as(self, index=None): index = self.tab.currentIndex() if type(index) is not int else index filename = QtWidgets.QFileDialog.getSaveFileName( None, "Save a picker ...", cmds.optionVar(query=LAST_SAVE_DIRECTORY), filter="Dreamwall Picker (*.json)")[0] if not filename: return False if os.path.exists(filename): msg = '{} already, exists. Do you want to erase it ?' if not question('File exist', msg.format(filename)): return False self.save_picker(index, filename) def call_undo(self): index = self.tab.currentIndex() if index < 0: return self.document(index).undo() self.document(index).changed.emit() def call_redo(self): index = self.tab.currentIndex() if index < 0: return self.document(index).redo() self.document(index).changed.emit() def save_picker(self, index, filename): self.document(index).filename = filename save_optionvar(LAST_SAVE_DIRECTORY, os.path.dirname(filename)) append_recent_filename(filename) with open(filename, 'w') as f: json.dump(self.document(index).data, f, indent=2) self.set_modified_state(index, False) return True def call_import(self): sources = QtWidgets.QFileDialog.getOpenFileNames( None, "Import a picker...", get_import_directory(), filter="Anim School Picker (*.pkr)")[0] if not sources: return dst = QtWidgets.QFileDialog.getExistingDirectory( None, "Conversion destination", os.path.dirname(sources[0]), options=QtWidgets.QFileDialog.ShowDirsOnly) if not dst: return save_optionvar(LAST_IMPORT_DIRECTORY, os.path.dirname(sources[0])) for src in sources: filename = animschool.convert(src, dst) self.add_picker_from_file(filename) def call_new(self): self.add_picker({ 'general': deepcopy(PICKER), 'shapes': []}) self.store_local_pickers_data() def document(self, index=None): index = self.tab.currentIndex() if type(index) is not int else index if index < 0: return None picker = self.tab.widget(index) return picker.document def call_edit(self): index = self.tab.currentIndex() if index < 0: QtWidgets.QMessageBox.warning(self, "Warning", "No picker set") return if self.editors[index] is None: document = self.document() editor = PickerEditor( document, parent=self) self.editors[index] = editor self.editors[index].show() self.editors[index].shape_canvas.focus() def call_next_tab(self): index = self.tab.currentIndex() + 1 if index == self.tab.count(): index = 0 self.tab.setCurrentIndex(index) def call_previous_tab(self): index = self.tab.currentIndex() - 1 if index < 0: index = self.tab.count() - 1 self.tab.setCurrentIndex(index) def set_editable(self, state): self.editable = state self.menubar.set_editable(state) for picker in self.pickers: picker.editable = state def update_modified_states(self): for index, picker in enumerate(self.pickers): state = picker.document.modified_state use_icon = cmds.optionVar(query=USE_ICON_FOR_UNSAVED_TAB) icon_ = icon('save.png') if state and use_icon else QtGui.QIcon() self.tab.setTabIcon(index, icon_) title = self.document(index).data['general']['name'] title = "*" + title if state and not use_icon else title self.tab.setTabText(index, title) def set_modified_state(self, index, state): """ Update the tab icon. Add a "save" icon if tab contains unsaved modifications. """ if not self.document(index).filename: return self.document(index).modified_state = state use_icon = cmds.optionVar(query=USE_ICON_FOR_UNSAVED_TAB) icon_ = icon('save.png') if state and use_icon else QtGui.QIcon() self.tab.setTabIcon(index, icon_) title = self.document(index).data['general']['name'] title = "*" + title if state and not use_icon else title self.tab.setTabText(index, title) def call_tools(self): webbrowser.open(DW_GITHUB) def call_dreamwall(self): webbrowser.open(DW_WEBSITE) def call_documentation(self): webbrowser.open(PICKER_DOCUMENTATION) def call_about(self): QtWidgets.QMessageBox.about(self, 'About', ABOUT) def sizeHint(self): return QtCore.QSize(500, 800) def change_title(self, index=None): if not self.editable: return index = ( self.tab.currentIndex() if not isinstance(index, int) else index) if index < 0: return title, operate = QtWidgets.QInputDialog.getText( None, 'Change picker title', 'New title', text=self.document(index).data['general']['name']) if not operate: return self.set_title(index, title) index = ( self.tab.currentIndex() if not isinstance(index, int) else index) if index < 0: return document = self.document(index) document.data['general']['name'] = title document.general_option_changed.emit('main_window', 'name') self.document(index).record_undo() self.set_title(index, title) def set_title(self, index=None, title=''): use_icon = cmds.optionVar(query=USE_ICON_FOR_UNSAVED_TAB) if not use_icon and self.document(index).modified_state: title = "*" + title self.tab.setTabText(index, title) def change_namespace_dialog(self): dialog = NamespaceDialog() if not dialog.exec_(): return namespace = dialog.namespace self.change_namespace(namespace) def change_namespace_combo(self): index = self.namespace_combo.currentIndex() text = self.namespace_combo.currentText() namespace = text if index else ":" self.change_namespace(namespace) def pick_namespace(self): namespace = selected_namespace() self.namespace_combo.setCurrentText(namespace) def change_namespace(self, namespace): document = self.document() if not document: return switch_namespace_function = ( self.replace_namespace_custom_function or switch_namespace) for shape in document.shapes: if not shape.targets(): continue targets = [ switch_namespace_function(t, namespace) for t in shape.targets()] shape.options['action.targets'] = targets document.record_undo() document.shapes_changed.emit() def add_background(self): filename = get_image_path(self) if not filename: return filename = format_path(filename) shape = deepcopy(BACKGROUND) shape['image.path'] = filename image = QtGui.QImage(filename) shape['image.width'] = image.size().width() shape['image.height'] = image.size().height() shape['shape.width'] = image.size().width() shape['shape.height'] = image.size().height() shape['bgcolor.transparency'] = 255 self.document().add_shapes([shape], prepend=True) self.document().record_undo() self.document().shapes_changed.emit() class DwPickerMenu(QtWidgets.QMenuBar): def __init__(self, parent=None): super(DwPickerMenu, self).__init__(parent) self.new = QtWidgets.QAction('&New', parent) self.open = QtWidgets.QAction('&Open', parent) self.import_ = QtWidgets.QAction('&Import', parent) self.save = QtWidgets.QAction('&Save', parent) self.save_as = QtWidgets.QAction('&Save as', parent) self.exit = QtWidgets.QAction('Exit', parent) self.undo = QtWidgets.QAction('Undo', parent) self.redo = QtWidgets.QAction('Redo', parent) self.advanced_edit = QtWidgets.QAction('Advanced &editing', parent) self.preferences = QtWidgets.QAction('Preferences', parent) text = 'Toggle panel display mode' self.toggle_display = QtWidgets.QAction(text, parent) text = 'Toggle hierarchy display' self.toggle_hierarchy_display = QtWidgets.QAction(text, parent) self.change_title = QtWidgets.QAction('Change picker title', parent) self.change_namespace = QtWidgets.QAction('Change namespace', parent) self.add_background = QtWidgets.QAction('Add background item', parent) self.documentation = QtWidgets.QAction('Documentation', parent) self.tools = QtWidgets.QAction('Other DreamWall &tools', parent) self.dw = QtWidgets.QAction('&About DreamWall', parent) self.about = QtWidgets.QAction('&About DwPicker', parent) self.file = QtWidgets.QMenu('&File', parent) self.file.addAction(self.new) self.file.addAction(self.open) self.file.addAction(self.import_) self.file.addSeparator() self.file.addAction(self.save) self.file.addAction(self.save_as) self.file.addSeparator() self.file.addAction(self.exit) self.edit = QtWidgets.QMenu('&Edit', parent) self.edit.addAction(self.undo) self.edit.addAction(self.redo) self.edit.addSeparator() self.edit.addAction(self.advanced_edit) self.edit.addAction(self.preferences) self.edit.addSeparator() self.edit.addAction(self.toggle_display) self.edit.addAction(self.toggle_hierarchy_display) self.edit.addSeparator() self.edit.addAction(self.change_title) self.edit.addSeparator() self.edit.addAction(self.change_namespace) self.edit.addAction(self.add_background) self.help = QtWidgets.QMenu('&Help', parent) self.help.addAction(self.documentation) self.help.addSeparator() self.help.addAction(self.tools) self.help.addAction(self.dw) self.help.addSeparator() self.help.addAction(self.about) self.addMenu(self.file) self.addMenu(self.edit) self.addMenu(self.help) def set_editable(self, state): self.undo.setEnabled(state) self.redo.setEnabled(state) self.change_title.setEnabled(state) self.advanced_edit.setEnabled(state) self.add_background.setEnabled(state)