def reload(self): """ Reloads current menu data """ self.clear() if self._enable_select_content: select_content_action = QAction(resources.icon('cursor'), 'Select content', self) select_content_action.triggered.connect(self.item().select_content) self.addAction(select_content_action) self.addSeparator() selection_sets = self.selection_sets() if not selection_sets: action = QAction('No selection sets found!', self) action.setEnabled(False) self.addAction(action) return for selection_set in selection_sets: dirname = os.path.basename(os.path.dirname(selection_set.path())) basename = os.path.basename(selection_set.path()).replace( selection_set.EXTENSION, '') nice_name = '{}: {}'.format(dirname, basename) selection_set_action = QAction(nice_name, self) selection_set_action_callback = partial( selection_set.load, namespaces=self.namespaces()) selection_set_action.triggered.connect( selection_set_action_callback) self.addAction(selection_set_action)
class ScriptEditorView(window.BaseWindow, object): lastTabClosed = Signal() scriptSaved = Signal(str) def __init__(self, model, controller, parent=None): self._model = model or script_model.ScriptEditorModel() self._controller = controller or script_controller.ScriptEditorController( model=self._model) super(ScriptEditorView, self).__init__(parent=parent) self._controller.update_namespace( {'self_output': self._output_console}) # self._load_settings() self.refresh() # self._process_args() # ================================================================================================================= # OVERRIDES # ================================================================================================================= @property def model(self): return self._model # ================================================================================================================= # OVERRIDES # ================================================================================================================= def ui(self): super(ScriptEditorView, self).ui() self._stack = stack.SlidingOpacityStackedWidget(parent=self) main_splitter = QSplitter(Qt.Vertical, parent=self) self._output_console = console.OutputConsole(parent=self) # NOTE: Scripts Tab MUST pass ScriptEditor as parent because internally some ScriptEditor functions # NOTE: are connected to some signals. If we don't do this Maya will crash when opening new Script Editors :) self._scripts_tab = script.ScriptsTab(controller=self._controller, parent=self) main_splitter.addWidget(self._output_console) main_splitter.addWidget(self._scripts_tab) self._menu_bar = self._setup_menubar() self._tool_bar = self._setup_toolbar() self._tool_bar_divider = dividers.Divider() # Empty widget empty_widget = QWidget(self) empty_layout = layouts.HorizontalLayout(spacing=5, margins=(5, 5, 5, 5)) empty_widget.setLayout(empty_layout) main_empty_layout = layouts.VerticalLayout(spacing=5, margins=(5, 5, 5, 5)) self._empty_label = label.BaseLabel('No Scripts Opened', parent=self).h4().strong() self._empty_label.setAlignment(Qt.AlignCenter) main_empty_layout.addStretch() main_empty_layout.addWidget(self._empty_label) main_empty_layout.addStretch() empty_layout.addStretch() empty_layout.addLayout(main_empty_layout) empty_layout.addStretch() self._stack.addWidget(empty_widget) self._stack.addWidget(main_splitter) self.main_layout.addWidget(self._menu_bar) self.main_layout.addWidget(self._tool_bar) self.main_layout.addWidget(self._tool_bar_divider) self.main_layout.addWidget(self._stack) def setup_signals(self): self._scripts_tab.lastTabClosed.connect(self.lastTabClosed.emit) self._output_console.outputTextChanged.connect( self._controller.set_output_text) self._scripts_tab.currentChanged.connect(self._on_current_tab_changed) self._scripts_tab.selectionChanged.connect(self._on_selection_changed) self._scripts_tab.scriptTextChanged.connect( self._on_script_text_changed) self._model.consoleVisibleChanged.connect( self._on_console_visible_changed) self._model.toolbarVisibleChanged.connect( self._on_toolbar_visible_changed) self._model.menubarVisibleChanged.connect( self._on_menubar_visible_changed) self._model.sessionPathChanged.connect(self._on_session_changed) self._model.currentScriptChanged.connect( self._on_current_script_changed) self._model.currentTextChanged.connect(self._on_current_text_changed) self._model.outputTextChanged.connect(self._on_output_text_changed) self._model.scriptOpened.connect(self._on_script_opened) self._model.scriptSaved.connect(self._on_script_saved) self._model.scriptClosed.connect(self._on_script_closed) def eventFilter(self, obj, event): if event.type() in (QEvent.Move, QEvent.Resize): self._adjust_completers() return super(ScriptEditorView, self).eventFilter(obj, event) def closeEvent(self, event): # self.save_current_session() # self._save_settings() super(ScriptEditorView, self).closeEvent(event) # ================================================================================================================= # BASE # ================================================================================================================= def refresh(self): self._update_session() self._execute_selected_action.setEnabled( bool(self._model.selected_text)) self._output_console.setVisible(self._model.console_visible) self._tool_bar.setVisible(self._model.toolbar_visible) self._tool_bar_divider.setVisible(self._model.toolbar_visible) self._menu_bar.setVisible(self._model.menubar_visible) self._refresh_stack() # ================================================================================================================= # INTERNAL # ================================================================================================================= def _setup_menubar(self): """ Internal function that setups menu bar for the widget :return: """ menubar = self.menuBar() save_icon = resources.icon('save') load_icon = resources.icon('open_folder') play_icon = resources.icon('play') clear_icon = resources.icon('delete') resume_icon = resources.icon('resume') undo_icon = resources.icon('undo') redo_icon = resources.icon('redo') copy_icon = resources.icon('copy') cut_icon = resources.icon('cut') paste_icon = resources.icon('paste') tab_icon = resources.icon('tab') quote_icon = resources.icon('quote') rename_icon = resources.icon('rename') keyboard_icon = resources.icon('keyboard') help_icon = resources.icon('help') file_menu = QMenu('File', menubar) menubar.addMenu(file_menu) save_session_action = QAction(save_icon, 'Save Session', file_menu) load_script_action = QAction(load_icon, 'Load Script', file_menu) save_script_action = QAction(save_icon, 'Save Script', file_menu) file_menu.addAction(save_session_action) file_menu.addAction(load_script_action) file_menu.addAction(save_script_action) load_script_action.setShortcut('Ctrl+O') # load_script_action.setShortcutContext(Qt.WidgetShortcut) save_script_action.setShortcut('Ctrl+S') # save_script_action.setShortcutContext(Qt.WidgetShortcut) edit_menu = QMenu('Edit', self) menubar.addMenu(edit_menu) undo_action = QAction(undo_icon, 'Undo', edit_menu) redo_action = QAction(redo_icon, 'Redo', edit_menu) copy_action = QAction(copy_icon, 'Copy', edit_menu) cut_action = QAction(cut_icon, 'Cut', edit_menu) paste_action = QAction(paste_icon, 'Paste', edit_menu) tab_to_spaces_action = QAction(tab_icon, 'Tab to Spaces', edit_menu) comment_action = QAction(quote_icon, 'Comment', edit_menu) find_and_replace = QAction(rename_icon, 'Find and Replace', edit_menu) edit_menu.addAction(undo_action) edit_menu.addAction(redo_action) edit_menu.addSeparator() edit_menu.addAction(copy_action) edit_menu.addAction(cut_action) edit_menu.addAction(paste_action) edit_menu.addSeparator() edit_menu.addAction(tab_to_spaces_action) edit_menu.addAction(comment_action) edit_menu.addAction(find_and_replace) run_menu = QMenu('Run', self) menubar.addMenu(run_menu) self._execute_all_action = QAction(play_icon, 'Execute All', run_menu) self._execute_all_action.setShortcut('Ctrl+Shift+Return') self._execute_all_action.setShortcutContext(Qt.ApplicationShortcut) self._execute_selected_action = QAction(resume_icon, 'Execute Selected', run_menu) self._execute_selected_action.setShortcut('Ctrl+Return') self._execute_selected_action.setShortcutContext( Qt.WidgetWithChildrenShortcut) self._clear_output_action = QAction(clear_icon, 'Clear Output', run_menu) run_menu.addAction(self._execute_all_action) run_menu.addAction(self._execute_selected_action) run_menu.addAction(self._clear_output_action) help_menu = QMenu('Help', self) menubar.addMenu(help_menu) show_shortcuts_action = QAction(keyboard_icon, 'Show Shortcuts', help_menu) print_help_action = QAction(help_icon, 'Print Help', help_menu) help_menu.addAction(show_shortcuts_action) help_menu.addSeparator() help_menu.addAction(print_help_action) undo_action.setShortcut('Ctrl+Z') undo_action.setShortcutContext(Qt.WidgetShortcut) redo_action.setShortcut('Ctrl+Y') redo_action.setShortcutContext(Qt.WidgetShortcut) copy_action.setShortcut('Ctrl+C') copy_action.setShortcutContext(Qt.WidgetShortcut) cut_action.setShortcut('Ctrl+X') cut_action.setShortcutContext(Qt.WidgetShortcut) paste_action.setShortcut('Ctrl+V') paste_action.setShortcutContext(Qt.WidgetShortcut) comment_action.setShortcut(QKeySequence(Qt.ALT + Qt.Key_Q)) comment_action.setShortcutContext(Qt.WidgetShortcut) self._execute_all_action.triggered.connect( self._controller.execute_script) self._execute_selected_action.triggered.connect( partial(self._controller.execute_script, True)) self._clear_output_action.triggered.connect( partial(self._controller.set_output_text, '')) # open_settings_folder_action.triggered.connect(self._open_settings) # show_shortcuts_action.triggered.connect(self._open_shortcuts) # print_help_action.triggered.connect(self.editor_help) save_session_action.triggered.connect( self._controller.save_current_session) save_script_action.triggered.connect(self._controller.save_script) load_script_action.triggered.connect(self._controller.load_script) undo_action.triggered.connect(self._scripts_tab.undo) redo_action.triggered.connect(self._scripts_tab.redo) copy_action.triggered.connect(self._scripts_tab.copy) cut_action.triggered.connect(self._scripts_tab.cut) paste_action.triggered.connect(self._scripts_tab.paste) tab_to_spaces_action.triggered.connect( self._controller.convert_tab_to_spaces) # find_and_replace.triggered.connect(self._open_find_replace) comment_action.triggered.connect(self._scripts_tab.comment) return menubar def _setup_toolbar(self): """ Internal function that setups script editor toolbar """ toolbar = QToolBar('Script Editor ToolBar', parent=self) toolbar.setIconSize(QSize(16, 16)) execute_btn = buttons.BaseToolButton(parent=self) execute_btn.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) execute_selected_btn = buttons.BaseToolButton(parent=self) execute_selected_btn.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) clear_output_btn = buttons.BaseToolButton(parent=self) clear_output_btn.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) toolbar.addWidget(execute_btn) toolbar.addWidget(execute_selected_btn) toolbar.addWidget(clear_output_btn) execute_btn.setDefaultAction(self._execute_all_action) execute_selected_btn.setDefaultAction(self._execute_selected_action) clear_output_btn.setDefaultAction(self._clear_output_action) return toolbar def _update_session(self): """ Internal function that updates ui taking into account the current active session """ current_session = self._controller.get_session() if not current_session: self._scripts_tab.add_new_tab() else: sessions = current_session.read() self._scripts_tab.clear() active = 0 if sessions: for i, s in enumerate(sessions): script_file = s.get('file', None) script_name = s.get('name', '') script_text = s.get('text', '') if script_file and os.path.isfile(script_file): script_editor = self._scripts_tab.add_new_tab( script_name, script_file) else: script_editor = self._scripts_tab.add_new_tab( script_name, script_text) if s.get('active', False): active = i script_editor.set_font_size(s.get('size', None)) else: self._scripts_tab.add_new_tab() self._scripts_tab.setCurrentIndex(active) def _adjust_completers(self): """ Internal callback function that updates the size of the completers depending on its visiblity """ for i in range(self._scripts_tab.count()): script_widget = self._scripts_tab.widget(i) if not script_widget: continue if script_widget.editor.completer.isVisible(): script_widget.editor.move_completer() def _refresh_stack(self): """ Internal function that updates stack status """ total_scripts = len(list(self._model.scripts.items())) if total_scripts <= 0: self._stack.setCurrentIndex(0) else: self._stack.setCurrentIndex(1) # self._scripts_tab.widget(0).editor.setFocus() def _process_args(self): """ Internal function that adds processes given args If file path is given in sys.argv, we tyr to open it ... :return: """ if sys.argv: f = sys.argv[-1] if os.path.exists(f): if not os.path.basename(f) == os.path.basename(__file__): if os.path.splitext(f)[-1] in ['.txt', '.py']: self._output_console.show_message( os.path.splitext(f)[-1]) self._output_console.show_message('Open File: ' + f) self._scripts_tab.add_new_tab(os.path.basename(f), f) if self._scripts_tab.count() == 2: self._scripts_tab.removeTab(0) # ================================================================================================================= # CALLBACKS # ================================================================================================================= def _on_console_visible_changed(self, flag): """ Internal callback function that is called when console visibility is changed in the model :param flag: bool """ self._output_console.setVisible(flag) def _on_toolbar_visible_changed(self, flag): """ Internal callback function that is called when toolbar visibility is changed in the model :param flag: bool """ self._tool_bar.setVisible(flag) self._tool_bar_divider.setVisible(flag) def _on_menubar_visible_changed(self, flag): """ Internal callback function that is called when menubar visibility is changed in the model :param flag: bool """ self._menu_bar.setVisible(flag) def _on_current_tab_changed(self, index): """ Internal callback function that is called when current user selects a new script tab :param index: int """ script_widget = self._scripts_tab.widget(index) if not script_widget: return script_path = script_widget.file_path all_text = self._scripts_tab.get_current_text() with qt_contexts.block_signals(self._model): self._controller.set_current_script(script_path) self._controller.set_current_text( all_text.strip() if all_text else '') def _on_selection_changed(self, text): """ Internal callback function that is called when user selects new script text :param text: str """ with qt_contexts.block_signals(self._model): self._controller.set_selected_text(text) self._execute_selected_action.setEnabled( bool(self._model.selected_text)) def _on_script_text_changed(self, text): """ Internal callback function that is called when script text changes :param text: str """ with qt_contexts.block_signals(self._model): self._controller.set_current_text(text) def _on_session_changed(self): """ Internal callback function that is called when current session is changed in the model """ self._update_session() def _on_current_script_changed(self, script_path): """ Internal callback function that is called each time current selected script is updated in the model :param script_path: str :return: """ script_path = path_utils.clean_path(script_path) for i in range(self._scripts_tab.count()): script_widget = self._scripts_tab.widget(i) if not script_widget: continue if path_utils.clean_path(script_widget.file_path) == script_path: self._scripts_tab.setCurrentIndex(i) return def _on_current_text_changed(self, script_text): """ Internal callback function that is called when current script text is updated by the model :param script_text: str """ script_widget = self._scripts_tab.currentWidget() if not script_widget: return with qt_contexts.block_signals(self._model): script_widget.editor.setPlainText(script_text) def _on_output_text_changed(self, output_text): """ Internal callback function that is called when output text is updated in the model :param output_text: str """ with qt_contexts.block_signals(self._model): self._output_console.setText(output_text) self._output_console.move_cursor_to_line_end() def _on_script_opened(self, script_path): """ Internal callback function that is called when a new script is opened by the model :param script_path: str """ if not script_path or not os.path.isfile(script_path): return self._scripts_tab.add_new_tab(os.path.basename(script_path), script_path, skip_if_exists=True) self._refresh_stack() def _on_script_saved(self, script_path): """ Internal callback function that is called when a script is saved :param script_path: str """ script_widget = self._scripts_tab.currentWidget() if not script_widget: return with qt_contexts.block_signals(self._model): script_path = path_utils.clean_path(script_path) current_file_path = path_utils.clean_path(script_widget.file_path) if script_path != current_file_path: script_widget.file_path = script_path self._scripts_tab.set_current_tab_name( os.path.basename(script_widget.file_path)) self._controller.set_current_script(script_widget.file_path) self._controller.set_current_text( self._scripts_tab.get_current_text()) self._controller.set_selected_text( self._scripts_tab.get_current_selected_text()) def _on_script_closed(self, script_path): """ Internal callback function that is called when a script is closed by model :param script_path: str """ if not script_path: return script_path = path_utils.clean_path(script_path) for i in range(self._scripts_tab.count()): script_widget = self._scripts_tab.widget(i) if not script_widget: continue if path_utils.clean_path(script_widget.file_path) == script_path: self._scripts_tab.removeTab(i, force=True) break self._refresh_stack()