def register_shortcut(self, action, shortcut, context=Qt.ApplicationShortcut): """Register an action for a shortcut""" shortcuts = psy.safe_list(shortcut) for j, shortcut in enumerate(shortcuts): found = False for i, (s, a) in enumerate(self.current_shortcuts): if s == shortcut: new_shortcuts = [ sc for sc in self.current_shortcuts[i][1].shortcuts() if sc != s ] a.setShortcut(QKeySequence()) if new_shortcuts: a.setShortcuts(new_shortcuts) self.current_shortcuts[i][1] = action found = True break if not found: self.default_shortcuts.append([shortcut, action]) self.current_shortcuts.append([shortcut, action]) action.setShortcuts(shortcuts) action.setShortcutContext(context)
def setup_shortcuts(self, main): """Setup the shortcuts when switched to the straditizer layout Parameters ---------- main: psyplot_gui.main.MainWindow The psyplot mainwindow""" main.register_shortcut(self.save_straditizer_action, QKeySequence.Save) main.register_shortcut(self.save_straditizer_as_action, QKeySequence.SaveAs) main.register_shortcut(self.close_straditizer_action, QKeySequence.Close) main.register_shortcut( self.close_all_straditizer_action, QKeySequence('Ctrl+Shift+W', QKeySequence.NativeText)) main.register_shortcut(self.export_final_action, QKeySequence('Ctrl+E', QKeySequence.NativeText)) main.register_shortcut( self.export_full_action, QKeySequence('Ctrl+Shift+E', QKeySequence.NativeText)) main.register_shortcut(self.load_stradi_action, [QKeySequence.Open, QKeySequence.New])
def __init__(self, show=True): """ Parameters ---------- show: bool If True, the created mainwindow is show """ if sys.stdout is None: sys.stdout = StreamToLogger(self.logger) if sys.stderr is None: sys.stderr = StreamToLogger(self.logger) super(MainWindow, self).__init__() self.setWindowIcon(QIcon(get_icon('logo.png'))) #: list of figures from the psyplot backend self.figures = [] self.error_msg = PyErrorMessage(self) self.setDockOptions(QMainWindow.AnimatedDocks | QMainWindow.AllowNestedDocks | QMainWindow.AllowTabbedDocks) #: Inprocess console self.console = ConsoleWidget(self) self.project_actions = {} self.config_pages = [] self.open_file_options = OrderedDict([ ('new psyplot plot from dataset', self.open_external_files), ('new psyplot project', partial(self.open_external_files, [])), ]) # --------------------------------------------------------------------- # ----------------------------- Menus --------------------------------- # --------------------------------------------------------------------- # ######################## File menu ################################## # --------------------------- New plot -------------------------------- self.file_menu = QMenu('File', parent=self) self.new_plot_action = QAction('New plot', self) self.new_plot_action.setStatusTip( 'Use an existing dataset (or open a new one) to create one or ' 'more plots') self.register_shortcut(self.new_plot_action, QKeySequence.New) self.new_plot_action.triggered.connect(lambda: self.new_plots(True)) self.file_menu.addAction(self.new_plot_action) # --------------------------- Open project ---------------------------- self.open_project_menu = QMenu('Open project', self) self.file_menu.addMenu(self.open_project_menu) self.open_mp_action = QAction('New main project', self) self.register_shortcut(self.open_mp_action, QKeySequence.Open) self.open_mp_action.setStatusTip('Open a new main project') self.open_mp_action.triggered.connect(self.open_mp) self.open_project_menu.addAction(self.open_mp_action) self.open_sp_action = QAction('Add to current', self) self.register_shortcut( self.open_sp_action, QKeySequence('Ctrl+Shift+O', QKeySequence.NativeText)) self.open_sp_action.setStatusTip( 'Load a project as a sub project and add it to the current main ' 'project') self.open_sp_action.triggered.connect(self.open_sp) self.open_project_menu.addAction(self.open_sp_action) # ---------------------- load preset menu ----------------------------- self.load_preset_menu = QMenu('Load preset', parent=self) self.file_menu.addMenu(self.load_preset_menu) self.load_sp_preset_action = self.load_preset_menu.addAction( "For selection", self.load_sp_preset) self.load_sp_preset_action.setStatusTip( "Load a preset for the selected project") self.load_mp_preset_action = self.load_preset_menu.addAction( "For full project", self.load_mp_preset) self.load_sp_preset_action.setStatusTip( "Load a preset for the full project") # ----------------------- Save project -------------------------------- self.save_project_menu = QMenu('Save', parent=self) self.file_menu.addMenu(self.save_project_menu) self.save_mp_action = QAction('Full psyplot project', self) self.save_mp_action.setStatusTip( 'Save the entire project into a pickle file') self.register_shortcut(self.save_mp_action, QKeySequence.Save) self.save_mp_action.triggered.connect(self.save_mp) self.save_project_menu.addAction(self.save_mp_action) self.save_sp_action = QAction('Selected psyplot project', self) self.save_sp_action.setStatusTip( 'Save the selected sub project into a pickle file') self.save_sp_action.triggered.connect(self.save_sp) self.save_project_menu.addAction(self.save_sp_action) # ------------------------ Save project as ---------------------------- self.save_project_as_menu = QMenu('Save as', parent=self) self.file_menu.addMenu(self.save_project_as_menu) self.save_mp_as_action = QAction('Full psyplot project', self) self.save_mp_as_action.setStatusTip( 'Save the entire project into a pickle file') self.register_shortcut(self.save_mp_as_action, QKeySequence.SaveAs) self.save_mp_as_action.triggered.connect( partial(self.save_mp, new_fname=True)) self.save_project_as_menu.addAction(self.save_mp_as_action) self.save_sp_as_action = QAction('Selected psyplot project', self) self.save_sp_as_action.setStatusTip( 'Save the selected sub project into a pickle file') self.save_sp_as_action.triggered.connect( partial(self.save_sp, new_fname=True)) self.save_project_as_menu.addAction(self.save_sp_as_action) # ------------------------ Save preset -------------------------------- self.save_preset_menu = QMenu('Save preset', parent=self) self.file_menu.addMenu(self.save_preset_menu) self.save_sp_preset_action = self.save_preset_menu.addAction( "Selection", self.save_sp_preset) self.save_sp_preset_action.setStatusTip( "Save the formatoptions of the selected project as a preset") self.save_mp_preset_action = self.save_preset_menu.addAction( "Full project", self.save_mp_preset) self.save_sp_preset_action.setStatusTip( "Save the formatoptions of the full project as a preset") # -------------------------- Pack project ----------------------------- self.pack_project_menu = QMenu('Zip project files', parent=self) self.file_menu.addMenu(self.pack_project_menu) self.pack_mp_action = QAction('Full psyplot project', self) self.pack_mp_action.setStatusTip( 'Pack all the data of the main project into one folder') self.pack_mp_action.triggered.connect(partial(self.save_mp, pack=True)) self.pack_project_menu.addAction(self.pack_mp_action) self.pack_sp_action = QAction('Selected psyplot project', self) self.pack_sp_action.setStatusTip( 'Pack all the data of the current sub project into one folder') self.pack_sp_action.triggered.connect(partial(self.save_sp, pack=True)) self.pack_project_menu.addAction(self.pack_sp_action) # ------------------------ Export figures ----------------------------- self.export_project_menu = QMenu('Export figures', parent=self) self.file_menu.addMenu(self.export_project_menu) self.export_mp_action = QAction('Full psyplot project', self) self.export_mp_action.setStatusTip( 'Pack all the data of the main project into one folder') self.export_mp_action.triggered.connect(self.export_mp) self.register_shortcut(self.export_mp_action, QKeySequence('Ctrl+E', QKeySequence.NativeText)) self.export_project_menu.addAction(self.export_mp_action) self.export_sp_action = QAction('Selected psyplot project', self) self.export_sp_action.setStatusTip( 'Pack all the data of the current sub project into one folder') self.register_shortcut( self.export_sp_action, QKeySequence('Ctrl+Shift+E', QKeySequence.NativeText)) self.export_sp_action.triggered.connect(self.export_sp) self.export_project_menu.addAction(self.export_sp_action) # ------------------------ Close project ------------------------------ self.file_menu.addSeparator() self.close_project_menu = QMenu('Close project', parent=self) self.file_menu.addMenu(self.close_project_menu) self.close_mp_action = QAction('Full psyplot project', self) self.register_shortcut( self.close_mp_action, QKeySequence('Ctrl+Shift+W', QKeySequence.NativeText)) self.close_mp_action.setStatusTip( 'Close the main project and delete all data and plots out of ' 'memory') self.close_mp_action.triggered.connect( lambda: psy.close(psy.gcp(True).num)) self.close_project_menu.addAction(self.close_mp_action) self.close_sp_action = QAction('Selected psyplot project', self) self.close_sp_action.setStatusTip( 'Close the selected arrays project and delete all data and plots ' 'out of memory') self.register_shortcut(self.close_sp_action, QKeySequence.Close) self.close_sp_action.triggered.connect( lambda: psy.gcp().close(True, True)) self.close_project_menu.addAction(self.close_sp_action) # ----------------------------- Quit ---------------------------------- if sys.platform != 'darwin': # mac os makes this anyway self.quit_action = QAction('Quit', self) self.quit_action.triggered.connect(self.close) self.quit_action.triggered.connect( QtCore.QCoreApplication.instance().quit) self.register_shortcut(self.quit_action, QKeySequence.Quit) self.file_menu.addAction(self.quit_action) self.menuBar().addMenu(self.file_menu) # ######################## Console menu ############################### self.console_menu = QMenu('Console', self) self.console_menu.addActions(self.console.actions()) self.menuBar().addMenu(self.console_menu) # ######################## Windows menu ############################### self.windows_menu = QMenu('Windows', self) self.menuBar().addMenu(self.windows_menu) # ############################ Help menu ############################## self.help_menu = QMenu('Help', parent=self) self.menuBar().addMenu(self.help_menu) # -------------------------- Preferences ------------------------------ self.help_action = QAction('Preferences', self) self.help_action.triggered.connect(lambda: self.edit_preferences(True)) self.register_shortcut(self.help_action, QKeySequence.Preferences) self.help_menu.addAction(self.help_action) # ---------------------------- About ---------------------------------- self.about_action = QAction('About', self) self.about_action.triggered.connect(self.about) self.help_menu.addAction(self.about_action) # ---------------------------- Dependencies --------------------------- self.dependencies_action = QAction('Dependencies', self) self.dependencies_action.triggered.connect( lambda: self.show_dependencies(True)) self.help_menu.addAction(self.dependencies_action) self.dockwidgets = [] # --------------------------------------------------------------------- # -------------------------- Dock windows ----------------------------- # --------------------------------------------------------------------- #: tab widget displaying the arrays in current main and sub project #: tree widget displaying the open datasets self.project_content = ProjectContentWidget(parent=self) self.ds_tree = DatasetTree(parent=self) #: tree widget displaying the open figures self.figures_tree = FiguresTree(parent=self) #: help explorer self.help_explorer = help_explorer = HelpExplorer(parent=self) if 'HTML help' in help_explorer.viewers and help_explorer.viewers[ 'HTML help'].sphinx_thread is not None: help_explorer.viewers[ 'HTML help'].sphinx_thread.html_ready.connect( self.focus_on_console) #: the DataFrameEditor widgets self.dataframeeditors = [] #: general formatoptions widget self.fmt_widget = FormatoptionWidget(parent=self, help_explorer=help_explorer, console=self.console) # load plugin widgets self.plugins = plugins = OrderedDict([ ('console', self.console), ('project_content', self.project_content), ('ds_tree', self.ds_tree), ('figures_tree', self.figures_tree), ('help_explorer', self.help_explorer), ('fmt_widget', self.fmt_widget), ]) self.default_plugins = list(plugins) for plugin_name, w_class in six.iteritems(rcParams.load_plugins()): plugins[plugin_name] = w_class(parent=self) self.add_mp_to_menu() psy.Project.oncpchange.connect(self.eventually_add_mp_to_menu) self.windows_menu.addSeparator() self.window_layouts_menu = QMenu('Window layouts', self) self.restore_layout_action = QAction('Restore default layout', self) self.restore_layout_action.triggered.connect(self.setup_default_layout) self.window_layouts_menu.addAction(self.restore_layout_action) self.windows_menu.addMenu(self.window_layouts_menu) self.panes_menu = QMenu('Panes', self) self.windows_menu.addMenu(self.panes_menu) self.dataframe_menu = QMenu('DataFrame editors', self) self.dataframe_menu.addAction( 'New Editor', partial(self.new_data_frame_editor, None, 'DataFrame Editor')) self.dataframe_menu.addSeparator() self.windows_menu.addMenu(self.dataframe_menu) self.central_widgets_menu = menu = QMenu('Central widget', self) self.windows_menu.addMenu(menu) self.central_widgets_actions = group = QActionGroup(self) group.setExclusive(True) # --------------------------------------------------------------------- # -------------------------- connections ------------------------------ # --------------------------------------------------------------------- self.console.help_explorer = help_explorer psyp.default_print_func = partial(help_explorer.show_rst, oname='formatoption_docs') psy.PlotterInterface._print_func = psyp.default_print_func self.setCentralWidget(self.console) # make sure that the plots are shown between the project content and # the help explorer widget self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea) # make sure that the formatoption widgets are shown between the # project content and the help explorer widget self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea) # --------------------------------------------------------------------- # ------------------------------ closure ------------------------------ # --------------------------------------------------------------------- if show: self.help_explorer.show_intro(self.console.intro_msg) # --------------------------------------------------------------------- # ------------------------- open_files_server ------------------------- # --------------------------------------------------------------------- self.callbacks = { 'new_plot': self.open_external.emit, 'change_cwd': self._change_cwd, 'run_script': self.console.run_script.emit, 'command': self.console.run_command.emit, } # Server to open external files on a single instance self.open_files_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) if rcParams['main.listen_to_port']: self._file_thread = Thread(target=self.start_open_files_server) self._file_thread.setDaemon(True) self._file_thread.start() self.open_external.connect(self._open_external_files) self.config_pages.extend([GuiRcParamsWidget, PsyRcParamsWidget]) # display the statusBar statusbar = self.statusBar() self.figures_label = QLabel() statusbar.addWidget(self.figures_label) self.plugin_label = QLabel() statusbar.addWidget(self.plugin_label) self.default_widths = {} self.setup_default_layout() if show: self.showMaximized() # save the default widths after they have been shown for w in self.plugins.values(): if w.dock is not None: self.default_widths[w] = w.dock.size().width() # hide plugin widgets that should be hidden at startup. Although this # has been executed by :meth:`setup_default_layout`, we have to execute # it again after the call of showMaximized for name, w in self.plugins.items(): if name != self.central_widget_key: w.to_dock(self) if w.hidden: w.hide_plugin() else: w.create_central_widget_action(self).setChecked(True) self._is_open = True
def __init__(self, main, *args, **kwargs): """ Parameters ---------- help_explorer: psyplot_gui.help_explorer.HelpExplorer or None A widget that can be used to show the documentation of an object ``*args,**kwargs`` Any other keyword argument for the :class:`qtconsole.rich_jupyter_widget.RichJupyterWidget` """ self._closed = False kernel_manager = QtInProcessKernelManager() # on windows, sys.stdout may be None when using pythonw.exe. Therefore # we just us a StringIO for security orig_stdout = sys.stdout if sys.stdout is None: sys.stdout = StreamToLogger(logger) orig_stderr = sys.stderr if sys.stderr is None: sys.stderr = StreamToLogger(logger) kernel_manager.start_kernel(show_banner=False) if ipykernel.__version__ < '5.1.1': # monkey patch to fix # https://github.com/ipython/ipykernel/issues/370 def _abort_queues(kernel): pass kernel_manager.kernel._abort_queues = _abort_queues sys.stdout = orig_stdout sys.stderr = orig_stderr kernel = kernel_manager.kernel kernel.gui = 'qt4' if not with_qt5 else 'qt' kernel_client = kernel_manager.client() if rcParams['console.start_channels']: kernel_client.start_channels() self.help_explorer = kwargs.pop('help_explorer', None) super(ConsoleWidget, self).__init__(*args, parent=main, **kwargs) self.intro_msg = dedents(""" psyplot version: %s gui version: %s The console provides you the full access to the current project and plots. To make your life easier, the following modules have been imported - %s Furthermore, each time you change the selection or the content in the plot objects viewer, the `sp` (the selection) and `mp` (all arrays) variables in the console are adjusted. To disable this behaviour, set:: >>> import psyplot_gui >>> psyplot_gui.rcParams['console.auto_set_mp'] = False >>> psyplot_gui.rcParams['console.auto_set_sp'] = False To inspect and object in the console and display it's documentation in the help explorer, type 'Ctrl + I' or a '?' after the object""") % ( psyplot.__version__, psyplot_gui.__version__, '\n - '.join('%s as %s' % t for t in modules2import)) self.kernel_manager = kernel_manager self.kernel_client = kernel_client self.run_command_in_shell( '\n'.join('import %s as %s' % t for t in modules2import)) self.exit_requested.connect(self._close_mainwindow) self.exit_requested.connect(QtCore.QCoreApplication.instance().quit) # we overwrite the short cut here because the 'Ctrl+S' shortcut is # reserved for mainwindows save action try: main.register_shortcut( self.export_action, QKeySequence( 'Ctrl+Alt+S', QKeySequence.NativeText)) except AttributeError: pass psy.Project.oncpchange.connect(self.update_mp) psy.Project.oncpchange.connect(self.update_sp) self.run_script.connect(self._run_script_in_shell) self.run_command.connect(self._run_command_in_shell) # HACK: we set the IOloop for the InProcessKernel here manually without # starting it (not necessary because QApplication has a blocking # IOLoop). However, we need this because the ZMQInteractiveShell wants # to call # loop = self.kernel.io_loop # loop.call_later(0.1, loop.stop)`` zmq_ioloop.install() self.kernel_manager.kernel.io_loop = ioloop.IOLoop.current()