コード例 #1
0
 def create_context_menu_actions(self):
     """Create context menu actions"""
     actions = []
     fnames = self.get_selected_filenames()
     new_actions = self.create_file_new_actions(fnames)
     if len(new_actions) > 1:
         # Creating a submenu only if there is more than one entry
         new_act_menu = QMenu(_('New'), self)
         add_actions(new_act_menu, new_actions)
         actions.append(new_act_menu)
     else:
         actions += new_actions
     import_actions = self.create_file_import_actions(fnames)
     if len(import_actions) > 1:
         # Creating a submenu only if there is more than one entry
         import_act_menu = QMenu(_('Import'), self)
         add_actions(import_act_menu, import_actions)
         actions.append(import_act_menu)
     else:
         actions += import_actions
     if actions:
         actions.append(None)
     if fnames:
         actions += self.create_file_manage_actions(fnames)
     if actions:
         actions.append(None)
     if fnames and all([osp.isdir(_fn) for _fn in fnames]):
         actions += self.create_folder_manage_actions(fnames)
     if actions:
         actions.append(None)
     actions += self.common_actions
     return actions
コード例 #2
0
    def contextMenuEvent(self, event):
        index_clicked = self.indexAt(event.pos())
        actions = []
        self.popup_menu = QMenu(self)
        clear_all_breakpoints_action = create_action(
            self,
            _("Clear breakpoints in all files"),
            triggered=lambda: self.clear_all_breakpoints.emit())
        actions.append(clear_all_breakpoints_action)
        if self.model.breakpoints:
            filename = self.model.breakpoints[index_clicked.row()][0]
            lineno = int(self.model.breakpoints[index_clicked.row()][1])
            clear_breakpoint_action = create_action(self,
                    _("Clear this breakpoint"),
                    triggered=lambda filename=filename, lineno=lineno: \
                    self.clear_breakpoint.emit(filename, lineno))
            actions.insert(0, clear_breakpoint_action)

            edit_breakpoint_action = create_action(self,
                    _("Edit this breakpoint"),
                    triggered=lambda filename=filename, lineno=lineno: \
                    (self.edit_goto.emit(filename, lineno, ''),
                     self.set_or_edit_conditional_breakpoint.emit())
                    )
            actions.append(edit_breakpoint_action)
        add_actions(self.popup_menu, actions)
        self.popup_menu.popup(event.globalPos())
        event.accept()
コード例 #3
0
ファイル: console.py プロジェクト: AminJamalzadeh/spyder
 def get_plugin_actions(self):
     """Return a list of actions related to plugin"""
     quit_action = create_action(self, _("&Quit"),
                                 icon=ima.icon('exit'), 
                                 tip=_("Quit"),
                                 triggered=self.quit)
     self.register_shortcut(quit_action, "_", "Quit", "Ctrl+Q")
     run_action = create_action(self, _("&Run..."), None,
                         ima.icon('run_small'),
                         _("Run a Python script"),
                         triggered=self.run_script)
     environ_action = create_action(self,
                         _("Environment variables..."),
                         icon=ima.icon('environ'),
                         tip=_("Show and edit environment variables"
                                     " (for current session)"),
                         triggered=self.show_env)
     syspath_action = create_action(self,
                         _("Show sys.path contents..."),
                         icon=ima.icon('syspath'),
                         tip=_("Show (read-only) sys.path"),
                         triggered=self.show_syspath)
     buffer_action = create_action(self,
                         _("Buffer..."), None,
                         tip=_("Set maximum line count"),
                         triggered=self.change_max_line_count)
     exteditor_action = create_action(self,
                         _("External editor path..."), None, None,
                         _("Set external editor executable path"),
                         triggered=self.change_exteditor)
     wrap_action = create_action(self,
                         _("Wrap lines"),
                         toggled=self.toggle_wrap_mode)
     wrap_action.setChecked(self.get_option('wrap'))
     calltips_action = create_action(self, _("Display balloon tips"),
         toggled=self.toggle_calltips)
     calltips_action.setChecked(self.get_option('calltips'))
     codecompletion_action = create_action(self,
                                       _("Automatic code completion"),
                                       toggled=self.toggle_codecompletion)
     codecompletion_action.setChecked(self.get_option('codecompletion/auto'))
     codecompenter_action = create_action(self,
                                 _("Enter key selects completion"),
                                 toggled=self.toggle_codecompletion_enter)
     codecompenter_action.setChecked(self.get_option(
                                                 'codecompletion/enter_key'))
     
     option_menu = QMenu(_('Internal console settings'), self)
     option_menu.setIcon(ima.icon('tooloptions'))
     add_actions(option_menu, (buffer_action, wrap_action,
                               calltips_action, codecompletion_action,
                               codecompenter_action, exteditor_action))
                 
     plugin_actions = [None, run_action, environ_action, syspath_action,
                       option_menu, None, quit_action]
     
     # Add actions to context menu
     add_actions(self.shell.menu, plugin_actions)
     
     return plugin_actions
コード例 #4
0
ファイル: tabs.py プロジェクト: MarvinLiu0810/spyder
    def __init__(self, parent, actions=None, menu=None,
                 corner_widgets=None, menu_use_tooltips=False):
        QTabWidget.__init__(self, parent)
        self.setUsesScrollButtons(True)

        # To style tabs on Mac
        if sys.platform == 'darwin':
            self.setObjectName('plugin-tab')

        self.corner_widgets = {}
        self.menu_use_tooltips = menu_use_tooltips
        
        if menu is None:
            self.menu = QMenu(self)
            if actions:
                add_actions(self.menu, actions)
        else:
            self.menu = menu
            
        # Corner widgets
        if corner_widgets is None:
            corner_widgets = {}
        corner_widgets.setdefault(Qt.TopLeftCorner, [])
        corner_widgets.setdefault(Qt.TopRightCorner, [])
        self.browse_button = create_toolbutton(self,
                                          icon=ima.icon('browse_tab'),
                                          tip=_("Browse tabs"))
        self.browse_tabs_menu = QMenu(self)
        self.browse_button.setMenu(self.browse_tabs_menu)
        self.browse_button.setPopupMode(self.browse_button.InstantPopup)
        self.browse_tabs_menu.aboutToShow.connect(self.update_browse_tabs_menu)
        corner_widgets[Qt.TopLeftCorner] += [self.browse_button]

        self.set_corner_widgets(corner_widgets)
コード例 #5
0
 def setup_context_menu(self):
     """Setup shell context menu"""
     self.menu = QMenu(self)
     self.cut_action = create_action(self, _("Cut"),
                                     shortcut=keybinding('Cut'),
                                     icon=ima.icon('editcut'),
                                     triggered=self.cut)
     self.copy_action = create_action(self, _("Copy"),
                                      shortcut=keybinding('Copy'),
                                      icon=ima.icon('editcopy'),
                                      triggered=self.copy)
     paste_action = create_action(self, _("Paste"),
                                  shortcut=keybinding('Paste'),
                                  icon=ima.icon('editpaste'),
                                  triggered=self.paste)
     save_action = create_action(self, _("Save history log..."),
                                 icon=ima.icon('filesave'),
                                 tip=_("Save current history log (i.e. all "
                                       "inputs and outputs) in a text file"),
                                 triggered=self.save_historylog)
     self.delete_action = create_action(self, _("Delete"),
                                 shortcut=keybinding('Delete'),
                                 icon=ima.icon('editdelete'),
                                 triggered=self.delete)
     selectall_action = create_action(self, _("Select All"),
                                 shortcut=keybinding('SelectAll'),
                                 icon=ima.icon('selectall'),
                                 triggered=self.selectAll)
     add_actions(self.menu, (self.cut_action, self.copy_action,
                             paste_action, self.delete_action, None,
                             selectall_action, None, save_action) )
コード例 #6
0
    def __init__(self,
                 parent,
                 actions=None,
                 menu=None,
                 corner_widgets=None,
                 menu_use_tooltips=False):
        QTabWidget.__init__(self, parent)

        self.setUsesScrollButtons(True)

        self.corner_widgets = {}
        self.menu_use_tooltips = menu_use_tooltips

        if menu is None:
            self.menu = QMenu(self)
            if actions:
                add_actions(self.menu, actions)
        else:
            self.menu = menu

        # Corner widgets
        if corner_widgets is None:
            corner_widgets = {}
        corner_widgets.setdefault(Qt.TopLeftCorner, [])
        corner_widgets.setdefault(Qt.TopRightCorner, [])
        self.browse_button = create_toolbutton(self,
                                               icon=get_icon("browse_tab.png"),
                                               tip=_("Browse tabs"))
        self.browse_tabs_menu = QMenu(self)
        self.browse_button.setMenu(self.browse_tabs_menu)
        self.browse_button.setPopupMode(self.browse_button.InstantPopup)
        self.browse_tabs_menu.aboutToShow.connect(self.update_browse_tabs_menu)
        corner_widgets[Qt.TopLeftCorner] += [self.browse_button]

        self.set_corner_widgets(corner_widgets)
コード例 #7
0
ファイル: tabs.py プロジェクト: Micseb/spyder
    def __init__(self, parent, actions=None, menu=None,
                 corner_widgets=None, menu_use_tooltips=False):
        QTabWidget.__init__(self, parent)
        
        self.setUsesScrollButtons(True)
        
        self.corner_widgets = {}
        self.menu_use_tooltips = menu_use_tooltips
        
        if menu is None:
            self.menu = QMenu(self)
            if actions:
                add_actions(self.menu, actions)
        else:
            self.menu = menu
            
        # Corner widgets
        if corner_widgets is None:
            corner_widgets = {}
        corner_widgets.setdefault(Qt.TopLeftCorner, [])
        corner_widgets.setdefault(Qt.TopRightCorner, [])
        self.browse_button = create_toolbutton(self,
                                          icon=get_icon("browse_tab.png"),
                                          tip=_("Browse tabs"))
        self.browse_tabs_menu = QMenu(self)
        self.browse_button.setMenu(self.browse_tabs_menu)
        self.browse_button.setPopupMode(self.browse_button.InstantPopup)
        self.browse_tabs_menu.aboutToShow.connect(self.update_browse_tabs_menu)
        corner_widgets[Qt.TopLeftCorner] += [self.browse_button]

        self.set_corner_widgets(corner_widgets)
コード例 #8
0
ファイル: importwizard.py プロジェクト: gyenney/Tools
    def __init__(self, parent):
        QTableView.__init__(self, parent)
        self._model = None

        # Setting up actions
        self.date_dayfirst_action = create_action(
            self, "dayfirst", triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=True)
        )
        self.date_monthfirst_action = create_action(
            self, "monthfirst", triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=False)
        )
        self.perc_action = create_action(self, "perc", triggered=ft_partial(self.parse_to_type, atype="perc"))
        self.acc_action = create_action(self, "account", triggered=ft_partial(self.parse_to_type, atype="account"))
        self.str_action = create_action(self, "unicode", triggered=ft_partial(self.parse_to_type, atype="unicode"))
        self.int_action = create_action(self, "int", triggered=ft_partial(self.parse_to_type, atype="int"))
        self.float_action = create_action(self, "float", triggered=ft_partial(self.parse_to_type, atype="float"))

        # Setting up menus
        self.date_menu = QMenu()
        self.date_menu.setTitle("Date")
        add_actions(self.date_menu, (self.date_dayfirst_action, self.date_monthfirst_action))
        self.parse_menu = QMenu(self)
        self.parse_menu.addMenu(self.date_menu)
        add_actions(self.parse_menu, (self.perc_action, self.acc_action))
        self.parse_menu.setTitle("String to")
        self.opt_menu = QMenu(self)
        self.opt_menu.addMenu(self.parse_menu)
        add_actions(self.opt_menu, (self.str_action, self.int_action, self.float_action))
コード例 #9
0
ファイル: browser.py プロジェクト: jromang/retina-old
 def contextMenuEvent(self, event):
     menu = QMenu(self)
     add_actions( menu, (self.pageAction(QWebPage.Back),
                         self.pageAction(QWebPage.Forward), None,
                         self.pageAction(QWebPage.SelectAll),
                         self.pageAction(QWebPage.Copy), None,
                         self.pageAction(QWebPage.Reload), None,
                         self.zoom_in_action, self.zoom_out_action) )
     menu.popup(event.globalPos())
     event.accept()
コード例 #10
0
 def setup(self, name_filters=['*.py', '*.pyw'], show_all=False):
     """Setup tree widget"""
     self.setup_view()
     
     self.set_name_filters(name_filters)
     self.show_all = show_all
     
     # Setup context menu
     self.menu = QMenu(self)
     self.common_actions = self.setup_common_actions()
コード例 #11
0
class BreakpointTableView(QTableView):
    def __init__(self, parent, data):
        QTableView.__init__(self, parent)
        self.model = BreakpointTableModel(self, data)
        self.setModel(self.model)
        self.delegate = BreakpointDelegate(self)
        self.setItemDelegate(self.delegate)

        self.setup_table()

    def setup_table(self):
        """Setup table"""
        self.horizontalHeader().setStretchLastSection(True)
        self.adjust_columns()
        self.columnAt(0)
        # Sorting columns
        self.setSortingEnabled(False)
        self.sortByColumn(0, Qt.DescendingOrder)

    def adjust_columns(self):
        """Resize three first columns to contents"""
        for col in range(3):
            self.resizeColumnToContents(col)

    def mouseDoubleClickEvent(self, event):
        """Reimplement Qt method"""
        index_clicked = self.indexAt(event.pos())
        if self.model.breakpoints:
            filename = self.model.breakpoints[index_clicked.row()][0]
            line_number_str = self.model.breakpoints[index_clicked.row()][1]
            self.emit(SIGNAL("edit_goto(QString,int,QString)"), filename, int(line_number_str), "")

    def contextMenuEvent(self, event):
        index_clicked = self.indexAt(event.pos())
        actions = []
        self.popup_menu = QMenu(self)
        if self.model.breakpoints:
            filename = self.model.breakpoints[index_clicked.row()][0]
            lineno = int(self.model.breakpoints[index_clicked.row()][1])
            clear_breakpoint_action = create_action(
                self,
                _("Clear this breakpoint"),
                triggered=lambda filename=filename, lineno=lineno: self.emit(
                    SIGNAL("clear_breakpoint(QString,int)"), filename, lineno
                ),
            )
            actions.append(clear_breakpoint_action)
        clear_all_breakpoints_action = create_action(
            self, _("Clear breakpoints in all files"), triggered=lambda: self.emit(SIGNAL("clear_all_breakpoints()"))
        )
        actions.append(clear_all_breakpoints_action)
        add_actions(self.popup_menu, actions)
        self.popup_menu.popup(event.globalPos())
        event.accept()
コード例 #12
0
ファイル: browser.py プロジェクト: CVML/spyder
 def contextMenuEvent(self, event):
     menu = QMenu(self)
     actions = [self.pageAction(QWebPage.Back),
                self.pageAction(QWebPage.Forward), None,
                self.pageAction(QWebPage.SelectAll),
                self.pageAction(QWebPage.Copy), None,
                self.zoom_in_action, self.zoom_out_action]
     if DEV:
         settings = self.page().settings()
         settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
         actions += [None, self.pageAction(QWebPage.InspectElement)]
     add_actions(menu, actions)
     menu.popup(event.globalPos())
     event.accept()
コード例 #13
0
ファイル: browser.py プロジェクト: yfma2010/spyder
 def contextMenuEvent(self, event):
     menu = QMenu(self)
     actions = [self.pageAction(QWebPage.Back),
                self.pageAction(QWebPage.Forward), None,
                self.pageAction(QWebPage.SelectAll),
                self.pageAction(QWebPage.Copy), None,
                self.zoom_in_action, self.zoom_out_action]
     if DEV:
         settings = self.page().settings()
         settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
         actions += [None, self.pageAction(QWebPage.InspectElement)]
     add_actions(menu, actions)
     menu.popup(event.globalPos())
     event.accept()
コード例 #14
0
ファイル: tour.py プロジェクト: MarvinLiu0810/spyder
    def context_menu_requested(self, event):
        """ """
        pos = QPoint(event.x(), event.y())
        menu = QMenu(self)

        actions = []
        action_title = create_action(self, _('Go to step: '), icon=QIcon())
        action_title.setDisabled(True)
        actions.append(action_title)
#        actions.append(create_action(self, _(': '), icon=QIcon()))

        add_actions(menu, actions)

        menu.popup(self.mapToGlobal(pos))
コード例 #15
0
    def context_menu_requested(self, event):
        """ """
        pos = QPoint(event.x(), event.y())
        menu = QMenu(self)

        actions = []
        action_title = create_action(self, _('Go to step: '), icon=QIcon())
        action_title.setDisabled(True)
        actions.append(action_title)
        #        actions.append(create_action(self, _(': '), icon=QIcon()))

        add_actions(menu, actions)

        menu.popup(self.mapToGlobal(pos))
コード例 #16
0
ファイル: pythonshell.py プロジェクト: yfma2010/spyder
 def get_options_menu(self):
     ExternalShellBase.get_options_menu(self)
     self.interact_action = create_action(self, _("Interact"))
     self.interact_action.setCheckable(True)
     self.debug_action = create_action(self, _("Debug"))
     self.debug_action.setCheckable(True)
     self.args_action = create_action(self, _("Arguments..."),
                                      triggered=self.get_arguments)
     self.post_mortem_action = create_action(self, _("Post Mortem Debug"))
     self.post_mortem_action.setCheckable(True)
     run_settings_menu = QMenu(_("Run settings"), self)
     add_actions(run_settings_menu,
                 (self.interact_action, self.debug_action, self.args_action,
                  self.post_mortem_action))
     self.cwd_button = create_action(self, _("Working directory"),
                             icon=ima.icon('DirOpenIcon'),
                             tip=_("Set current working directory"),
                             triggered=self.set_current_working_directory)
     self.env_button = create_action(self, _("Environment variables"),
                                     icon=ima.icon('environ'),
                                     triggered=self.show_env)
     self.syspath_button = create_action(self,
                                         _("Show sys.path contents"),
                                         icon=ima.icon('syspath'),
                                         triggered=self.show_syspath)
     actions = [run_settings_menu, self.show_time_action, None,
                self.cwd_button, self.env_button, self.syspath_button]
     if self.menu_actions is not None:
         actions += [None]+self.menu_actions
     return actions
コード例 #17
0
 def setup_menu(self):
     """Setup context menu"""
     copy_action = create_action(self,
                                 _('Copy'),
                                 shortcut=keybinding('Copy'),
                                 icon=ima.icon('editcopy'),
                                 triggered=self.copy,
                                 context=Qt.WidgetShortcut)
     functions = ((_("To bool"), bool), (_("To complex"),
                                         complex), (_("To int"), int),
                  (_("To float"), float), (_("To str"), to_text_string))
     types_in_menu = [copy_action]
     for name, func in functions:
         # QAction.triggered works differently for PySide and PyQt
         if not API == 'pyside':
             slot = lambda _checked, func=func: self.change_type(func)
         else:
             slot = lambda func=func: self.change_type(func)
         types_in_menu += [
             create_action(self,
                           name,
                           triggered=slot,
                           context=Qt.WidgetShortcut)
         ]
     menu = QMenu(self)
     add_actions(menu, types_in_menu)
     return menu
コード例 #18
0
ファイル: breakpointsgui.py プロジェクト: rachelqhuang/spyder
    def contextMenuEvent(self, event):
        index_clicked = self.indexAt(event.pos())
        actions = []
        self.popup_menu = QMenu(self)
        clear_all_breakpoints_action = create_action(
            self, _("Clear breakpoints in all files"), triggered=lambda: self.clear_all_breakpoints.emit()
        )
        actions.append(clear_all_breakpoints_action)
        if self.model.breakpoints:
            filename = self.model.breakpoints[index_clicked.row()][0]
            lineno = int(self.model.breakpoints[index_clicked.row()][1])
            clear_breakpoint_action = create_action(
                self,
                _("Clear this breakpoint"),
                triggered=lambda filename=filename, lineno=lineno: self.clear_breakpoint.emit(filename, lineno),
            )
            actions.insert(0, clear_breakpoint_action)

            edit_breakpoint_action = create_action(
                self,
                _("Edit this breakpoint"),
                triggered=lambda filename=filename, lineno=lineno: (
                    self.edit_goto.emit(filename, lineno, ""),
                    self.set_or_edit_conditional_breakpoint.emit(),
                ),
            )
            actions.append(edit_breakpoint_action)
        add_actions(self.popup_menu, actions)
        self.popup_menu.popup(event.globalPos())
        event.accept()
コード例 #19
0
ファイル: explorer.py プロジェクト: YP-Ye/spyderlib
    def __init__(self, parent=None, name_filters=['*.py', '*.pyw'],
                 valid_types=('.py', '.pyw'), show_all=False,
                 show_cd_only=None, show_toolbar=True, show_icontext=True):
        QWidget.__init__(self, parent)
        
        self.treewidget = ExplorerTreeWidget(self, show_cd_only=show_cd_only)
        self.treewidget.setup(name_filters=name_filters,
                              valid_types=valid_types, show_all=show_all)
        self.treewidget.chdir(getcwd())
        
        toolbar_action = create_action(self, _("Show toolbar"),
                                       toggled=self.toggle_toolbar)
        icontext_action = create_action(self, _("Show icons and text"),
                                        toggled=self.toggle_icontext)
        self.treewidget.common_actions += [None,
                                           toolbar_action, icontext_action]
        
        # Setup toolbar
        self.toolbar = QToolBar(self)
        self.toolbar.setIconSize(QSize(16, 16))
        
        self.previous_action = create_action(self, text=_("Previous"),
                            icon=get_icon('previous.png'),
                            triggered=self.treewidget.go_to_previous_directory)
        self.toolbar.addAction(self.previous_action)
        self.previous_action.setEnabled(False)
        self.connect(self.treewidget, SIGNAL("set_previous_enabled(bool)"),
                     self.previous_action.setEnabled)
        
        self.next_action = create_action(self, text=_("Next"),
                            icon=get_icon('next.png'),
                            triggered=self.treewidget.go_to_next_directory)
        self.toolbar.addAction(self.next_action)
        self.next_action.setEnabled(False)
        self.connect(self.treewidget, SIGNAL("set_next_enabled(bool)"),
                     self.next_action.setEnabled)
        
        parent_action = create_action(self, text=_("Parent"),
                            icon=get_icon('up.png'),
                            triggered=self.treewidget.go_to_parent_directory)
        self.toolbar.addAction(parent_action)

        options_action = create_action(self, text='', tip=_("Options"),
                                       icon=get_icon('tooloptions.png'))
        self.toolbar.addAction(options_action)
        widget = self.toolbar.widgetForAction(options_action)
        widget.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        add_actions(menu, self.treewidget.common_actions)
        options_action.setMenu(menu)
            
        toolbar_action.setChecked(show_toolbar)
        self.toggle_toolbar(show_toolbar)   
        icontext_action.setChecked(show_icontext)
        self.toggle_icontext(show_icontext)     
        
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.toolbar)
        vlayout.addWidget(self.treewidget)
        self.setLayout(vlayout)
コード例 #20
0
ファイル: shell.py プロジェクト: ImadBouirmane/spyder
 def setup_context_menu(self):
     """Setup shell context menu"""
     self.menu = QMenu(self)
     self.cut_action = create_action(self, _("Cut"),
                                     shortcut=keybinding('Cut'),
                                     icon=ima.icon('editcut'),
                                     triggered=self.cut)
     self.copy_action = create_action(self, _("Copy"),
                                      shortcut=keybinding('Copy'),
                                      icon=ima.icon('editcopy'),
                                      triggered=self.copy)
     paste_action = create_action(self, _("Paste"),
                                  shortcut=keybinding('Paste'),
                                  icon=ima.icon('editpaste'),
                                  triggered=self.paste)
     save_action = create_action(self, _("Save history log..."),
                                 icon=ima.icon('filesave'),
                                 tip=_("Save current history log (i.e. all "
                                       "inputs and outputs) in a text file"),
                                 triggered=self.save_historylog)
     self.delete_action = create_action(self, _("Delete"),
                                 shortcut=keybinding('Delete'),
                                 icon=ima.icon('editdelete'),
                                 triggered=self.delete)
     selectall_action = create_action(self, _("Select All"),
                                 shortcut=keybinding('SelectAll'),
                                 icon=ima.icon('selectall'),
                                 triggered=self.selectAll)
     add_actions(self.menu, (self.cut_action, self.copy_action,
                             paste_action, self.delete_action, None,
                             selectall_action, None, save_action) )
コード例 #21
0
    def get_toolbar_buttons(self):
        """Return toolbar buttons list"""
        #TODO: Eventually add some buttons (Empty for now)
        # (see for example: spyderlib/widgets/externalshell/baseshell.py)
        buttons = []
        # Code to add the stop button
        if self.stop_button is None:
            self.stop_button = create_toolbutton(
                self,
                text=_("Stop"),
                icon=self.stop_icon,
                tip=_("Stop the current command"))
            self.disable_stop_button()
            # set click event handler
            self.stop_button.clicked.connect(self.stop_button_click_handler)
        if self.stop_button is not None:
            buttons.append(self.stop_button)

        if self.options_button is None:
            options = self.get_options_menu()
            if options:
                self.options_button = create_toolbutton(
                    self, text=_("Options"), icon=get_icon('tooloptions.png'))
                self.options_button.setPopupMode(QToolButton.InstantPopup)
                menu = QMenu(self)
                add_actions(menu, options)
                self.options_button.setMenu(menu)
        if self.options_button is not None:
            buttons.append(self.options_button)

        return buttons
コード例 #22
0
 def get_toolbar_buttons(self):
     if self.run_button is None:
         self.run_button = create_toolbutton(
             self,
             text=_("Run"),
             icon=get_icon('run.png'),
             tip=_("Run again this program"),
             triggered=self.start_shell)
     if self.kill_button is None:
         self.kill_button = create_toolbutton(
             self,
             text=_("Kill"),
             icon=get_icon('kill.png'),
             tip=_("Kills the current process, "
                   "causing it to exit immediately"))
     buttons = [self.run_button]
     if self.options_button is None:
         options = self.get_options_menu()
         if options:
             self.options_button = create_toolbutton(
                 self, text=_("Options"), icon=get_icon('tooloptions.png'))
             self.options_button.setPopupMode(QToolButton.InstantPopup)
             menu = QMenu(self)
             add_actions(menu, options)
             self.options_button.setMenu(menu)
     if self.options_button is not None:
         buttons.append(self.options_button)
     buttons.append(self.kill_button)
     return buttons
コード例 #23
0
    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.connect(self, SIGNAL("edit_goto(QString,int,QString)"),
                     self.main.editor.load)
        self.connect(self, SIGNAL('redirect_stdio(bool)'),
                     self.main.redirect_internalshell_stdio)
        self.main.add_dockwidget(self)

        c2p_act = create_action(self,
                                _("Import COMBINE as Python"),
                                triggered=self.run_c2p)
        c2p_act.setEnabled(True)
        #self.register_shortcut(c2p_act, context="Combine to Python",
        #                       name="Import combine archive", default="Alt-C")
        for item in self.main.file_menu_actions:
            try:
                menu_title = item.title()
            except AttributeError:
                pass
            else:
                if not is_text_string(menu_title):  # string is a QString
                    menu_title = to_text_string(menu_title.toUtf8)
                if item.title() == str("Import"):
                    item.addAction(c2p_act)
        c2p_actions = (None, c2p_act)
        import_menu = QMenu(_("Import"))
        add_actions(import_menu, c2p_actions)
        self.main.file_menu_actions.insert(8, import_menu)
コード例 #24
0
    def __init__(self, parent):
        QTreeWidget.__init__(self, parent)
        self.setItemsExpandable(True)
        self.setColumnCount(1)
        self.itemActivated.connect(self.activated)
        self.itemClicked.connect(self.clicked)
        # Setup context menu
        self.menu = QMenu(self)
        self.collapse_all_action = None
        self.collapse_selection_action = None
        self.expand_all_action = None
        self.expand_selection_action = None
        self.common_actions = self.setup_common_actions()

        self.__expanded_state = None

        self.itemSelectionChanged.connect(self.item_selection_changed)
        self.item_selection_changed()
コード例 #25
0
ファイル: arrayeditor.py プロジェクト: StevenXXu/spyder
 def setup_menu(self):
     """Setup context menu"""
     self.copy_action = create_action(self, _('Copy'),
                                      shortcut=keybinding('Copy'),
                                      icon=ima.icon('editcopy'),
                                      triggered=self.copy,
                                      context=Qt.WidgetShortcut)
     menu = QMenu(self)
     add_actions(menu, [self.copy_action, ])
     return menu
コード例 #26
0
ファイル: explorer.py プロジェクト: sonofeft/spyder
    def setup(self, name_filters=["*.py", "*.pyw"], show_all=False):
        """Setup tree widget"""
        self.setup_view()

        self.set_name_filters(name_filters)
        self.show_all = show_all

        # Setup context menu
        self.menu = QMenu(self)
        self.common_actions = self.setup_common_actions()
コード例 #27
0
ファイル: history.py プロジェクト: zssure-thu/spyder
    def __init__(self, parent):
        self.tabwidget = None
        self.menu_actions = None
        self.dockviewer = None
        self.wrap_action = None

        self.editors = []
        self.filenames = []
        self.icons = []
        if PYQT5:
            SpyderPluginWidget.__init__(self, parent, main=parent)
        else:
            SpyderPluginWidget.__init__(self, parent)

        # Initialize plugin
        self.initialize_plugin()

        self.set_default_color_scheme()

        layout = QVBoxLayout()
        self.tabwidget = Tabs(self, self.menu_actions)
        self.tabwidget.currentChanged.connect(self.refresh_plugin)
        self.tabwidget.move_data.connect(self.move_tab)

        if sys.platform == 'darwin':
            tab_container = QWidget()
            tab_container.setObjectName('tab-container')
            tab_layout = QHBoxLayout(tab_container)
            tab_layout.setContentsMargins(0, 0, 0, 0)
            tab_layout.addWidget(self.tabwidget)
            layout.addWidget(tab_container)
        else:
            layout.addWidget(self.tabwidget)

        self.tabwidget.setStyleSheet("QTabWidget::pane {border: 0;}")

        # Menu as corner widget
        options_button = create_toolbutton(self,
                                           text=_('Options'),
                                           icon=ima.icon('tooloptions'))
        options_button.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        add_actions(menu, self.menu_actions)
        options_button.setMenu(menu)
        self.tabwidget.setCornerWidget(options_button)

        # Find/replace widget
        self.find_widget = FindReplace(self)
        self.find_widget.hide()
        self.register_widget_shortcuts("Editor", self.find_widget)

        layout.addWidget(self.find_widget)

        self.setLayout(layout)
コード例 #28
0
ファイル: explorer.py プロジェクト: jromang/retina-old
 def setup(self, name_filters=['*.py', '*.pyw'],
           valid_types= ('.py', '.pyw'), show_all=False):
     """Setup tree widget"""
     self.setup_view()
     
     self.set_name_filters(name_filters)
     self.valid_types = valid_types
     self.show_all = show_all
     
     # Setup context menu
     self.menu = QMenu(self)
     self.common_actions = self.setup_common_actions()
コード例 #29
0
ファイル: importwizard.py プロジェクト: zssure-thu/spyder
    def __init__(self, parent):
        QTableView.__init__(self, parent)
        self._model = None

        # Setting up actions
        self.date_dayfirst_action = create_action(self, "dayfirst",
            triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=True))
        self.date_monthfirst_action = create_action(self, "monthfirst",
            triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=False))
        self.perc_action = create_action(self, "perc",
            triggered=ft_partial(self.parse_to_type, atype="perc"))
        self.acc_action = create_action(self, "account",
            triggered=ft_partial(self.parse_to_type, atype="account"))
        self.str_action = create_action(self, "unicode",
            triggered=ft_partial(self.parse_to_type, atype="unicode"))
        self.int_action = create_action(self, "int",
            triggered=ft_partial(self.parse_to_type, atype="int"))
        self.float_action = create_action(self, "float",
            triggered=ft_partial(self.parse_to_type, atype="float"))

        # Setting up menus
        self.date_menu = QMenu()
        self.date_menu.setTitle("Date")
        add_actions( self.date_menu, (self.date_dayfirst_action,
                                      self.date_monthfirst_action))
        self.parse_menu = QMenu(self)
        self.parse_menu.addMenu(self.date_menu)
        add_actions( self.parse_menu, (self.perc_action, self.acc_action))
        self.parse_menu.setTitle("String to")
        self.opt_menu = QMenu(self)
        self.opt_menu.addMenu(self.parse_menu)
        add_actions( self.opt_menu, (self.str_action, self.int_action,
                                     self.float_action))
コード例 #30
0
ファイル: breakpointsgui.py プロジェクト: wodole/spyder
    def contextMenuEvent(self, event):
        index_clicked = self.indexAt(event.pos())
        actions = []
        self.popup_menu = QMenu(self)
        clear_all_breakpoints_action = create_action(
            self,
            _("Clear breakpoints in all files"),
            triggered=lambda: self.clear_all_breakpoints.emit())
        actions.append(clear_all_breakpoints_action)
        if self.model.breakpoints:
            filename = self.model.breakpoints[index_clicked.row()][0]
            lineno = int(self.model.breakpoints[index_clicked.row()][1])
            # QAction.triggered works differently for PySide and PyQt
            if not API == 'pyside':
                clear_slot = lambda _checked, filename=filename, lineno=lineno: \
                    self.clear_breakpoint.emit(filename, lineno)
                edit_slot = lambda _checked, filename=filename, lineno=lineno: \
                    (self.edit_goto.emit(filename, lineno, ''),
                     self.set_or_edit_conditional_breakpoint.emit())
            else:
                clear_slot = lambda filename=filename, lineno=lineno: \
                    self.clear_breakpoint.emit(filename, lineno)
                edit_slot = lambda filename=filename, lineno=lineno: \
                    (self.edit_goto.emit(filename, lineno, ''),
                     self.set_or_edit_conditional_breakpoint.emit())

            clear_breakpoint_action = create_action(self,
                                                    _("Clear this breakpoint"),
                                                    triggered=clear_slot)
            actions.insert(0, clear_breakpoint_action)

            edit_breakpoint_action = create_action(self,
                                                   _("Edit this breakpoint"),
                                                   triggered=edit_slot)
            actions.append(edit_breakpoint_action)
        add_actions(self.popup_menu, actions)
        self.popup_menu.popup(event.globalPos())
        event.accept()
コード例 #31
0
    def __init__(self,
                 parent,
                 actions=None,
                 menu=None,
                 corner_widgets=None,
                 menu_use_tooltips=False):
        QTabWidget.__init__(self, parent)
        self.setUsesScrollButtons(True)

        # To style tabs on Mac
        if sys.platform == 'darwin':
            self.setObjectName('plugin-tab')

        self.corner_widgets = {}
        self.menu_use_tooltips = menu_use_tooltips

        if menu is None:
            self.menu = QMenu(self)
            if actions:
                add_actions(self.menu, actions)
        else:
            self.menu = menu

        # Corner widgets
        if corner_widgets is None:
            corner_widgets = {}
        corner_widgets.setdefault(Qt.TopLeftCorner, [])
        corner_widgets.setdefault(Qt.TopRightCorner, [])
        self.browse_button = create_toolbutton(self,
                                               icon=ima.icon('browse_tab'),
                                               tip=_("Browse tabs"))
        self.browse_tabs_menu = QMenu(self)
        self.browse_button.setMenu(self.browse_tabs_menu)
        self.browse_button.setPopupMode(self.browse_button.InstantPopup)
        self.browse_tabs_menu.aboutToShow.connect(self.update_browse_tabs_menu)
        corner_widgets[Qt.TopLeftCorner] += [self.browse_button]

        self.set_corner_widgets(corner_widgets)
コード例 #32
0
ファイル: onecolumntree.py プロジェクト: gyenney/Tools
    def __init__(self, parent):
        QTreeWidget.__init__(self, parent)
        self.setItemsExpandable(True)
        self.setColumnCount(1)
        self.itemActivated.connect(self.activated)
        self.itemClicked.connect(self.clicked)
        # Setup context menu
        self.menu = QMenu(self)
        self.collapse_all_action = None
        self.collapse_selection_action = None
        self.expand_all_action = None
        self.expand_selection_action = None
        self.common_actions = self.setup_common_actions()
        
        self.__expanded_state = None

        self.itemSelectionChanged.connect(self.item_selection_changed)
        self.item_selection_changed()
コード例 #33
0
ファイル: dataframeeditor.py プロジェクト: StevenXXu/spyder
 def setup_menu(self):
     """Setup context menu"""
     copy_action = create_action(self, _('Copy'),
                                 shortcut=keybinding('Copy'),
                                 icon=ima.icon('editcopy'),
                                 triggered=self.copy,
                                 context=Qt.WidgetShortcut)
     functions = ((_("To bool"), bool), (_("To complex"), complex),
                  (_("To int"), int), (_("To float"), float),
                  (_("To str"), to_text_string))
     types_in_menu = [copy_action]
     for name, func in functions:
         types_in_menu += [create_action(self, name,
                                         triggered=lambda func=func:
                                                   self.change_type(func),
                                         context=Qt.WidgetShortcut)]
     menu = QMenu(self)
     add_actions(menu, types_in_menu)
     return menu
コード例 #34
0
    def __init__(self, parent):
        self.tabwidget = None
        self.menu_actions = None
        self.dockviewer = None
        self.wrap_action = None

        self.editors = []
        self.filenames = []
        self.icons = []

        SpyderPluginWidget.__init__(self, parent)

        # Initialize plugin
        self.initialize_plugin()

        self.set_default_color_scheme()

        layout = QVBoxLayout()
        self.tabwidget = Tabs(self, self.menu_actions)
        self.connect(self.tabwidget, SIGNAL('currentChanged(int)'),
                     self.refresh_plugin)
        self.connect(self.tabwidget, SIGNAL('move_data(int,int)'),
                     self.move_tab)
        layout.addWidget(self.tabwidget)

        # Menu as corner widget
        options_button = create_toolbutton(self,
                                           text=_("Options"),
                                           icon=get_icon('tooloptions.png'))
        options_button.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        add_actions(menu, self.menu_actions)
        options_button.setMenu(menu)
        self.tabwidget.setCornerWidget(options_button)

        # Find/replace widget
        self.find_widget = FindReplace(self)
        self.find_widget.hide()
        self.register_widget_shortcuts("Editor", self.find_widget)

        layout.addWidget(self.find_widget)

        self.setLayout(layout)
コード例 #35
0
 def contextMenuEvent(self, event):
     index_clicked = self.indexAt(event.pos())
     actions = []
     self.popup_menu = QMenu(self)
     if self.model.breakpoints:
         filename = self.model.breakpoints[index_clicked.row()][0]
         lineno = int(self.model.breakpoints[index_clicked.row()][1])
         clear_breakpoint_action = create_action(
             self,
             _("Clear this breakpoint"),
             triggered=lambda filename=filename, lineno=lineno: self.emit(
                 SIGNAL("clear_breakpoint(QString,int)"), filename, lineno
             ),
         )
         actions.append(clear_breakpoint_action)
     clear_all_breakpoints_action = create_action(
         self, _("Clear breakpoints in all files"), triggered=lambda: self.emit(SIGNAL("clear_all_breakpoints()"))
     )
     actions.append(clear_all_breakpoints_action)
     add_actions(self.popup_menu, actions)
     self.popup_menu.popup(event.globalPos())
     event.accept()
コード例 #36
0
    def contextMenuEvent(self, event):
        index_clicked = self.indexAt(event.pos())
        actions = []
        self.popup_menu = QMenu(self)
        clear_all_breakpoints_action = create_action(self, 
            _("Clear breakpoints in all files"),
            triggered=lambda: self.clear_all_breakpoints.emit())
        actions.append(clear_all_breakpoints_action)
        if self.model.breakpoints:
            filename = self.model.breakpoints[index_clicked.row()][0]
            lineno = int(self.model.breakpoints[index_clicked.row()][1])
            # QAction.triggered works differently for PySide and PyQt
            if not API == 'pyside':
                clear_slot = lambda _checked, filename=filename, lineno=lineno: \
                    self.clear_breakpoint.emit(filename, lineno)
                edit_slot = lambda _checked, filename=filename, lineno=lineno: \
                    (self.edit_goto.emit(filename, lineno, ''),
                     self.set_or_edit_conditional_breakpoint.emit())
            else:
                clear_slot = lambda filename=filename, lineno=lineno: \
                    self.clear_breakpoint.emit(filename, lineno)
                edit_slot = lambda filename=filename, lineno=lineno: \
                    (self.edit_goto.emit(filename, lineno, ''),
                     self.set_or_edit_conditional_breakpoint.emit())

            clear_breakpoint_action = create_action(self,
                    _("Clear this breakpoint"),
                    triggered=clear_slot)
            actions.insert(0,clear_breakpoint_action)

            edit_breakpoint_action = create_action(self,
                    _("Edit this breakpoint"),
                    triggered=edit_slot)
            actions.append(edit_breakpoint_action)
        add_actions(self.popup_menu, actions)        
        self.popup_menu.popup(event.globalPos())
        event.accept()
コード例 #37
0
ファイル: onecolumntree.py プロジェクト: gyenney/Tools
class OneColumnTree(QTreeWidget):
    """One-column tree widget with context menu, ..."""
    def __init__(self, parent):
        QTreeWidget.__init__(self, parent)
        self.setItemsExpandable(True)
        self.setColumnCount(1)
        self.itemActivated.connect(self.activated)
        self.itemClicked.connect(self.clicked)
        # Setup context menu
        self.menu = QMenu(self)
        self.collapse_all_action = None
        self.collapse_selection_action = None
        self.expand_all_action = None
        self.expand_selection_action = None
        self.common_actions = self.setup_common_actions()
        
        self.__expanded_state = None

        self.itemSelectionChanged.connect(self.item_selection_changed)
        self.item_selection_changed()
                     
    def activated(self, item):
        """Double-click event"""
        raise NotImplementedError
        
    def clicked(self, item):
        pass
                     
    def set_title(self, title):
        self.setHeaderLabels([title])
                     
    def setup_common_actions(self):
        """Setup context menu common actions"""
        self.collapse_all_action = create_action(self,
                                     text=_('Collapse all'),
                                     icon=ima.icon('collapse'),
                                     triggered=self.collapseAll)
        self.expand_all_action = create_action(self,
                                     text=_('Expand all'),
                                     icon=ima.icon('expand'),
                                     triggered=self.expandAll)
        self.restore_action = create_action(self,
                                     text=_('Restore'),
                                     tip=_('Restore original tree layout'),
                                     icon=ima.icon('restore'),
                                     triggered=self.restore)
        self.collapse_selection_action = create_action(self,
                                     text=_('Collapse selection'),
                                     icon=ima.icon('collapse_selection'),
                                     triggered=self.collapse_selection)
        self.expand_selection_action = create_action(self,
                                     text=_('Expand selection'),
                                     icon=ima.icon('expand_selection'),
                                     triggered=self.expand_selection)
        return [self.collapse_all_action, self.expand_all_action,
                self.restore_action, None,
                self.collapse_selection_action, self.expand_selection_action]
                     
    def update_menu(self):
        self.menu.clear()
        items = self.selectedItems()
        actions = self.get_actions_from_items(items)
        if actions:
            actions.append(None)
        actions += self.common_actions
        add_actions(self.menu, actions)
        
    def get_actions_from_items(self, items):
        # Right here: add other actions if necessary
        # (reimplement this method)
        return []

    @Slot()
    def restore(self):
        self.collapseAll()
        for item in self.get_top_level_items():
            self.expandItem(item)
        
    def is_item_expandable(self, item):
        """To be reimplemented in child class
        See example in project explorer widget"""
        return True
        
    def __expand_item(self, item):
        if self.is_item_expandable(item):
            self.expandItem(item)
            for index in range(item.childCount()):
                child = item.child(index)
                self.__expand_item(child)
    
    @Slot()
    def expand_selection(self):
        items = self.selectedItems()
        if not items:
            items = self.get_top_level_items()
        for item in items:
            self.__expand_item(item)
        if items:
            self.scrollToItem(items[0])
        
    def __collapse_item(self, item):
        self.collapseItem(item)
        for index in range(item.childCount()):
            child = item.child(index)
            self.__collapse_item(child)

    @Slot()
    def collapse_selection(self):
        items = self.selectedItems()
        if not items:
            items = self.get_top_level_items()
        for item in items:
            self.__collapse_item(item)
        if items:
            self.scrollToItem(items[0])
            
    def item_selection_changed(self):
        """Item selection has changed"""
        is_selection = len(self.selectedItems()) > 0
        self.expand_selection_action.setEnabled(is_selection)
        self.collapse_selection_action.setEnabled(is_selection)
    
    def get_top_level_items(self):
        """Iterate over top level items"""
        return [self.topLevelItem(_i) for _i in range(self.topLevelItemCount())]
    
    def get_items(self):
        """Return items (excluding top level items)"""
        itemlist = []
        def add_to_itemlist(item):
            for index in range(item.childCount()):
                citem = item.child(index)
                itemlist.append(citem)
                add_to_itemlist(citem)
        for tlitem in self.get_top_level_items():
            add_to_itemlist(tlitem)
        return itemlist
    
    def get_scrollbar_position(self):
        return (self.horizontalScrollBar().value(),
                self.verticalScrollBar().value())
        
    def set_scrollbar_position(self, position):
        hor, ver = position
        self.horizontalScrollBar().setValue(hor)
        self.verticalScrollBar().setValue(ver)
        
    def get_expanded_state(self):
        self.save_expanded_state()
        return self.__expanded_state
    
    def set_expanded_state(self, state):
        self.__expanded_state = state
        self.restore_expanded_state()
    
    def save_expanded_state(self):
        """Save all items expanded state"""
        self.__expanded_state = {}
        def add_to_state(item):
            user_text = get_item_user_text(item)
            self.__expanded_state[hash(user_text)] = item.isExpanded()
        def browse_children(item):
            add_to_state(item)
            for index in range(item.childCount()):
                citem = item.child(index)
                user_text = get_item_user_text(citem)
                self.__expanded_state[hash(user_text)] = citem.isExpanded()
                browse_children(citem)
        for tlitem in self.get_top_level_items():
            browse_children(tlitem)
    
    def restore_expanded_state(self):
        """Restore all items expanded state"""
        if self.__expanded_state is None:
            return
        for item in self.get_items()+self.get_top_level_items():
            user_text = get_item_user_text(item)
            is_expanded = self.__expanded_state.get(hash(user_text))
            if is_expanded is not None:
                item.setExpanded(is_expanded)

    def sort_top_level_items(self, key):
        """Sorting tree wrt top level items"""
        self.save_expanded_state()
        items = sorted([self.takeTopLevelItem(0)
                        for index in range(self.topLevelItemCount())], key=key)
        for index, item in enumerate(items):
            self.insertTopLevelItem(index, item)
        self.restore_expanded_state()
                     
    def contextMenuEvent(self, event):
        """Override Qt method"""
        self.update_menu()
        self.menu.popup(event.globalPos())
コード例 #38
0
    def setup(self,
              check_all=None,
              exclude_private=None,
              exclude_uppercase=None,
              exclude_capitalized=None,
              exclude_unsupported=None,
              excluded_names=None,
              truncate=None,
              minmax=None,
              remote_editing=None,
              autorefresh=None):
        """Setup the namespace browser"""
        assert self.shellwidget is not None

        self.check_all = check_all
        self.exclude_private = exclude_private
        self.exclude_uppercase = exclude_uppercase
        self.exclude_capitalized = exclude_capitalized
        self.exclude_unsupported = exclude_unsupported
        self.excluded_names = excluded_names
        self.truncate = truncate
        self.minmax = minmax
        self.remote_editing = remote_editing
        self.autorefresh = autorefresh

        if self.editor is not None:
            self.editor.setup_menu(truncate, minmax)
            self.exclude_private_action.setChecked(exclude_private)
            self.exclude_uppercase_action.setChecked(exclude_uppercase)
            self.exclude_capitalized_action.setChecked(exclude_capitalized)
            self.exclude_unsupported_action.setChecked(exclude_unsupported)
            # Don't turn autorefresh on for IPython kernels
            # See Issue 1450
            if not self.is_ipykernel:
                self.auto_refresh_button.setChecked(autorefresh)
            self.refresh_table()
            return

        # Dict editor:
        if self.is_internal_shell:
            self.editor = CollectionsEditorTableView(self,
                                                     None,
                                                     truncate=truncate,
                                                     minmax=minmax)
        else:
            self.editor = RemoteCollectionsEditorTableView(
                self,
                None,
                truncate=truncate,
                minmax=minmax,
                remote_editing=remote_editing,
                get_value_func=self.get_value,
                set_value_func=self.set_value,
                new_value_func=self.set_value,
                remove_values_func=self.remove_values,
                copy_value_func=self.copy_value,
                is_list_func=self.is_list,
                get_len_func=self.get_len,
                is_array_func=self.is_array,
                is_image_func=self.is_image,
                is_dict_func=self.is_dict,
                is_data_frame_func=self.is_data_frame,
                is_series_func=self.is_series,
                get_array_shape_func=self.get_array_shape,
                get_array_ndim_func=self.get_array_ndim,
                oedit_func=self.oedit,
                plot_func=self.plot,
                imshow_func=self.imshow,
                show_image_func=self.show_image)
        self.editor.sig_option_changed.connect(self.sig_option_changed.emit)
        self.editor.sig_files_dropped.connect(self.import_data)

        # Setup layout
        layout = QVBoxLayout()
        blayout = QHBoxLayout()
        toolbar = self.setup_toolbar(exclude_private, exclude_uppercase,
                                     exclude_capitalized, exclude_unsupported,
                                     autorefresh)
        for widget in toolbar:
            blayout.addWidget(widget)

        # Options menu
        options_button = create_toolbutton(self,
                                           text=_('Options'),
                                           icon=ima.icon('tooloptions'))
        options_button.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        editor = self.editor
        actions = [
            self.exclude_private_action, self.exclude_uppercase_action,
            self.exclude_capitalized_action, self.exclude_unsupported_action,
            None, editor.truncate_action
        ]
        if is_module_installed('numpy'):
            actions.append(editor.minmax_action)
        add_actions(menu, actions)
        options_button.setMenu(menu)

        blayout.addStretch()
        blayout.addWidget(options_button)
        layout.addLayout(blayout)
        layout.addWidget(self.editor)
        self.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)

        self.sig_option_changed.connect(self.option_changed)
コード例 #39
0
class DirView(QTreeView):
    """Base file/directory tree view"""
    def __init__(self, parent=None):
        super(DirView, self).__init__(parent)
        self.name_filters = None
        self.parent_widget = parent
        self.show_all = None
        self.menu = None
        self.common_actions = None
        self.__expanded_state = None
        self._to_be_loaded = None
        self.fsmodel = None
        self.setup_fs_model()
        self._scrollbar_positions = None
                
    #---- Model
    def setup_fs_model(self):
        """Setup filesystem model"""
        filters = QDir.AllDirs | QDir.Files | QDir.Drives | QDir.NoDotAndDotDot
        self.fsmodel = QFileSystemModel(self)
        self.fsmodel.setFilter(filters)
        self.fsmodel.setNameFilterDisables(False)
        
    def install_model(self):
        """Install filesystem model"""
        self.setModel(self.fsmodel)
        
    def setup_view(self):
        """Setup view"""
        self.install_model()
        self.connect(self.fsmodel, SIGNAL('directoryLoaded(QString)'),
                     lambda: self.resizeColumnToContents(0))
        self.setAnimated(False)
        self.setSortingEnabled(True)
        self.sortByColumn(0, Qt.AscendingOrder)
        
    def set_name_filters(self, name_filters):
        """Set name filters"""
        self.name_filters = name_filters
        self.fsmodel.setNameFilters(name_filters)
        
    def set_show_all(self, state):
        """Toggle 'show all files' state"""
        if state:
            self.fsmodel.setNameFilters([])
        else:
            self.fsmodel.setNameFilters(self.name_filters)
            
    def get_filename(self, index):
        """Return filename associated with *index*"""
        if index:
            return osp.normpath(to_text_string(self.fsmodel.filePath(index)))
        
    def get_index(self, filename):
        """Return index associated with filename"""
        return self.fsmodel.index(filename)
        
    def get_selected_filenames(self):
        """Return selected filenames"""
        if self.selectionMode() == self.ExtendedSelection:
            return [self.get_filename(idx) for idx in self.selectedIndexes()]
        else:
            return [self.get_filename(self.currentIndex())]
            
    def get_dirname(self, index):
        """Return dirname associated with *index*"""
        fname = self.get_filename(index)
        if fname:
            if osp.isdir(fname):
                return fname
            else:
                return osp.dirname(fname)
        
    #---- Tree view widget
    def setup(self, name_filters=['*.py', '*.pyw'], show_all=False):
        """Setup tree widget"""
        self.setup_view()
        
        self.set_name_filters(name_filters)
        self.show_all = show_all
        
        # Setup context menu
        self.menu = QMenu(self)
        self.common_actions = self.setup_common_actions()
        
    #---- Context menu
    def setup_common_actions(self):
        """Setup context menu common actions"""
        # Filters
        filters_action = create_action(self, _("Edit filename filters..."),
                                       None, get_icon('filter.png'),
                                       triggered=self.edit_filter)
        # Show all files
        all_action = create_action(self, _("Show all files"),
                                   toggled=self.toggle_all)
        all_action.setChecked(self.show_all)
        self.toggle_all(self.show_all)
        
        return [filters_action, all_action]
        
    def edit_filter(self):
        """Edit name filters"""
        filters, valid = QInputDialog.getText(self, _('Edit filename filters'),
                                              _('Name filters:'),
                                              QLineEdit.Normal,
                                              ", ".join(self.name_filters))
        if valid:
            filters = [f.strip() for f in to_text_string(filters).split(',')]
            self.parent_widget.sig_option_changed.emit('name_filters', filters)
            self.set_name_filters(filters)
            
    def toggle_all(self, checked):
        """Toggle all files mode"""
        self.parent_widget.sig_option_changed.emit('show_all', checked)
        self.show_all = checked
        self.set_show_all(checked)
        
    def create_file_new_actions(self, fnames):
        """Return actions for submenu 'New...'"""
        if not fnames:
            return []
        new_file_act = create_action(self, _("File..."), icon='filenew.png',
                                     triggered=lambda:
                                     self.new_file(fnames[-1]))
        new_module_act = create_action(self, _("Module..."), icon='py.png',
                                       triggered=lambda:
                                         self.new_module(fnames[-1]))
        new_folder_act = create_action(self, _("Folder..."),
                                       icon='folder_new.png',
                                       triggered=lambda:
                                        self.new_folder(fnames[-1]))
        new_package_act = create_action(self, _("Package..."),
                                        icon=get_icon('package_collapsed.png'),
                                        triggered=lambda:
                                         self.new_package(fnames[-1]))
        return [new_file_act, new_folder_act, None,
                new_module_act, new_package_act]
        
    def create_file_import_actions(self, fnames):
        """Return actions for submenu 'Import...'"""
        return []

    def create_file_manage_actions(self, fnames):
        """Return file management actions"""
        only_files = all([osp.isfile(_fn) for _fn in fnames])
        only_modules = all([osp.splitext(_fn)[1] in ('.py', '.pyw', '.ipy')
                            for _fn in fnames])
        only_notebooks = all([osp.splitext(_fn)[1] == '.ipynb'
                              for _fn in fnames])
        only_valid = all([encoding.is_text_file(_fn) for _fn in fnames])
        run_action = create_action(self, _("Run"), icon="run_small.png",
                                   triggered=self.run)
        edit_action = create_action(self, _("Edit"), icon="edit.png",
                                    triggered=self.clicked)
        move_action = create_action(self, _("Move..."),
                                    icon="move.png",
                                    triggered=self.move)
        delete_action = create_action(self, _("Delete..."),
                                      icon="delete.png",
                                      triggered=self.delete)
        rename_action = create_action(self, _("Rename..."),
                                      icon="rename.png",
                                      triggered=self.rename)
        open_action = create_action(self, _("Open"), triggered=self.open)
        ipynb_convert_action = create_action(self, _("Convert to Python script"),
                                             icon="python.png",
                                             triggered=self.convert)
        
        actions = []
        if only_modules:
            actions.append(run_action)
        if only_valid and only_files:
            actions.append(edit_action)
        else:
            actions.append(open_action)
        actions += [delete_action, rename_action]
        basedir = fixpath(osp.dirname(fnames[0]))
        if all([fixpath(osp.dirname(_fn)) == basedir for _fn in fnames]):
            actions.append(move_action)
        actions += [None]
        if only_notebooks and nbexporter is not None:
            actions.append(ipynb_convert_action)
        
        # VCS support is quite limited for now, so we are enabling the VCS
        # related actions only when a single file/folder is selected:
        dirname = fnames[0] if osp.isdir(fnames[0]) else osp.dirname(fnames[0])
        if len(fnames) == 1 and vcs.is_vcs_repository(dirname):
            vcs_ci = create_action(self, _("Commit"),
                                   icon="vcs_commit.png",
                                   triggered=lambda fnames=[dirname]:
                                   self.vcs_command(fnames, 'commit'))
            vcs_log = create_action(self, _("Browse repository"),
                                    icon="vcs_browse.png",
                                    triggered=lambda fnames=[dirname]:
                                    self.vcs_command(fnames, 'browse'))
            actions += [None, vcs_ci, vcs_log]
        
        return actions

    def create_folder_manage_actions(self, fnames):
        """Return folder management actions"""
        actions = []
        if os.name == 'nt':
            _title = _("Open command prompt here")
        else:
            _title = _("Open terminal here")
        action = create_action(self, _title, icon="cmdprompt.png",
                               triggered=lambda fnames=fnames:
                               self.open_terminal(fnames))
        actions.append(action)
        _title = _("Open Python console here")
        action = create_action(self, _title, icon="python.png",
                               triggered=lambda fnames=fnames:
                               self.open_interpreter(fnames))
        actions.append(action)
        return actions
        
    def create_context_menu_actions(self):
        """Create context menu actions"""
        actions = []
        fnames = self.get_selected_filenames()
        new_actions = self.create_file_new_actions(fnames)
        if len(new_actions) > 1:
            # Creating a submenu only if there is more than one entry
            new_act_menu = QMenu(_('New'), self)
            add_actions(new_act_menu, new_actions)
            actions.append(new_act_menu)
        else:
            actions += new_actions
        import_actions = self.create_file_import_actions(fnames)
        if len(import_actions) > 1:
            # Creating a submenu only if there is more than one entry
            import_act_menu = QMenu(_('Import'), self)
            add_actions(import_act_menu, import_actions)
            actions.append(import_act_menu)
        else:
            actions += import_actions
        if actions:
            actions.append(None)
        if fnames:
            actions += self.create_file_manage_actions(fnames)
        if actions:
            actions.append(None)
        if fnames and all([osp.isdir(_fn) for _fn in fnames]):
            actions += self.create_folder_manage_actions(fnames)
        if actions:
            actions.append(None)
        actions += self.common_actions
        return actions

    def update_menu(self):
        """Update context menu"""
        self.menu.clear()
        add_actions(self.menu, self.create_context_menu_actions())
    
    #---- Events
    def viewportEvent(self, event):
        """Reimplement Qt method"""

        # Prevent Qt from crashing or showing warnings like:
        # "QSortFilterProxyModel: index from wrong model passed to 
        # mapFromSource", probably due to the fact that the file system model 
        # is being built. See Issue 1250.
        #
        # This workaround was inspired by the following KDE bug:
        # https://bugs.kde.org/show_bug.cgi?id=172198
        #
        # Apparently, this is a bug from Qt itself.
        self.executeDelayedItemsLayout()
        
        return QTreeView.viewportEvent(self, event)        
                
    def contextMenuEvent(self, event):
        """Override Qt method"""
        self.update_menu()
        self.menu.popup(event.globalPos())

    def keyPressEvent(self, event):
        """Reimplement Qt method"""
        if event.key() in (Qt.Key_Enter, Qt.Key_Return):
            self.clicked()
        elif event.key() == Qt.Key_F2:
            self.rename()
        elif event.key() == Qt.Key_Delete:
            self.delete()
        else:
            QTreeView.keyPressEvent(self, event)

    def mouseDoubleClickEvent(self, event):
        """Reimplement Qt method"""
        QTreeView.mouseDoubleClickEvent(self, event)
        self.clicked()
        
    def clicked(self):
        """Selected item was double-clicked or enter/return was pressed"""
        fnames = self.get_selected_filenames()
        for fname in fnames:
            if osp.isdir(fname):
                self.directory_clicked(fname)
            else:
                self.open([fname])
                
    def directory_clicked(self, dirname):
        """Directory was just clicked"""
        pass
        
    #---- Drag
    def dragEnterEvent(self, event):
        """Drag and Drop - Enter event"""
        event.setAccepted(event.mimeData().hasFormat("text/plain"))

    def dragMoveEvent(self, event):
        """Drag and Drop - Move event"""
        if (event.mimeData().hasFormat("text/plain")):
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()
            
    def startDrag(self, dropActions):
        """Reimplement Qt Method - handle drag event"""
        data = QMimeData()
        data.setUrls([QUrl(fname) for fname in self.get_selected_filenames()])
        drag = QDrag(self)
        drag.setMimeData(data)
        drag.exec_()
        
    #---- File/Directory actions
    def open(self, fnames=None):
        """Open files with the appropriate application"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        for fname in fnames:
            if osp.isfile(fname) and encoding.is_text_file(fname):
                self.parent_widget.sig_open_file.emit(fname)
            else:
                self.open_outside_spyder([fname])
        
    def open_outside_spyder(self, fnames):
        """Open file outside Spyder with the appropriate application
        If this does not work, opening unknown file in Spyder, as text file"""
        for path in sorted(fnames):
            path = file_uri(path)
            ok = programs.start_file(path)
            if not ok:
                self.parent_widget.emit(SIGNAL("edit(QString)"), path)
                
    def open_terminal(self, fnames):
        """Open terminal"""
        for path in sorted(fnames):
            self.parent_widget.emit(SIGNAL("open_terminal(QString)"), path)
            
    def open_interpreter(self, fnames):
        """Open interpreter"""
        for path in sorted(fnames):
            self.parent_widget.emit(SIGNAL("open_interpreter(QString)"), path)
        
    def run(self, fnames=None):
        """Run Python scripts"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        for fname in fnames:
            self.parent_widget.emit(SIGNAL("run(QString)"), fname)
    
    def remove_tree(self, dirname):
        """Remove whole directory tree
        Reimplemented in project explorer widget"""
        shutil.rmtree(dirname, onerror=misc.onerror)
    
    def delete_file(self, fname, multiple, yes_to_all):
        """Delete file"""
        if multiple:
            buttons = QMessageBox.Yes|QMessageBox.YesAll| \
                      QMessageBox.No|QMessageBox.Cancel
        else:
            buttons = QMessageBox.Yes|QMessageBox.No
        if yes_to_all is None:
            answer = QMessageBox.warning(self, _("Delete"),
                                 _("Do you really want "
                                   "to delete <b>%s</b>?"
                                   ) % osp.basename(fname), buttons)
            if answer == QMessageBox.No:
                return yes_to_all
            elif answer == QMessageBox.Cancel:
                return False
            elif answer == QMessageBox.YesAll:
                yes_to_all = True
        try:
            if osp.isfile(fname):
                misc.remove_file(fname)
                self.parent_widget.emit(SIGNAL("removed(QString)"),
                                        fname)
            else:
                self.remove_tree(fname)
                self.parent_widget.emit(SIGNAL("removed_tree(QString)"),
                                        fname)
            return yes_to_all
        except EnvironmentError as error:
            action_str = _('delete')
            QMessageBox.critical(self, _("Project Explorer"),
                            _("<b>Unable to %s <i>%s</i></b>"
                              "<br><br>Error message:<br>%s"
                              ) % (action_str, fname, to_text_string(error)))
        return False
        
    def delete(self, fnames=None):
        """Delete files"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        multiple = len(fnames) > 1
        yes_to_all = None
        for fname in fnames:
            yes_to_all = self.delete_file(fname, multiple, yes_to_all)
            if yes_to_all is not None and not yes_to_all:
                # Canceled
                return

    def convert_notebook(self, fname):
        """Convert an IPython notebook to a Python script in editor"""
        try: 
            script = nbexporter().from_filename(fname)[0]
        except Exception as e:
            QMessageBox.critical(self, _('Conversion error'), 
                                 _("It was not possible to convert this "
                                 "notebook. The error is:\n\n") + \
                                 to_text_string(e))
            return
        self.parent_widget.sig_new_file.emit(script)
    
    def convert(self, fnames=None):
        """Convert IPython notebooks to Python scripts in editor"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        if not isinstance(fnames, (tuple, list)):
            fnames = [fnames]
        for fname in fnames:
            self.convert_notebook(fname)

    def rename_file(self, fname):
        """Rename file"""
        path, valid = QInputDialog.getText(self, _('Rename'),
                              _('New name:'), QLineEdit.Normal,
                              osp.basename(fname))
        if valid:
            path = osp.join(osp.dirname(fname), to_text_string(path))
            if path == fname:
                return
            if osp.exists(path):
                if QMessageBox.warning(self, _("Rename"),
                         _("Do you really want to rename <b>%s</b> and "
                           "overwrite the existing file <b>%s</b>?"
                           ) % (osp.basename(fname), osp.basename(path)),
                         QMessageBox.Yes|QMessageBox.No) == QMessageBox.No:
                    return
            try:
                misc.rename_file(fname, path)
                self.parent_widget.emit( \
                     SIGNAL("renamed(QString,QString)"), fname, path)
                return path
            except EnvironmentError as error:
                QMessageBox.critical(self, _("Rename"),
                            _("<b>Unable to rename file <i>%s</i></b>"
                              "<br><br>Error message:<br>%s"
                              ) % (osp.basename(fname), to_text_string(error)))
    
    def rename(self, fnames=None):
        """Rename files"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        if not isinstance(fnames, (tuple, list)):
            fnames = [fnames]
        for fname in fnames:
            self.rename_file(fname)
        
    def move(self, fnames=None):
        """Move files/directories"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        orig = fixpath(osp.dirname(fnames[0]))
        while True:
            self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), False)
            folder = getexistingdirectory(self, _("Select directory"), orig)
            self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), True)
            if folder:
                folder = fixpath(folder)
                if folder != orig:
                    break
            else:
                return
        for fname in fnames:
            basename = osp.basename(fname)
            try:
                misc.move_file(fname, osp.join(folder, basename))
            except EnvironmentError as error:
                QMessageBox.critical(self, _("Error"),
                                     _("<b>Unable to move <i>%s</i></b>"
                                       "<br><br>Error message:<br>%s"
                                       ) % (basename, to_text_string(error)))
        
    def create_new_folder(self, current_path, title, subtitle, is_package):
        """Create new folder"""
        if current_path is None:
            current_path = ''
        if osp.isfile(current_path):
            current_path = osp.dirname(current_path)
        name, valid = QInputDialog.getText(self, title, subtitle,
                                           QLineEdit.Normal, "")
        if valid:
            dirname = osp.join(current_path, to_text_string(name))
            try:
                os.mkdir(dirname)
            except EnvironmentError as error:
                QMessageBox.critical(self, title,
                                     _("<b>Unable "
                                       "to create folder <i>%s</i></b>"
                                       "<br><br>Error message:<br>%s"
                                       ) % (dirname, to_text_string(error)))
            finally:
                if is_package:
                    fname = osp.join(dirname, '__init__.py')
                    try:
                        with open(fname, 'wb') as f:
                            f.write(to_binary_string('#'))
                        return dirname
                    except EnvironmentError as error:
                        QMessageBox.critical(self, title,
                                             _("<b>Unable "
                                               "to create file <i>%s</i></b>"
                                               "<br><br>Error message:<br>%s"
                                               ) % (fname,
                                                    to_text_string(error)))

    def new_folder(self, basedir):
        """New folder"""
        title = _('New folder')
        subtitle = _('Folder name:')
        self.create_new_folder(basedir, title, subtitle, is_package=False)
    
    def new_package(self, basedir):
        """New package"""
        title = _('New package')
        subtitle = _('Package name:')
        self.create_new_folder(basedir, title, subtitle, is_package=True)
    
    def create_new_file(self, current_path, title, filters, create_func):
        """Create new file
        Returns True if successful"""
        if current_path is None:
            current_path = ''
        if osp.isfile(current_path):
            current_path = osp.dirname(current_path)
        self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), False)
        fname, _selfilter = getsavefilename(self, title, current_path, filters)
        self.parent_widget.emit(SIGNAL('redirect_stdio(bool)'), True)
        if fname:
            try:
                create_func(fname)
                return fname
            except EnvironmentError as error:
                QMessageBox.critical(self, _("New file"),
                                     _("<b>Unable to create file <i>%s</i>"
                                       "</b><br><br>Error message:<br>%s"
                                       ) % (fname, to_text_string(error)))

    def new_file(self, basedir):
        """New file"""
        title = _("New file")
        filters = _("All files")+" (*)"
        def create_func(fname):
            """File creation callback"""
            if osp.splitext(fname)[1] in ('.py', '.pyw', '.ipy'):
                create_script(fname)
            else:
                with open(fname, 'wb') as f:
                    f.write(to_binary_string(''))
        fname = self.create_new_file(basedir, title, filters, create_func)
        if fname is not None:
            self.open([fname])
    
    def new_module(self, basedir):
        """New module"""
        title = _("New module")
        filters = _("Python scripts")+" (*.py *.pyw *.ipy)"
        create_func = lambda fname: self.parent_widget.emit( \
                                     SIGNAL("create_module(QString)"), fname)
        self.create_new_file(basedir, title, filters, create_func)
        
    #----- VCS actions
    def vcs_command(self, fnames, action):
        """VCS action (commit, browse)"""
        try:
            for path in sorted(fnames):
                vcs.run_vcs_tool(path, action)
        except vcs.ActionToolNotFound as error:
            msg = _("For %s support, please install one of the<br/> "
                    "following tools:<br/><br/>  %s")\
                        % (error.vcsname, ', '.join(error.tools))
            QMessageBox.critical(self, _("Error"),
                _("""<b>Unable to find external program.</b><br><br>%s""")
                    % to_text_string(msg))
        
    #----- Settings
    def get_scrollbar_position(self):
        """Return scrollbar positions"""
        return (self.horizontalScrollBar().value(),
                self.verticalScrollBar().value())
        
    def set_scrollbar_position(self, position):
        """Set scrollbar positions"""
        # Scrollbars will be restored after the expanded state
        self._scrollbar_positions = position
        if self._to_be_loaded is not None and len(self._to_be_loaded) == 0:
            self.restore_scrollbar_positions()
            
    def restore_scrollbar_positions(self):
        """Restore scrollbar positions once tree is loaded"""
        hor, ver = self._scrollbar_positions
        self.horizontalScrollBar().setValue(hor)
        self.verticalScrollBar().setValue(ver)
        
    def get_expanded_state(self):
        """Return expanded state"""
        self.save_expanded_state()
        return self.__expanded_state
    
    def set_expanded_state(self, state):
        """Set expanded state"""
        self.__expanded_state = state
        self.restore_expanded_state()
    
    def save_expanded_state(self):
        """Save all items expanded state"""
        model = self.model()
        # If model is not installed, 'model' will be None: this happens when
        # using the Project Explorer without having selected a workspace yet
        if model is not None:
            self.__expanded_state = []
            for idx in model.persistentIndexList():
                if self.isExpanded(idx):
                    self.__expanded_state.append(self.get_filename(idx))

    def restore_directory_state(self, fname):
        """Restore directory expanded state"""
        root = osp.normpath(to_text_string(fname))
        if not osp.exists(root):
            # Directory has been (re)moved outside Spyder
            return
        for basename in os.listdir(root):
            path = osp.normpath(osp.join(root, basename))
            if osp.isdir(path) and path in self.__expanded_state:
                self.__expanded_state.pop(self.__expanded_state.index(path))
                if self._to_be_loaded is None:
                    self._to_be_loaded = []
                self._to_be_loaded.append(path)
                self.setExpanded(self.get_index(path), True)
        if not self.__expanded_state:
            self.disconnect(self.fsmodel, SIGNAL('directoryLoaded(QString)'),
                            self.restore_directory_state)
                
    def follow_directories_loaded(self, fname):
        """Follow directories loaded during startup"""
        if self._to_be_loaded is None:
            return
        path = osp.normpath(to_text_string(fname))
        if path in self._to_be_loaded:
            self._to_be_loaded.remove(path)
        if self._to_be_loaded is not None and len(self._to_be_loaded) == 0:
            self.disconnect(self.fsmodel, SIGNAL('directoryLoaded(QString)'),
                            self.follow_directories_loaded)
            if self._scrollbar_positions is not None:
                # The tree view need some time to render branches:
                QTimer.singleShot(50, self.restore_scrollbar_positions)

    def restore_expanded_state(self):
        """Restore all items expanded state"""
        if self.__expanded_state is not None:
            # In the old project explorer, the expanded state was a dictionnary:
            if isinstance(self.__expanded_state, list):
                self.connect(self.fsmodel, SIGNAL('directoryLoaded(QString)'),
                             self.restore_directory_state)
                self.connect(self.fsmodel, SIGNAL('directoryLoaded(QString)'),
                             self.follow_directories_loaded)
コード例 #40
0
ファイル: importwizard.py プロジェクト: jromang/retina-old
class PreviewTable(QTableView):
    """Import wizard preview widget"""
    def __init__(self, parent):
        QTableView.__init__(self, parent)
        self._model = None

        # Setting up actions
        self.date_dayfirst_action = create_action(self, "dayfirst",
            triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=True))
        self.date_monthfirst_action = create_action(self,"monthfirst",
            triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=False))
        self.perc_action = create_action(self, "perc",
            triggered=ft_partial(self.parse_to_type, atype="perc"))
        self.acc_action = create_action(self, "account",
            triggered=ft_partial(self.parse_to_type, atype="account"))
        self.str_action = create_action(self, "unicode",
            triggered=ft_partial(self.parse_to_type, atype="unicode"))
        self.int_action = create_action(self, "int",
            triggered=ft_partial(self.parse_to_type, atype="int"))
        self.float_action = create_action(self,"float",
            triggered=ft_partial(self.parse_to_type, atype="float"))
        
        # Setting up menus
        self.date_menu = QMenu()
        self.date_menu.setTitle("Date")
        add_actions( self.date_menu, (self.date_dayfirst_action,
                                      self.date_monthfirst_action))
        self.parse_menu = QMenu(self)
        self.parse_menu.addMenu(self.date_menu)
        add_actions( self.parse_menu, (self.perc_action, self.acc_action))
        self.parse_menu.setTitle("String to")
        self.opt_menu = QMenu(self)
        self.opt_menu.addMenu(self.parse_menu)
        add_actions( self.opt_menu, (self.str_action, self.int_action,
                                     self.float_action))

    def _shape_text(self, text, colsep=u"\t", rowsep=u"\n", transpose=False,
                    skiprows=0, comments='#'):
        """Decode the shape of the given text"""
        assert colsep != rowsep
        out = []
        text_rows = map(None, text.split(rowsep))[skiprows:]
        for row in text_rows:
            stripped = unicode(row).strip()
            if len(stripped) == 0 or stripped.startswith(comments):
                continue
            line = unicode(row).split(colsep)
            line = map(lambda x: try_to_parse(unicode(x)), line)
            out.append(line)
        if transpose:
            return [[r[col] for r in out] for col in range(len(out[0]))]
        return out
    
    def get_data(self):
        """Return model data"""
        if self._model is None:
            return None
        return self._model.get_data()

    def process_data(self, text, colsep=u"\t", rowsep=u"\n", transpose=False,
                     skiprows=0, comments='#'):
        """Put data into table model"""
        data = self._shape_text(text, colsep, rowsep, transpose, skiprows,
                                comments)
        self._model = PreviewTableModel(data)
        self.setModel(self._model)

    def parse_to_type(self,**kwargs):
        """Parse to a given type"""
        indexes = self.selectedIndexes()
        if not indexes: return
        for index in indexes:
            self.model().parse_data_type(index, **kwargs)
    
    def contextMenuEvent(self, event):
        """Reimplement Qt method"""
        self.opt_menu.popup(event.globalPos())
        event.accept()
コード例 #41
0
class BaseTabs(QTabWidget):
    """TabWidget with context menu and corner widgets"""
    sig_close_tab = Signal(int)

    def __init__(self,
                 parent,
                 actions=None,
                 menu=None,
                 corner_widgets=None,
                 menu_use_tooltips=False):
        QTabWidget.__init__(self, parent)
        self.setUsesScrollButtons(True)

        # To style tabs on Mac
        if sys.platform == 'darwin':
            self.setObjectName('plugin-tab')

        self.corner_widgets = {}
        self.menu_use_tooltips = menu_use_tooltips

        if menu is None:
            self.menu = QMenu(self)
            if actions:
                add_actions(self.menu, actions)
        else:
            self.menu = menu

        # Corner widgets
        if corner_widgets is None:
            corner_widgets = {}
        corner_widgets.setdefault(Qt.TopLeftCorner, [])
        corner_widgets.setdefault(Qt.TopRightCorner, [])
        self.browse_button = create_toolbutton(self,
                                               icon=ima.icon('browse_tab'),
                                               tip=_("Browse tabs"))
        self.browse_tabs_menu = QMenu(self)
        self.browse_button.setMenu(self.browse_tabs_menu)
        self.browse_button.setPopupMode(self.browse_button.InstantPopup)
        self.browse_tabs_menu.aboutToShow.connect(self.update_browse_tabs_menu)
        corner_widgets[Qt.TopLeftCorner] += [self.browse_button]

        self.set_corner_widgets(corner_widgets)

    def update_browse_tabs_menu(self):
        """Update browse tabs menu"""
        self.browse_tabs_menu.clear()
        names = []
        dirnames = []
        for index in range(self.count()):
            if self.menu_use_tooltips:
                text = to_text_string(self.tabToolTip(index))
            else:
                text = to_text_string(self.tabText(index))
            names.append(text)
            if osp.isfile(text):
                # Testing if tab names are filenames
                dirnames.append(osp.dirname(text))
        offset = None

        # If tab names are all filenames, removing common path:
        if len(names) == len(dirnames):
            common = get_common_path(dirnames)
            if common is None:
                offset = None
            else:
                offset = len(common) + 1
                if offset <= 3:
                    # Common path is not a path but a drive letter...
                    offset = None

        for index, text in enumerate(names):
            tab_action = create_action(
                self,
                text[offset:],
                icon=self.tabIcon(index),
                toggled=lambda state, index=index: self.setCurrentIndex(index),
                tip=self.tabToolTip(index))
            tab_action.setChecked(index == self.currentIndex())
            self.browse_tabs_menu.addAction(tab_action)

    def set_corner_widgets(self, corner_widgets):
        """
        Set tabs corner widgets
        corner_widgets: dictionary of (corner, widgets)
        corner: Qt.TopLeftCorner or Qt.TopRightCorner
        widgets: list of widgets (may contains integers to add spacings)
        """
        assert isinstance(corner_widgets, dict)
        assert all(key in (Qt.TopLeftCorner, Qt.TopRightCorner)
                   for key in corner_widgets)
        self.corner_widgets.update(corner_widgets)
        for corner, widgets in list(self.corner_widgets.items()):
            cwidget = QWidget()
            cwidget.hide()
            prev_widget = self.cornerWidget(corner)
            if prev_widget:
                prev_widget.close()
            self.setCornerWidget(cwidget, corner)
            clayout = QHBoxLayout()
            clayout.setContentsMargins(0, 0, 0, 0)
            for widget in widgets:
                if isinstance(widget, int):
                    clayout.addSpacing(widget)
                else:
                    clayout.addWidget(widget)
            cwidget.setLayout(clayout)
            cwidget.show()

    def add_corner_widgets(self, widgets, corner=Qt.TopRightCorner):
        self.set_corner_widgets(
            {corner: self.corner_widgets.get(corner, []) + widgets})

    def contextMenuEvent(self, event):
        """Override Qt method"""
        if self.menu:
            self.menu.popup(event.globalPos())

    def mousePressEvent(self, event):
        """Override Qt method"""
        if event.button() == Qt.MidButton:
            index = self.tabBar().tabAt(event.pos())
            if index >= 0:
                self.sig_close_tab.emit(index)
                event.accept()
                return
        QTabWidget.mousePressEvent(self, event)

    def keyPressEvent(self, event):
        """Override Qt method"""
        ctrl = event.modifiers() & Qt.ControlModifier
        key = event.key()
        handled = False
        if ctrl and self.count() > 0:
            index = self.currentIndex()
            if key == Qt.Key_PageUp:
                if index > 0:
                    self.setCurrentIndex(index - 1)
                else:
                    self.setCurrentIndex(self.count() - 1)
                handled = True
            elif key == Qt.Key_PageDown:
                if index < self.count() - 1:
                    self.setCurrentIndex(index + 1)
                else:
                    self.setCurrentIndex(0)
                handled = True
        if not handled:
            QTabWidget.keyPressEvent(self, event)

    def set_close_function(self, func):
        """Setting Tabs close function
        None -> tabs are not closable"""
        state = func is not None
        if state:
            self.sig_close_tab.connect(func)
        try:
            # Assuming Qt >= 4.5
            QTabWidget.setTabsClosable(self, state)
            self.tabCloseRequested.connect(func)
        except AttributeError:
            # Workaround for Qt < 4.5
            close_button = create_toolbutton(self,
                                             triggered=func,
                                             icon=ima.icon('fileclose'),
                                             tip=_("Close current tab"))
            self.setCornerWidget(close_button if state else None)
コード例 #42
0
ファイル: importwizard.py プロジェクト: gyenney/Tools
class PreviewTable(QTableView):
    """Import wizard preview widget"""

    def __init__(self, parent):
        QTableView.__init__(self, parent)
        self._model = None

        # Setting up actions
        self.date_dayfirst_action = create_action(
            self, "dayfirst", triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=True)
        )
        self.date_monthfirst_action = create_action(
            self, "monthfirst", triggered=ft_partial(self.parse_to_type, atype="date", dayfirst=False)
        )
        self.perc_action = create_action(self, "perc", triggered=ft_partial(self.parse_to_type, atype="perc"))
        self.acc_action = create_action(self, "account", triggered=ft_partial(self.parse_to_type, atype="account"))
        self.str_action = create_action(self, "unicode", triggered=ft_partial(self.parse_to_type, atype="unicode"))
        self.int_action = create_action(self, "int", triggered=ft_partial(self.parse_to_type, atype="int"))
        self.float_action = create_action(self, "float", triggered=ft_partial(self.parse_to_type, atype="float"))

        # Setting up menus
        self.date_menu = QMenu()
        self.date_menu.setTitle("Date")
        add_actions(self.date_menu, (self.date_dayfirst_action, self.date_monthfirst_action))
        self.parse_menu = QMenu(self)
        self.parse_menu.addMenu(self.date_menu)
        add_actions(self.parse_menu, (self.perc_action, self.acc_action))
        self.parse_menu.setTitle("String to")
        self.opt_menu = QMenu(self)
        self.opt_menu.addMenu(self.parse_menu)
        add_actions(self.opt_menu, (self.str_action, self.int_action, self.float_action))

    def _shape_text(self, text, colsep=u"\t", rowsep=u"\n", transpose=False, skiprows=0, comments="#"):
        """Decode the shape of the given text"""
        assert colsep != rowsep
        out = []
        text_rows = text.split(rowsep)[skiprows:]
        for row in text_rows:
            stripped = to_text_string(row).strip()
            if len(stripped) == 0 or stripped.startswith(comments):
                continue
            line = to_text_string(row).split(colsep)
            line = [try_to_parse(to_text_string(x)) for x in line]
            out.append(line)
        # Replace missing elements with np.nan's or None's
        if programs.is_module_installed("numpy"):
            from numpy import nan

            out = list(zip_longest(*out, fillvalue=nan))
        else:
            out = list(zip_longest(*out, fillvalue=None))
        # Tranpose the last result to get the expected one
        out = [[r[col] for r in out] for col in range(len(out[0]))]
        if transpose:
            return [[r[col] for r in out] for col in range(len(out[0]))]
        return out

    def get_data(self):
        """Return model data"""
        if self._model is None:
            return None
        return self._model.get_data()

    def process_data(self, text, colsep=u"\t", rowsep=u"\n", transpose=False, skiprows=0, comments="#"):
        """Put data into table model"""
        data = self._shape_text(text, colsep, rowsep, transpose, skiprows, comments)
        self._model = PreviewTableModel(data)
        self.setModel(self._model)

    @Slot()
    def parse_to_type(self, **kwargs):
        """Parse to a given type"""
        indexes = self.selectedIndexes()
        if not indexes:
            return
        for index in indexes:
            self.model().parse_data_type(index, **kwargs)

    def contextMenuEvent(self, event):
        """Reimplement Qt method"""
        self.opt_menu.popup(event.globalPos())
        event.accept()
コード例 #43
0
ファイル: shell.py プロジェクト: jromang/retina-old
class ShellBaseWidget(ConsoleBaseWidget):
    """
    Shell base widget
    """
    INITHISTORY = None
    SEPARATOR = None
    
    def __init__(self, parent, history_filename, debug=False, profile=False):
        """
        parent : specifies the parent widget
        """
        ConsoleBaseWidget.__init__(self, parent)
                
        # Prompt position: tuple (line, index)
        self.current_prompt_pos = None
        self.new_input_line = True
        
        # History
        self.histidx = None
        self.hist_wholeline = False
        assert isinstance(history_filename, (str, unicode))
        self.history_filename = history_filename
        self.history = self.load_history()
        
        # Session
        self.historylog_filename = CONF.get('main', 'historylog_filename',
                                            get_conf_path('history.log'))
        
        # Context menu
        self.menu = None
        self.setup_context_menu()

        # Debug mode
        self.debug = debug

        # Simple profiling test
        self.profile = profile
        
        # Buffer to increase performance of write/flush operations
        self.__buffer = []
        self.__timestamp = 0.0
        self.__flushtimer = QTimer(self)
        self.__flushtimer.setSingleShot(True)
        self.connect(self.__flushtimer, SIGNAL('timeout()'), self.flush)

        # Give focus to widget
        self.setFocus()
                
        # Calltips
        calltip_size = CONF.get('shell_appearance', 'calltips/size')
        calltip_font = get_font('shell_appearance', 'calltips')
        self.setup_calltips(calltip_size, calltip_font)
        
        # Completion
        completion_size = CONF.get('shell_appearance', 'completion/size')
        completion_font = get_font('shell_appearance', 'completion')
        self.completion_widget.setup_appearance(completion_size,
                                                completion_font)
        # Cursor width
        self.setCursorWidth( CONF.get('shell_appearance', 'cursor/width') )
        
    def toggle_wrap_mode(self, enable):
        """Enable/disable wrap mode"""
        self.set_wrap_mode('character' if enable else None)

    def set_font(self, font):
        """Set shell styles font"""
        self.set_pythonshell_font(font)
        cursor = self.textCursor()
        cursor.select(QTextCursor.Document)
        charformat = QTextCharFormat()
        charformat.setFontFamily(font.family())
        charformat.setFontPointSize(font.pointSize())
        cursor.mergeCharFormat(charformat)


    #------ Context menu
    def setup_context_menu(self):
        """Setup shell context menu"""
        self.menu = QMenu(self)
        self.cut_action = create_action(self, _("Cut"),
                                        shortcut=keybinding('Cut'),
                                        icon=get_icon('editcut.png'),
                                        triggered=self.cut)
        self.copy_action = create_action(self, _("Copy"),
                                         shortcut=keybinding('Copy'),
                                         icon=get_icon('editcopy.png'),
                                         triggered=self.copy)
        paste_action = create_action(self, _("Paste"),
                                     shortcut=keybinding('Paste'),
                                     icon=get_icon('editpaste.png'),
                                     triggered=self.paste)
        save_action = create_action(self, _("Save history log..."),
                                    icon=get_icon('filesave.png'),
                                    tip=_("Save current history log (i.e. all "
                                          "inputs and outputs) in a text file"),
                                    triggered=self.save_historylog)
        self.delete_action = create_action(self, _("Delete"),
                                    shortcut=keybinding('Delete'),
                                    icon=get_icon('editdelete.png'),
                                    triggered=self.delete)
        selectall_action = create_action(self, _("Select All"),
                                    shortcut=keybinding('SelectAll'),
                                    icon=get_icon('selectall.png'),
                                    triggered=self.selectAll)
        add_actions(self.menu, (self.cut_action, self.copy_action,
                                paste_action, self.delete_action, None,
                                selectall_action, None, save_action) )
          
    def contextMenuEvent(self, event):
        """Reimplement Qt method"""
        state = self.has_selected_text()
        self.copy_action.setEnabled(state)
        self.cut_action.setEnabled(state)
        self.delete_action.setEnabled(state)
        self.menu.popup(event.globalPos())
        event.accept()        
        
        
    #------ Input buffer
    def get_current_line_to_cursor(self):
        return self.get_text(self.current_prompt_pos, 'cursor')
    
    def get_current_line_from_cursor(self):
        return self.get_text('cursor', 'eof')
    
    def _select_input(self):
        """Select current line (without selecting console prompt)"""
        line, index = self.get_position('eof')
        if self.current_prompt_pos is None:
            pline, pindex = line, index
        else:
            pline, pindex = self.current_prompt_pos
        self.setSelection(pline, pindex, line, index)
            
    def clear_line(self):
        """Clear current line (without clearing console prompt)"""
        if self.current_prompt_pos is not None:
            self.remove_text(self.current_prompt_pos, 'eof')
        
    def clear_terminal(self):
        """
        Clear terminal window
        Child classes reimplement this method to write prompt
        """
        self.clear()

    # The buffer being edited
    def _set_input_buffer(self, text):
        """Set input buffer"""
        if self.current_prompt_pos is not None:
            self.replace_text(self.current_prompt_pos, 'eol', text)
        else:
            self.insert(text)
        self.set_cursor_position('eof')

    def _get_input_buffer(self):
        """Return input buffer"""
        input_buffer = ''
        if self.current_prompt_pos is not None:
            input_buffer = self.get_text(self.current_prompt_pos, 'eol')
            input_buffer = input_buffer.replace(os.linesep, '\n')
        return input_buffer

    input_buffer = Property("QString", _get_input_buffer, _set_input_buffer)
        
        
    #------ Prompt
    def new_prompt(self, prompt):
        """
        Print a new prompt and save its (line, index) position
        """
        self.write(prompt, prompt=True)
        # now we update our cursor giving end of prompt
        self.current_prompt_pos = self.get_position('cursor')
        self.ensureCursorVisible()
        self.new_input_line = False
        
    def check_selection(self):
        """
        Check if selected text is r/w,
        otherwise remove read-only parts of selection
        """
        if self.current_prompt_pos is None:
            self.set_cursor_position('eof')
        else:
            self.truncate_selection(self.current_prompt_pos)
        
        
    #------ Copy / Keyboard interrupt
    def copy(self):
        """Copy text to clipboard... or keyboard interrupt"""
        if self.has_selected_text():
            ConsoleBaseWidget.copy(self)
        else:
            self.emit(SIGNAL("keyboard_interrupt()"))
            
    def cut(self):
        """Cut text"""
        self.check_selection()
        if self.has_selected_text():
            ConsoleBaseWidget.cut(self)

    def delete(self):
        """Remove selected text"""
        self.check_selection()
        if self.has_selected_text():
            ConsoleBaseWidget.remove_selected_text(self)
        
    def save_historylog(self):
        """Save current history log (all text in console)"""
        title = _("Save history log")
        self.emit(SIGNAL('redirect_stdio(bool)'), False)
        filename, _selfilter = getsavefilename(self, title,
                    self.historylog_filename, "%s (*.log)" % _("History logs"))
        self.emit(SIGNAL('redirect_stdio(bool)'), True)
        if filename:
            filename = osp.normpath(filename)
            try:
                encoding.write(unicode(self.get_text_with_eol()), filename)
                self.historylog_filename = filename
                CONF.set('main', 'historylog_filename', filename)
            except EnvironmentError, error:
                QMessageBox.critical(self, title,
                                     _("<b>Unable to save file '%s'</b>"
                                       "<br><br>Error message:<br>%s"
                                       ) % (osp.basename(filename),
                                            unicode(error)))
コード例 #44
0
class PreviewTable(QTableView):
    """Import wizard preview widget"""
    def __init__(self, parent):
        QTableView.__init__(self, parent)
        self._model = None

        # Setting up actions
        self.date_dayfirst_action = create_action(self,
                                                  "dayfirst",
                                                  triggered=ft_partial(
                                                      self.parse_to_type,
                                                      atype="date",
                                                      dayfirst=True))
        self.date_monthfirst_action = create_action(self,
                                                    "monthfirst",
                                                    triggered=ft_partial(
                                                        self.parse_to_type,
                                                        atype="date",
                                                        dayfirst=False))
        self.perc_action = create_action(self,
                                         "perc",
                                         triggered=ft_partial(
                                             self.parse_to_type, atype="perc"))
        self.acc_action = create_action(self,
                                        "account",
                                        triggered=ft_partial(
                                            self.parse_to_type,
                                            atype="account"))
        self.str_action = create_action(self,
                                        "unicode",
                                        triggered=ft_partial(
                                            self.parse_to_type,
                                            atype="unicode"))
        self.int_action = create_action(self,
                                        "int",
                                        triggered=ft_partial(
                                            self.parse_to_type, atype="int"))
        self.float_action = create_action(self,
                                          "float",
                                          triggered=ft_partial(
                                              self.parse_to_type,
                                              atype="float"))

        # Setting up menus
        self.date_menu = QMenu()
        self.date_menu.setTitle("Date")
        add_actions(self.date_menu,
                    (self.date_dayfirst_action, self.date_monthfirst_action))
        self.parse_menu = QMenu(self)
        self.parse_menu.addMenu(self.date_menu)
        add_actions(self.parse_menu, (self.perc_action, self.acc_action))
        self.parse_menu.setTitle("String to")
        self.opt_menu = QMenu(self)
        self.opt_menu.addMenu(self.parse_menu)
        add_actions(self.opt_menu,
                    (self.str_action, self.int_action, self.float_action))

    def _shape_text(self,
                    text,
                    colsep=u("\t"),
                    rowsep=u("\n"),
                    transpose=False,
                    skiprows=0,
                    comments='#'):
        """Decode the shape of the given text"""
        assert colsep != rowsep
        out = []
        text_rows = text.split(rowsep)[skiprows:]
        for row in text_rows:
            stripped = to_text_string(row).strip()
            if len(stripped) == 0 or stripped.startswith(comments):
                continue
            line = to_text_string(row).split(colsep)
            line = [try_to_parse(to_text_string(x)) for x in line]
            out.append(line)
        # Replace missing elements with np.nan's or None's
        if programs.is_module_installed('numpy'):
            from numpy import nan
            out = list(zip_longest(*out, fillvalue=nan))
        else:
            out = list(zip_longest(*out, fillvalue=None))
        # Tranpose the last result to get the expected one
        out = [[r[col] for r in out] for col in range(len(out[0]))]
        if transpose:
            return [[r[col] for r in out] for col in range(len(out[0]))]
        return out

    def get_data(self):
        """Return model data"""
        if self._model is None:
            return None
        return self._model.get_data()

    def process_data(self,
                     text,
                     colsep=u("\t"),
                     rowsep=u("\n"),
                     transpose=False,
                     skiprows=0,
                     comments='#'):
        """Put data into table model"""
        data = self._shape_text(text, colsep, rowsep, transpose, skiprows,
                                comments)
        self._model = PreviewTableModel(data)
        self.setModel(self._model)

    @Slot()
    def parse_to_type(self, **kwargs):
        """Parse to a given type"""
        indexes = self.selectedIndexes()
        if not indexes: return
        for index in indexes:
            self.model().parse_data_type(index, **kwargs)

    def contextMenuEvent(self, event):
        """Reimplement Qt method"""
        self.opt_menu.popup(event.globalPos())
        event.accept()
コード例 #45
0
class BreakpointTableView(QTableView):
    edit_goto = Signal(str, int, str)
    clear_breakpoint = Signal(str, int)
    clear_all_breakpoints = Signal()
    set_or_edit_conditional_breakpoint = Signal()
    
    def __init__(self, parent, data):
        QTableView.__init__(self, parent)
        self.model = BreakpointTableModel(self, data)
        self.setModel(self.model)
        self.delegate = BreakpointDelegate(self)
        self.setItemDelegate(self.delegate)

        self.setup_table()
        
    def setup_table(self):
        """Setup table"""
        self.horizontalHeader().setStretchLastSection(True)
        self.adjust_columns()
        self.columnAt(0)
        # Sorting columns
        self.setSortingEnabled(False)
        self.sortByColumn(0, Qt.DescendingOrder)
    
    def adjust_columns(self):
        """Resize three first columns to contents"""
        for col in range(3):
            self.resizeColumnToContents(col)    
    
    def mouseDoubleClickEvent(self, event):
        """Reimplement Qt method"""
        index_clicked = self.indexAt(event.pos())
        if self.model.breakpoints:
            filename = self.model.breakpoints[index_clicked.row()][0]
            line_number_str = self.model.breakpoints[index_clicked.row()][1]
            self.edit_goto.emit(filename, int(line_number_str), '')
        if index_clicked.column()==2:
            self.set_or_edit_conditional_breakpoint.emit()
                           
    def contextMenuEvent(self, event):
        index_clicked = self.indexAt(event.pos())
        actions = []
        self.popup_menu = QMenu(self)
        clear_all_breakpoints_action = create_action(self, 
            _("Clear breakpoints in all files"),
            triggered=lambda: self.clear_all_breakpoints.emit())
        actions.append(clear_all_breakpoints_action)
        if self.model.breakpoints:
            filename = self.model.breakpoints[index_clicked.row()][0]
            lineno = int(self.model.breakpoints[index_clicked.row()][1])
            # QAction.triggered works differently for PySide and PyQt
            if not API == 'pyside':
                clear_slot = lambda _checked, filename=filename, lineno=lineno: \
                    self.clear_breakpoint.emit(filename, lineno)
                edit_slot = lambda _checked, filename=filename, lineno=lineno: \
                    (self.edit_goto.emit(filename, lineno, ''),
                     self.set_or_edit_conditional_breakpoint.emit())
            else:
                clear_slot = lambda filename=filename, lineno=lineno: \
                    self.clear_breakpoint.emit(filename, lineno)
                edit_slot = lambda filename=filename, lineno=lineno: \
                    (self.edit_goto.emit(filename, lineno, ''),
                     self.set_or_edit_conditional_breakpoint.emit())

            clear_breakpoint_action = create_action(self,
                    _("Clear this breakpoint"),
                    triggered=clear_slot)
            actions.insert(0,clear_breakpoint_action)

            edit_breakpoint_action = create_action(self,
                    _("Edit this breakpoint"),
                    triggered=edit_slot)
            actions.append(edit_breakpoint_action)
        add_actions(self.popup_menu, actions)        
        self.popup_menu.popup(event.globalPos())
        event.accept()
コード例 #46
0
    def setup_toolbar(self, exclude_private, exclude_uppercase,
                      exclude_capitalized, exclude_unsupported, autorefresh):
        """Setup toolbar"""
        self.setup_in_progress = True                          
                          
        toolbar = []

        refresh_button = create_toolbutton(self, text=_('Refresh'),
                                           icon=ima.icon('reload'),
                                           triggered=self.refresh_table)
        self.auto_refresh_button = create_toolbutton(self,
                                           text=_('Refresh periodically'),
                                           icon=ima.icon('auto_reload'),
                                           toggled=self.toggle_auto_refresh)
        self.auto_refresh_button.setChecked(autorefresh)
        load_button = create_toolbutton(self, text=_('Import data'),
                                        icon=ima.icon('fileimport'),
                                        triggered=self.import_data)
        self.save_button = create_toolbutton(self, text=_("Save data"),
                            icon=ima.icon('filesave'),
                            triggered=lambda: self.save_data(self.filename))
        self.save_button.setEnabled(False)
        save_as_button = create_toolbutton(self,
                                           text=_("Save data as..."),
                                           icon=ima.icon('filesaveas'),
                                           triggered=self.save_data)
        toolbar += [refresh_button, self.auto_refresh_button, load_button,
                    self.save_button, save_as_button]
        
        self.exclude_private_action = create_action(self,
                _("Exclude private references"),
                tip=_("Exclude references which name starts"
                            " with an underscore"),
                toggled=lambda state:
                self.sig_option_changed.emit('exclude_private', state))
        self.exclude_private_action.setChecked(exclude_private)
        
        self.exclude_uppercase_action = create_action(self,
                _("Exclude all-uppercase references"),
                tip=_("Exclude references which name is uppercase"),
                toggled=lambda state:
                self.sig_option_changed.emit('exclude_uppercase', state))
        self.exclude_uppercase_action.setChecked(exclude_uppercase)
        
        self.exclude_capitalized_action = create_action(self,
                _("Exclude capitalized references"),
                tip=_("Exclude references which name starts with an "
                      "uppercase character"),
                toggled=lambda state:
                self.sig_option_changed.emit('exclude_capitalized', state))
        self.exclude_capitalized_action.setChecked(exclude_capitalized)
        
        self.exclude_unsupported_action = create_action(self,
                _("Exclude unsupported data types"),
                tip=_("Exclude references to unsupported data types"
                            " (i.e. which won't be handled/saved correctly)"),
                toggled=lambda state:
                self.sig_option_changed.emit('exclude_unsupported', state))
        self.exclude_unsupported_action.setChecked(exclude_unsupported)
        
        options_button = create_toolbutton(self, text=_('Options'),
                                           icon=ima.icon('tooloptions'))
        toolbar.append(options_button)
        options_button.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        editor = self.editor
        actions = [self.exclude_private_action, self.exclude_uppercase_action,
                   self.exclude_capitalized_action,
                   self.exclude_unsupported_action, None,
                   editor.truncate_action]
        if is_module_installed('numpy'):
            actions.append(editor.minmax_action)
        add_actions(menu, actions)
        options_button.setMenu(menu)
        
        self.setup_in_progress = False
        
        return toolbar
コード例 #47
0
ファイル: shell.py プロジェクト: ImadBouirmane/spyder
class ShellBaseWidget(ConsoleBaseWidget, SaveHistoryMixin):
    """
    Shell base widget
    """
    
    redirect_stdio = Signal(bool)
    sig_keyboard_interrupt = Signal()
    execute = Signal(str)
    append_to_history = Signal(str, str)
    
    def __init__(self, parent, history_filename, profile=False):
        """
        parent : specifies the parent widget
        """
        ConsoleBaseWidget.__init__(self, parent)
        SaveHistoryMixin.__init__(self)
                
        # Prompt position: tuple (line, index)
        self.current_prompt_pos = None
        self.new_input_line = True
        
        # History
        self.histidx = None
        self.hist_wholeline = False
        assert is_text_string(history_filename)
        self.history_filename = history_filename
        self.history = self.load_history()
        
        # Session
        self.historylog_filename = CONF.get('main', 'historylog_filename',
                                            get_conf_path('history.log'))
        
        # Context menu
        self.menu = None
        self.setup_context_menu()

        # Simple profiling test
        self.profile = profile
        
        # Buffer to increase performance of write/flush operations
        self.__buffer = []
        self.__timestamp = 0.0
        self.__flushtimer = QTimer(self)
        self.__flushtimer.setSingleShot(True)
        self.__flushtimer.timeout.connect(self.flush)

        # Give focus to widget
        self.setFocus()
        
        # Completion
        completion_size = CONF.get('shell_appearance', 'completion/size')
        completion_font = get_font('console')
        self.completion_widget.setup_appearance(completion_size,
                                                completion_font)
        # Cursor width
        self.setCursorWidth( CONF.get('shell_appearance', 'cursor/width') )
        
    def toggle_wrap_mode(self, enable):
        """Enable/disable wrap mode"""
        self.set_wrap_mode('character' if enable else None)

    def set_font(self, font):
        """Set shell styles font"""
        self.setFont(font)
        self.set_pythonshell_font(font)
        cursor = self.textCursor()
        cursor.select(QTextCursor.Document)
        charformat = QTextCharFormat()
        charformat.setFontFamily(font.family())
        charformat.setFontPointSize(font.pointSize())
        cursor.mergeCharFormat(charformat)


    #------ Context menu
    def setup_context_menu(self):
        """Setup shell context menu"""
        self.menu = QMenu(self)
        self.cut_action = create_action(self, _("Cut"),
                                        shortcut=keybinding('Cut'),
                                        icon=ima.icon('editcut'),
                                        triggered=self.cut)
        self.copy_action = create_action(self, _("Copy"),
                                         shortcut=keybinding('Copy'),
                                         icon=ima.icon('editcopy'),
                                         triggered=self.copy)
        paste_action = create_action(self, _("Paste"),
                                     shortcut=keybinding('Paste'),
                                     icon=ima.icon('editpaste'),
                                     triggered=self.paste)
        save_action = create_action(self, _("Save history log..."),
                                    icon=ima.icon('filesave'),
                                    tip=_("Save current history log (i.e. all "
                                          "inputs and outputs) in a text file"),
                                    triggered=self.save_historylog)
        self.delete_action = create_action(self, _("Delete"),
                                    shortcut=keybinding('Delete'),
                                    icon=ima.icon('editdelete'),
                                    triggered=self.delete)
        selectall_action = create_action(self, _("Select All"),
                                    shortcut=keybinding('SelectAll'),
                                    icon=ima.icon('selectall'),
                                    triggered=self.selectAll)
        add_actions(self.menu, (self.cut_action, self.copy_action,
                                paste_action, self.delete_action, None,
                                selectall_action, None, save_action) )
          
    def contextMenuEvent(self, event):
        """Reimplement Qt method"""
        state = self.has_selected_text()
        self.copy_action.setEnabled(state)
        self.cut_action.setEnabled(state)
        self.delete_action.setEnabled(state)
        self.menu.popup(event.globalPos())
        event.accept()        
        
        
    #------ Input buffer
    def get_current_line_from_cursor(self):
        return self.get_text('cursor', 'eof')
    
    def _select_input(self):
        """Select current line (without selecting console prompt)"""
        line, index = self.get_position('eof')
        if self.current_prompt_pos is None:
            pline, pindex = line, index
        else:
            pline, pindex = self.current_prompt_pos
        self.setSelection(pline, pindex, line, index)

    @Slot()
    def clear_line(self):
        """Clear current line (without clearing console prompt)"""
        if self.current_prompt_pos is not None:
            self.remove_text(self.current_prompt_pos, 'eof')

    @Slot()
    def clear_terminal(self):
        """
        Clear terminal window
        Child classes reimplement this method to write prompt
        """
        self.clear()

    # The buffer being edited
    def _set_input_buffer(self, text):
        """Set input buffer"""
        if self.current_prompt_pos is not None:
            self.replace_text(self.current_prompt_pos, 'eol', text)
        else:
            self.insert(text)
        self.set_cursor_position('eof')

    def _get_input_buffer(self):
        """Return input buffer"""
        input_buffer = ''
        if self.current_prompt_pos is not None:
            input_buffer = self.get_text(self.current_prompt_pos, 'eol')
            input_buffer = input_buffer.replace(os.linesep, '\n')
        return input_buffer

    input_buffer = Property("QString", _get_input_buffer, _set_input_buffer)
        
        
    #------ Prompt
    def new_prompt(self, prompt):
        """
        Print a new prompt and save its (line, index) position
        """
        if self.get_cursor_line_column()[1] != 0:
            self.write('\n')
        self.write(prompt, prompt=True)
        # now we update our cursor giving end of prompt
        self.current_prompt_pos = self.get_position('cursor')
        self.ensureCursorVisible()
        self.new_input_line = False
        
    def check_selection(self):
        """
        Check if selected text is r/w,
        otherwise remove read-only parts of selection
        """
        if self.current_prompt_pos is None:
            self.set_cursor_position('eof')
        else:
            self.truncate_selection(self.current_prompt_pos)
        
        
    #------ Copy / Keyboard interrupt
    @Slot()
    def copy(self):
        """Copy text to clipboard... or keyboard interrupt"""
        if self.has_selected_text():
            ConsoleBaseWidget.copy(self)
        elif not sys.platform == 'darwin':
            self.interrupt()

    def interrupt(self):
        """Keyboard interrupt"""
        self.sig_keyboard_interrupt.emit()

    @Slot()
    def cut(self):
        """Cut text"""
        self.check_selection()
        if self.has_selected_text():
            ConsoleBaseWidget.cut(self)

    @Slot()
    def delete(self):
        """Remove selected text"""
        self.check_selection()
        if self.has_selected_text():
            ConsoleBaseWidget.remove_selected_text(self)

    @Slot()
    def save_historylog(self):
        """Save current history log (all text in console)"""
        title = _("Save history log")
        self.redirect_stdio.emit(False)
        filename, _selfilter = getsavefilename(self, title,
                    self.historylog_filename, "%s (*.log)" % _("History logs"))
        self.redirect_stdio.emit(True)
        if filename:
            filename = osp.normpath(filename)
            try:
                encoding.write(to_text_string(self.get_text_with_eol()),
                               filename)
                self.historylog_filename = filename
                CONF.set('main', 'historylog_filename', filename)
            except EnvironmentError as error:
                QMessageBox.critical(self, title,
                                     _("<b>Unable to save file '%s'</b>"
                                       "<br><br>Error message:<br>%s"
                                       ) % (osp.basename(filename),
                                            to_text_string(error)))
        
        
    #------ Basic keypress event handler
    def on_enter(self, command):
        """on_enter"""
        self.execute_command(command)
        
    def execute_command(self, command):
        self.execute.emit(command)
        self.add_to_history(command)
        self.new_input_line = True
        
    def on_new_line(self):
        """On new input line"""
        self.set_cursor_position('eof')
        self.current_prompt_pos = self.get_position('cursor')
        self.new_input_line = False

    @Slot()
    def paste(self):
        """Reimplemented slot to handle multiline paste action"""
        if self.new_input_line:
            self.on_new_line()
        ConsoleBaseWidget.paste(self)
        
    def keyPressEvent(self, event):
        """
        Reimplement Qt Method
        Basic keypress event handler
        (reimplemented in InternalShell to add more sophisticated features)
        """
        if self.preprocess_keyevent(event):
            # Event was accepted in self.preprocess_keyevent
            return
        self.postprocess_keyevent(event)
        
    def preprocess_keyevent(self, event):
        """Pre-process keypress event:
        return True if event is accepted, false otherwise"""
        # Copy must be done first to be able to copy read-only text parts
        # (otherwise, right below, we would remove selection
        #  if not on current line)
        ctrl = event.modifiers() & Qt.ControlModifier
        meta = event.modifiers() & Qt.MetaModifier    # meta=ctrl in OSX
        if event.key() == Qt.Key_C and \
          ((Qt.MetaModifier | Qt.ControlModifier) & event.modifiers()):
            if meta and sys.platform == 'darwin':
                self.interrupt()
            elif ctrl:
                self.copy()
            event.accept()
            return True
        
        if self.new_input_line and ( len(event.text()) or event.key() in \
           (Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right) ):
            self.on_new_line()
        
        return False
        
    def postprocess_keyevent(self, event):
        """Post-process keypress event:
        in InternalShell, this is method is called when shell is ready"""
        event, text, key, ctrl, shift = restore_keyevent(event)
        
        # Is cursor on the last line? and after prompt?
        if len(text):
            #XXX: Shouldn't it be: `if len(unicode(text).strip(os.linesep))` ?
            if self.has_selected_text():
                self.check_selection()
            self.restrict_cursor_position(self.current_prompt_pos, 'eof')
            
        cursor_position = self.get_position('cursor')

        if key in (Qt.Key_Return, Qt.Key_Enter):
            if self.is_cursor_on_last_line():
                self._key_enter()
            # add and run selection
            else:
                self.insert_text(self.get_selected_text(), at_end=True)
            
        elif key == Qt.Key_Insert and not shift and not ctrl:
            self.setOverwriteMode(not self.overwriteMode())
            
        elif key == Qt.Key_Delete:
            if self.has_selected_text():
                self.check_selection()
                self.remove_selected_text()
            elif self.is_cursor_on_last_line():
                self.stdkey_clear()
            
        elif key == Qt.Key_Backspace:
            self._key_backspace(cursor_position)
            
        elif key == Qt.Key_Tab:
            self._key_tab()
            
        elif key == Qt.Key_Space and ctrl:
            self._key_ctrl_space()

        elif key == Qt.Key_Left:
            if self.current_prompt_pos == cursor_position:
                # Avoid moving cursor on prompt
                return
            method = self.extend_selection_to_next if shift \
                     else self.move_cursor_to_next
            method('word' if ctrl else 'character', direction='left')
                
        elif key == Qt.Key_Right:
            if self.is_cursor_at_end():
                return
            method = self.extend_selection_to_next if shift \
                     else self.move_cursor_to_next
            method('word' if ctrl else 'character', direction='right')

        elif (key == Qt.Key_Home) or ((key == Qt.Key_Up) and ctrl):
            self._key_home(shift, ctrl)

        elif (key == Qt.Key_End) or ((key == Qt.Key_Down) and ctrl):
            self._key_end(shift, ctrl)

        elif key == Qt.Key_Up:
            if not self.is_cursor_on_last_line():
                self.set_cursor_position('eof')
            y_cursor = self.get_coordinates(cursor_position)[1]
            y_prompt = self.get_coordinates(self.current_prompt_pos)[1]
            if y_cursor > y_prompt:
                self.stdkey_up(shift)
            else:
                self.browse_history(backward=True)
                
        elif key == Qt.Key_Down:
            if not self.is_cursor_on_last_line():
                self.set_cursor_position('eof')
            y_cursor = self.get_coordinates(cursor_position)[1]
            y_end = self.get_coordinates('eol')[1]
            if y_cursor < y_end:
                self.stdkey_down(shift)
            else:
                self.browse_history(backward=False)
            
        elif key in (Qt.Key_PageUp, Qt.Key_PageDown):
            #XXX: Find a way to do this programmatically instead of calling
            # widget keyhandler (this won't work if the *event* is coming from
            # the event queue - i.e. if the busy buffer is ever implemented)
            ConsoleBaseWidget.keyPressEvent(self, event)

        elif key == Qt.Key_Escape and shift:
            self.clear_line()

        elif key == Qt.Key_Escape:
            self._key_escape()
                
        elif key == Qt.Key_L and ctrl:
            self.clear_terminal()
            
        elif key == Qt.Key_V and ctrl:
            self.paste()
            
        elif key == Qt.Key_X and ctrl:
            self.cut()
            
        elif key == Qt.Key_Z and ctrl:
            self.undo()
            
        elif key == Qt.Key_Y and ctrl:
            self.redo()
            
        elif key == Qt.Key_A and ctrl:
            self.selectAll()
                
        elif key == Qt.Key_Question and not self.has_selected_text():
            self._key_question(text)
            
        elif key == Qt.Key_ParenLeft and not self.has_selected_text():
            self._key_parenleft(text)
            
        elif key == Qt.Key_Period and not self.has_selected_text():
            self._key_period(text)

        elif len(text) and not self.isReadOnly():
            self.hist_wholeline = False
            self.insert_text(text)
            self._key_other(text)
                
        else:
            # Let the parent widget handle the key press event
            ConsoleBaseWidget.keyPressEvent(self, event)
            
                
    #------ Key handlers
    def _key_enter(self):
        command = self.input_buffer
        self.insert_text('\n', at_end=True)
        self.on_enter(command)
        self.flush()
    def _key_other(self, text):
        raise NotImplementedError
    def _key_backspace(self, cursor_position):
        raise NotImplementedError
    def _key_tab(self):
        raise NotImplementedError
    def _key_ctrl_space(self):
        raise NotImplementedError
    def _key_home(self, shift, ctrl):
        if self.is_cursor_on_last_line():
            self.stdkey_home(shift, ctrl, self.current_prompt_pos)
    def _key_end(self, shift, ctrl):
        if self.is_cursor_on_last_line():
            self.stdkey_end(shift, ctrl)
    def _key_pageup(self):
        raise NotImplementedError
    def _key_pagedown(self):
        raise NotImplementedError
    def _key_escape(self):
        raise NotImplementedError
    def _key_question(self, text):
        raise NotImplementedError
    def _key_parenleft(self, text):
        raise NotImplementedError
    def _key_period(self, text):
        raise NotImplementedError

        
    #------ History Management
    def load_history(self):
        """Load history from a .py file in user home directory"""
        if osp.isfile(self.history_filename):
            rawhistory, _ = encoding.readlines(self.history_filename)
            rawhistory = [line.replace('\n', '') for line in rawhistory]
            if rawhistory[1] != self.INITHISTORY[1]:
                rawhistory[1] = self.INITHISTORY[1]
        else:
            rawhistory = self.INITHISTORY
        history = [line for line in rawhistory \
                   if line and not line.startswith('#')]

        # Truncating history to X entries:
        while len(history) >= CONF.get('historylog', 'max_entries'):
            del history[0]
            while rawhistory[0].startswith('#'):
                del rawhistory[0]
            del rawhistory[0]
        # Saving truncated history:
        encoding.writelines(rawhistory, self.history_filename)
        return history
        
    def browse_history(self, backward):
        """Browse history"""
        if self.is_cursor_before('eol') and self.hist_wholeline:
            self.hist_wholeline = False
        tocursor = self.get_current_line_to_cursor()
        text, self.histidx = self.__find_in_history(tocursor,
                                                    self.histidx, backward)
        if text is not None:
            if self.hist_wholeline:
                self.clear_line()
                self.insert_text(text)
            else:
                cursor_position = self.get_position('cursor')
                # Removing text from cursor to the end of the line
                self.remove_text('cursor', 'eol')
                # Inserting history text
                self.insert_text(text)
                self.set_cursor_position(cursor_position)

    def __find_in_history(self, tocursor, start_idx, backward):
        """Find text 'tocursor' in history, from index 'start_idx'"""
        if start_idx is None:
            start_idx = len(self.history)
        # Finding text in history
        step = -1 if backward else 1
        idx = start_idx
        if len(tocursor) == 0 or self.hist_wholeline:
            idx += step
            if idx >= len(self.history) or len(self.history) == 0:
                return "", len(self.history)
            elif idx < 0:
                idx = 0
            self.hist_wholeline = True
            return self.history[idx], idx
        else:
            for index in range(len(self.history)):
                idx = (start_idx+step*(index+1)) % len(self.history)
                entry = self.history[idx]
                if entry.startswith(tocursor):
                    return entry[len(tocursor):], idx
            else:
                return None, start_idx
    
    
    #------ Simulation standards input/output
    def write_error(self, text):
        """Simulate stderr"""
        self.flush()
        self.write(text, flush=True, error=True)
        if DEBUG:
            STDERR.write(text)

    def write(self, text, flush=False, error=False, prompt=False):
        """Simulate stdout and stderr"""
        if prompt:
            self.flush()
        if not is_string(text):
            # This test is useful to discriminate QStrings from decoded str
            text = to_text_string(text)
        self.__buffer.append(text)
        ts = time.time()
        if flush or prompt:
            self.flush(error=error, prompt=prompt)
        elif ts - self.__timestamp > 0.05:
            self.flush(error=error)
            self.__timestamp = ts
            # Timer to flush strings cached by last write() operation in series
            self.__flushtimer.start(50)

    def flush(self, error=False, prompt=False):
        """Flush buffer, write text to console"""
        # Fix for Issue 2452 
        if PY3:
            try:
                text = "".join(self.__buffer)
            except TypeError:
                text = b"".join(self.__buffer)
                try:
                    text = text.decode( locale.getdefaultlocale()[1] )
                except:
                    pass
        else:
            text = "".join(self.__buffer)

        self.__buffer = []
        self.insert_text(text, at_end=True, error=error, prompt=prompt)
        QCoreApplication.processEvents()
        self.repaint()
        # Clear input buffer:
        self.new_input_line = True


    #------ Text Insertion
    def insert_text(self, text, at_end=False, error=False, prompt=False):
        """
        Insert text at the current cursor position
        or at the end of the command line
        """
        if at_end:
            # Insert text at the end of the command line
            self.append_text_to_shell(text, error, prompt)
        else:
            # Insert text at current cursor position
            ConsoleBaseWidget.insert_text(self, text)

            
    #------ Re-implemented Qt Methods
    def focusNextPrevChild(self, next):
        """
        Reimplemented to stop Tab moving to the next window
        """
        if next:
            return False
        return ConsoleBaseWidget.focusNextPrevChild(self, next)

    
    #------ Drag and drop
    def dragEnterEvent(self, event):
        """Drag and Drop - Enter event"""
        event.setAccepted(event.mimeData().hasFormat("text/plain"))

    def dragMoveEvent(self, event):
        """Drag and Drop - Move event"""
        if (event.mimeData().hasFormat("text/plain")):
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        """Drag and Drop - Drop event"""
        if (event.mimeData().hasFormat("text/plain")):
            text = to_text_string(event.mimeData().text())
            if self.new_input_line:
                self.on_new_line()
            self.insert_text(text, at_end=True)
            self.setFocus()
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()
            
    def drop_pathlist(self, pathlist):
        """Drop path list"""
        raise NotImplementedError
コード例 #48
0
ファイル: explorer.py プロジェクト: jromang/retina-old
class DirView(QTreeView):
    """Base file/directory tree view"""
    def __init__(self, parent=None):
        super(DirView, self).__init__(parent)
        self.name_filters = None
        self.parent_widget = parent
        self.valid_types = None
        self.show_all = None
        self.menu = None
        self.common_actions = None
        self.__expanded_state = None
        self._to_be_loaded = None
        self.fsmodel = None
        self.setup_fs_model()
        self._scrollbar_positions = None
                
    #---- Model
    def setup_fs_model(self):
        """Setup filesystem model"""
        filters = QDir.AllDirs | QDir.Files | QDir.Drives | QDir.NoDotAndDotDot
        self.fsmodel = QFileSystemModel(self)
        self.fsmodel.setFilter(filters)
        self.fsmodel.setNameFilterDisables(False)
        
    def install_model(self):
        """Install filesystem model"""
        self.setModel(self.fsmodel)
        
    def setup_view(self):
        """Setup view"""
        self.install_model()
        self.connect(self.fsmodel, SIGNAL('directoryLoaded(QString)'),
                     lambda: self.resizeColumnToContents(0))
        self.setAnimated(False)
        self.setSortingEnabled(True)
        self.sortByColumn(0, Qt.AscendingOrder)
        
    def set_name_filters(self, name_filters):
        """Set name filters"""
        self.name_filters = name_filters
        self.fsmodel.setNameFilters(name_filters)
        
    def set_show_all(self, state):
        """Toggle 'show all files' state"""
        if state:
            self.fsmodel.setNameFilters([])
        else:
            self.fsmodel.setNameFilters(self.name_filters)
            
    def get_filename(self, index):
        """Return filename associated with *index*"""
        if index:
            return osp.normpath(unicode(self.fsmodel.filePath(index)))
        
    def get_index(self, filename):
        """Return index associated with filename"""
        return self.fsmodel.index(filename)
        
    def get_selected_filenames(self):
        """Return selected filenames"""
        if self.selectionMode() == self.ExtendedSelection:
            return [self.get_filename(idx) for idx in self.selectedIndexes()]
        else:
            return [self.get_filename(self.currentIndex())]
            
    def get_dirname(self, index):
        """Return dirname associated with *index*"""
        fname = self.get_filename(index)
        if fname:
            if osp.isdir(fname):
                return fname
            else:
                return osp.dirname(fname)
        
    #---- Tree view widget
    def setup(self, name_filters=['*.py', '*.pyw'],
              valid_types= ('.py', '.pyw'), show_all=False):
        """Setup tree widget"""
        self.setup_view()
        
        self.set_name_filters(name_filters)
        self.valid_types = valid_types
        self.show_all = show_all
        
        # Setup context menu
        self.menu = QMenu(self)
        self.common_actions = self.setup_common_actions()
        
    #---- Context menu
    def setup_common_actions(self):
        """Setup context menu common actions"""
        # Filters
        filters_action = create_action(self, _("Edit filename filters..."),
                                       None, get_icon('filter.png'),
                                       triggered=self.edit_filter)
        # Show all files
        all_action = create_action(self, _("Show all files"),
                                   toggled=self.toggle_all)
        all_action.setChecked(self.show_all)
        self.toggle_all(self.show_all)
        
        return [filters_action, all_action]
        
    def edit_filter(self):
        """Edit name filters"""
        filters, valid = QInputDialog.getText(self, _('Edit filename filters'),
                                              _('Name filters:'),
                                              QLineEdit.Normal,
                                              ", ".join(self.name_filters))
        if valid:
            filters = [f.strip() for f in unicode(filters).split(',')]
            self.parent_widget.sig_option_changed.emit('name_filters', filters)
            self.set_name_filters(filters)
            
    def toggle_all(self, checked):
        """Toggle all files mode"""
        self.parent_widget.sig_option_changed.emit('show_all', checked)
        self.show_all = checked
        self.set_show_all(checked)
        
    def create_file_new_actions(self, fnames):
        """Return actions for submenu 'New...'"""
        if not fnames:
            return []
        new_file_act = create_action(self, _("File..."), icon='filenew.png',
                                     triggered=lambda:
                                     self.new_file(fnames[-1]))
        new_module_act = create_action(self, _("Module..."), icon='py.png',
                                       triggered=lambda:
                                         self.new_module(fnames[-1]))
        new_folder_act = create_action(self, _("Folder..."),
                                       icon='folder_new.png',
                                       triggered=lambda:
                                        self.new_folder(fnames[-1]))
        new_package_act = create_action(self, _("Package..."),
                                        icon=get_icon('package_collapsed.png'),
                                        triggered=lambda:
                                         self.new_package(fnames[-1]))
        return [new_file_act, new_folder_act, None,
                new_module_act, new_package_act]
        
    def create_file_import_actions(self, fnames):
        """Return actions for submenu 'Import...'"""
        return []

    def create_file_manage_actions(self, fnames):
        """Return file management actions"""
        only_files = all([osp.isfile(_fn) for _fn in fnames])
        only_modules = all([osp.splitext(_fn)[1] in ('.py', '.pyw', '.ipy')
                            for _fn in fnames])
        only_valid = all([osp.splitext(_fn)[1] in self.valid_types
                          for _fn in fnames])
        run_action = create_action(self, _("Run"), icon="run_small.png",
                                   triggered=self.run)
        edit_action = create_action(self, _("Edit"), icon="edit.png",
                                    triggered=self.clicked)
        move_action = create_action(self, _("Move..."),
                                    icon="move.png",
                                    triggered=self.move)
        delete_action = create_action(self, _("Delete..."),
                                      icon="delete.png",
                                      triggered=self.delete)
        rename_action = create_action(self, _("Rename..."),
                                      icon="rename.png",
                                      triggered=self.rename)
        open_action = create_action(self, _("Open"), triggered=self.open)
        
        actions = []
        if only_modules:
            actions.append(run_action)
        if only_valid and only_files:
            actions.append(edit_action)
        else:
            actions.append(open_action)
        actions += [delete_action, rename_action]
        basedir = fixpath(osp.dirname(fnames[0]))
        if all([fixpath(osp.dirname(_fn)) == basedir for _fn in fnames]):
            actions.append(move_action)
        actions += [None]
        
        # SCM support is quite limited for now, so we are enabling the SCM
        # related actions only when a single file/folder is selected:
        dirname = fnames[0] if osp.isdir(fnames[0]) else osp.dirname(fnames[0])
        if len(fnames) == 1 and scm.is_scm_repository(dirname):
            scm_ci = create_action(self, _("Commit"),
                                   icon="scm_commit.png",
                                   triggered=lambda fnames=[dirname]:
                                   self.scm_command(fnames, tool='commit'))
            scm_log = create_action(self, _("Browse repository"),
                                    icon="scm_browse.png",
                                    triggered=lambda fnames=[dirname]:
                                    self.scm_command(fnames, tool='browse'))
            actions += [None, scm_ci, scm_log]
        
        return actions

    def create_folder_manage_actions(self, fnames):
        """Return folder management actions"""
        actions = []
        if os.name == 'nt':
            _title = _("Open command prompt here")
        else:
            _title = _("Open terminal here")
        action = create_action(self, _title, icon="cmdprompt.png",
                               triggered=lambda fnames=fnames:
                               self.open_terminal(fnames))
        actions.append(action)
        _title = _("Open Python interpreter here")
        action = create_action(self, _title, icon="python.png",
                               triggered=lambda fnames=fnames:
                               self.open_interpreter(fnames))
        actions.append(action)
        if programs.is_module_installed('IPython', '0.1'):
            _title = _("Open IPython here")
            action = create_action(self, _title, icon="ipython.png",
                                   triggered=lambda fnames=fnames:
                                   self.open_ipython(fnames))
            actions.append(action)
        return actions
        
    def create_context_menu_actions(self):
        """Create context menu actions"""
        actions = []
        fnames = self.get_selected_filenames()
        new_actions = self.create_file_new_actions(fnames)
        if len(new_actions) > 1:
            # Creating a submenu only if there is more than one entry
            new_act_menu = QMenu(_('New'), self)
            add_actions(new_act_menu, new_actions)
            actions.append(new_act_menu)
        else:
            actions += new_actions
        import_actions = self.create_file_import_actions(fnames)
        if len(import_actions) > 1:
            # Creating a submenu only if there is more than one entry
            import_act_menu = QMenu(_('Import'), self)
            add_actions(import_act_menu, import_actions)
            actions.append(import_act_menu)
        else:
            actions += import_actions
        if actions:
            actions.append(None)
        if fnames:
            actions += self.create_file_manage_actions(fnames)
        if actions:
            actions.append(None)
        if fnames and all([osp.isdir(_fn) for _fn in fnames]):
            actions += self.create_folder_manage_actions(fnames)
        if actions:
            actions.append(None)
        actions += self.common_actions
        return actions

    def update_menu(self):
        """Update context menu"""
        self.menu.clear()
        add_actions(self.menu, self.create_context_menu_actions())
    
    #---- Events
    def contextMenuEvent(self, event):
        """Override Qt method"""
        self.update_menu()
        self.menu.popup(event.globalPos())

    def keyPressEvent(self, event):
        """Reimplement Qt method"""
        if event.key() in (Qt.Key_Enter, Qt.Key_Return):
            self.clicked()
        elif event.key() == Qt.Key_F2:
            self.rename()
        elif event.key() == Qt.Key_Delete:
            self.delete()
        else:
            QTreeView.keyPressEvent(self, event)

    def mouseDoubleClickEvent(self, event):
        """Reimplement Qt method"""
        QTreeView.mouseDoubleClickEvent(self, event)
        self.clicked()
        
    def clicked(self):
        """Selected item was double-clicked or enter/return was pressed"""
        fnames = self.get_selected_filenames()
        for fname in fnames:
            if osp.isdir(fname):
                self.directory_clicked(fname)
            else:
                self.open([fname])
                
    def directory_clicked(self, dirname):
        """Directory was just clicked"""
        pass
        
    #---- Drag
    def dragEnterEvent(self, event):
        """Drag and Drop - Enter event"""
        event.setAccepted(event.mimeData().hasFormat("text/plain"))

    def dragMoveEvent(self, event):
        """Drag and Drop - Move event"""
        if (event.mimeData().hasFormat("text/plain")):
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()
            
    def startDrag(self, dropActions):
        """Reimplement Qt Method - handle drag event"""
        data = QMimeData()
        data.setUrls([QUrl(fname) for fname in self.get_selected_filenames()])
        drag = QDrag(self)
        drag.setMimeData(data)
        drag.exec_()
        
    #---- File/Directory actions
    def open(self, fnames=None):
        """Open files with the appropriate application"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        for fname in fnames:
            ext = osp.splitext(fname)[1]
            if osp.isfile(fname) and ext in self.valid_types:
                self.parent_widget.sig_open_file.emit(fname)
            else:
                self.open_outside_spyder([fname])
        
    def open_outside_spyder(self, fnames):
        """Open file outside Spyder with the appropriate application
        If this does not work, opening unknown file in Spyder, as text file"""
        for path in sorted(fnames):
            path = file_uri(path)
            ok = programs.start_file(path)
            if not ok:
                self.parent_widget.emit(SIGNAL("edit(QString)"), path)
                
    def open_terminal(self, fnames):
        """Open terminal"""
        for path in sorted(fnames):
            self.parent_widget.emit(SIGNAL("open_terminal(QString)"), path)
            
    def open_interpreter(self, fnames):
        """Open interpreter"""
        for path in sorted(fnames):
            self.parent_widget.emit(SIGNAL("open_interpreter(QString)"), path)
            
    def open_ipython(self, fnames):
        """Open IPython"""
        for path in sorted(fnames):
            self.parent_widget.emit(SIGNAL("open_ipython(QString)"), path)
        
    def run(self, fnames=None):
        """Run Python scripts"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        for fname in fnames:
            self.parent_widget.emit(SIGNAL("run(QString)"), fname)
    
    def remove_tree(self, dirname):
        """Remove whole directory tree
        Reimplemented in project explorer widget"""
        shutil.rmtree(dirname)
    
    def delete_file(self, fname, multiple, yes_to_all):
        """Delete file"""
        if multiple:
            buttons = QMessageBox.Yes|QMessageBox.YesAll| \
                      QMessageBox.No|QMessageBox.Cancel
        else:
            buttons = QMessageBox.Yes|QMessageBox.No
        if yes_to_all is None:
            answer = QMessageBox.warning(self, _("Delete"),
                                 _("Do you really want "
                                   "to delete <b>%s</b>?"
                                   ) % osp.basename(fname), buttons)
            if answer == QMessageBox.No:
                return yes_to_all
            elif answer == QMessageBox.Cancel:
                return False
            elif answer == QMessageBox.YesAll:
                yes_to_all = True
        try:
            if osp.isfile(fname):
                misc.remove_file(fname)
                self.parent_widget.emit(SIGNAL("removed(QString)"),
                                        fname)
            else:
                self.remove_tree(fname)
                self.parent_widget.emit(SIGNAL("removed_tree(QString)"),
                                        fname)
            return yes_to_all
        except EnvironmentError, error:
            action_str = _('delete')
            QMessageBox.critical(self, _("Project Explorer"),
                            _("<b>Unable to %s <i>%s</i></b>"
                              "<br><br>Error message:<br>%s"
                              ) % (action_str, fname, unicode(error)))
        return False
コード例 #49
0
ファイル: explorer.py プロジェクト: sonofeft/spyder
class DirView(QTreeView):
    """Base file/directory tree view"""

    def __init__(self, parent=None):
        super(DirView, self).__init__(parent)
        self.name_filters = ["*.py"]
        self.parent_widget = parent
        self.show_all = None
        self.menu = None
        self.common_actions = None
        self.__expanded_state = None
        self._to_be_loaded = None
        self.fsmodel = None
        self.setup_fs_model()
        self._scrollbar_positions = None

    # ---- Model
    def setup_fs_model(self):
        """Setup filesystem model"""
        filters = QDir.AllDirs | QDir.Files | QDir.Drives | QDir.NoDotAndDotDot
        self.fsmodel = QFileSystemModel(self)
        self.fsmodel.setFilter(filters)
        self.fsmodel.setNameFilterDisables(False)

    def install_model(self):
        """Install filesystem model"""
        self.setModel(self.fsmodel)

    def setup_view(self):
        """Setup view"""
        self.install_model()
        if not is_pyqt46:
            self.fsmodel.directoryLoaded.connect(lambda: self.resizeColumnToContents(0))
        self.setAnimated(False)
        self.setSortingEnabled(True)
        self.sortByColumn(0, Qt.AscendingOrder)

    def set_name_filters(self, name_filters):
        """Set name filters"""
        self.name_filters = name_filters
        self.fsmodel.setNameFilters(name_filters)

    def set_show_all(self, state):
        """Toggle 'show all files' state"""
        if state:
            self.fsmodel.setNameFilters([])
        else:
            self.fsmodel.setNameFilters(self.name_filters)

    def get_filename(self, index):
        """Return filename associated with *index*"""
        if index:
            return osp.normpath(to_text_string(self.fsmodel.filePath(index)))

    def get_index(self, filename):
        """Return index associated with filename"""
        return self.fsmodel.index(filename)

    def get_selected_filenames(self):
        """Return selected filenames"""
        if self.selectionMode() == self.ExtendedSelection:
            return [self.get_filename(idx) for idx in self.selectedIndexes()]
        else:
            return [self.get_filename(self.currentIndex())]

    def get_dirname(self, index):
        """Return dirname associated with *index*"""
        fname = self.get_filename(index)
        if fname:
            if osp.isdir(fname):
                return fname
            else:
                return osp.dirname(fname)

    # ---- Tree view widget
    def setup(self, name_filters=["*.py", "*.pyw"], show_all=False):
        """Setup tree widget"""
        self.setup_view()

        self.set_name_filters(name_filters)
        self.show_all = show_all

        # Setup context menu
        self.menu = QMenu(self)
        self.common_actions = self.setup_common_actions()

    # ---- Context menu
    def setup_common_actions(self):
        """Setup context menu common actions"""
        # Filters
        filters_action = create_action(
            self, _("Edit filename filters..."), None, ima.icon("filter"), triggered=self.edit_filter
        )
        # Show all files
        all_action = create_action(self, _("Show all files"), toggled=self.toggle_all)
        all_action.setChecked(self.show_all)
        self.toggle_all(self.show_all)

        return [filters_action, all_action]

    @Slot()
    def edit_filter(self):
        """Edit name filters"""
        filters, valid = QInputDialog.getText(
            self, _("Edit filename filters"), _("Name filters:"), QLineEdit.Normal, ", ".join(self.name_filters)
        )
        if valid:
            filters = [f.strip() for f in to_text_string(filters).split(",")]
            self.parent_widget.sig_option_changed.emit("name_filters", filters)
            self.set_name_filters(filters)

    @Slot(bool)
    def toggle_all(self, checked):
        """Toggle all files mode"""
        self.parent_widget.sig_option_changed.emit("show_all", checked)
        self.show_all = checked
        self.set_show_all(checked)

    def create_file_new_actions(self, fnames):
        """Return actions for submenu 'New...'"""
        if not fnames:
            return []
        new_file_act = create_action(
            self, _("File..."), icon=ima.icon("filenew"), triggered=lambda: self.new_file(fnames[-1])
        )
        new_module_act = create_action(
            self, _("Module..."), icon=ima.icon("spyder"), triggered=lambda: self.new_module(fnames[-1])
        )
        new_folder_act = create_action(
            self, _("Folder..."), icon=ima.icon("folder_new"), triggered=lambda: self.new_folder(fnames[-1])
        )
        new_package_act = create_action(
            self, _("Package..."), icon=ima.icon("package_new"), triggered=lambda: self.new_package(fnames[-1])
        )
        return [new_file_act, new_folder_act, None, new_module_act, new_package_act]

    def create_file_import_actions(self, fnames):
        """Return actions for submenu 'Import...'"""
        return []

    def create_file_manage_actions(self, fnames):
        """Return file management actions"""
        only_files = all([osp.isfile(_fn) for _fn in fnames])
        only_modules = all([osp.splitext(_fn)[1] in (".py", ".pyw", ".ipy") for _fn in fnames])
        only_notebooks = all([osp.splitext(_fn)[1] == ".ipynb" for _fn in fnames])
        only_valid = all([encoding.is_text_file(_fn) for _fn in fnames])
        run_action = create_action(self, _("Run"), icon=ima.icon("run"), triggered=self.run)
        edit_action = create_action(self, _("Edit"), icon=ima.icon("edit"), triggered=self.clicked)
        move_action = create_action(self, _("Move..."), icon="move.png", triggered=self.move)
        delete_action = create_action(self, _("Delete..."), icon=ima.icon("editdelete"), triggered=self.delete)
        rename_action = create_action(self, _("Rename..."), icon=ima.icon("rename"), triggered=self.rename)
        open_action = create_action(self, _("Open"), triggered=self.open)
        ipynb_convert_action = create_action(
            self, _("Convert to Python script"), icon=ima.icon("python"), triggered=self.convert_notebooks
        )

        actions = []
        if only_modules:
            actions.append(run_action)
        if only_valid and only_files:
            actions.append(edit_action)
        else:
            actions.append(open_action)
        actions += [delete_action, rename_action]
        basedir = fixpath(osp.dirname(fnames[0]))
        if all([fixpath(osp.dirname(_fn)) == basedir for _fn in fnames]):
            actions.append(move_action)
        actions += [None]
        if only_notebooks and nbexporter is not None:
            actions.append(ipynb_convert_action)

        # VCS support is quite limited for now, so we are enabling the VCS
        # related actions only when a single file/folder is selected:
        dirname = fnames[0] if osp.isdir(fnames[0]) else osp.dirname(fnames[0])
        if len(fnames) == 1 and vcs.is_vcs_repository(dirname):
            vcs_ci = create_action(
                self,
                _("Commit"),
                icon=ima.icon("vcs_commit"),
                triggered=lambda fnames=[dirname]: self.vcs_command(fnames, "commit"),
            )
            vcs_log = create_action(
                self,
                _("Browse repository"),
                icon=ima.icon("vcs_browse"),
                triggered=lambda fnames=[dirname]: self.vcs_command(fnames, "browse"),
            )
            actions += [None, vcs_ci, vcs_log]

        return actions

    def create_folder_manage_actions(self, fnames):
        """Return folder management actions"""
        actions = []
        if os.name == "nt":
            _title = _("Open command prompt here")
        else:
            _title = _("Open terminal here")
        action = create_action(self, _title, icon=ima.icon("cmdprompt"), triggered=lambda: self.open_terminal(fnames))
        actions.append(action)
        _title = _("Open Python console here")
        action = create_action(self, _title, icon=ima.icon("python"), triggered=lambda: self.open_interpreter(fnames))
        actions.append(action)
        return actions

    def create_context_menu_actions(self):
        """Create context menu actions"""
        actions = []
        fnames = self.get_selected_filenames()
        new_actions = self.create_file_new_actions(fnames)
        if len(new_actions) > 1:
            # Creating a submenu only if there is more than one entry
            new_act_menu = QMenu(_("New"), self)
            add_actions(new_act_menu, new_actions)
            actions.append(new_act_menu)
        else:
            actions += new_actions
        import_actions = self.create_file_import_actions(fnames)
        if len(import_actions) > 1:
            # Creating a submenu only if there is more than one entry
            import_act_menu = QMenu(_("Import"), self)
            add_actions(import_act_menu, import_actions)
            actions.append(import_act_menu)
        else:
            actions += import_actions
        if actions:
            actions.append(None)
        if fnames:
            actions += self.create_file_manage_actions(fnames)
        if actions:
            actions.append(None)
        if fnames and all([osp.isdir(_fn) for _fn in fnames]):
            actions += self.create_folder_manage_actions(fnames)
        if actions:
            actions.append(None)
        actions += self.common_actions
        return actions

    def update_menu(self):
        """Update context menu"""
        self.menu.clear()
        add_actions(self.menu, self.create_context_menu_actions())

    # ---- Events
    def viewportEvent(self, event):
        """Reimplement Qt method"""

        # Prevent Qt from crashing or showing warnings like:
        # "QSortFilterProxyModel: index from wrong model passed to
        # mapFromSource", probably due to the fact that the file system model
        # is being built. See Issue 1250.
        #
        # This workaround was inspired by the following KDE bug:
        # https://bugs.kde.org/show_bug.cgi?id=172198
        #
        # Apparently, this is a bug from Qt itself.
        self.executeDelayedItemsLayout()

        return QTreeView.viewportEvent(self, event)

    def contextMenuEvent(self, event):
        """Override Qt method"""
        self.update_menu()
        self.menu.popup(event.globalPos())

    def keyPressEvent(self, event):
        """Reimplement Qt method"""
        if event.key() in (Qt.Key_Enter, Qt.Key_Return):
            self.clicked()
        elif event.key() == Qt.Key_F2:
            self.rename()
        elif event.key() == Qt.Key_Delete:
            self.delete()
        elif event.key() == Qt.Key_Backspace:
            self.go_to_parent_directory()
        else:
            QTreeView.keyPressEvent(self, event)

    def mouseDoubleClickEvent(self, event):
        """Reimplement Qt method"""
        QTreeView.mouseDoubleClickEvent(self, event)
        self.clicked()

    @Slot()
    def clicked(self):
        """Selected item was double-clicked or enter/return was pressed"""
        fnames = self.get_selected_filenames()
        for fname in fnames:
            if osp.isdir(fname):
                self.directory_clicked(fname)
            else:
                self.open([fname])

    def directory_clicked(self, dirname):
        """Directory was just clicked"""
        pass

    # ---- Drag
    def dragEnterEvent(self, event):
        """Drag and Drop - Enter event"""
        event.setAccepted(event.mimeData().hasFormat("text/plain"))

    def dragMoveEvent(self, event):
        """Drag and Drop - Move event"""
        if event.mimeData().hasFormat("text/plain"):
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()

    def startDrag(self, dropActions):
        """Reimplement Qt Method - handle drag event"""
        data = QMimeData()
        data.setUrls([QUrl(fname) for fname in self.get_selected_filenames()])
        drag = QDrag(self)
        drag.setMimeData(data)
        drag.exec_()

    # ---- File/Directory actions
    @Slot()
    def open(self, fnames=None):
        """Open files with the appropriate application"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        for fname in fnames:
            if osp.isfile(fname) and encoding.is_text_file(fname):
                self.parent_widget.sig_open_file.emit(fname)
            else:
                self.open_outside_spyder([fname])

    def open_outside_spyder(self, fnames):
        """Open file outside Spyder with the appropriate application
        If this does not work, opening unknown file in Spyder, as text file"""
        for path in sorted(fnames):
            path = file_uri(path)
            ok = programs.start_file(path)
            if not ok:
                self.parent_widget.edit.emit(path)

    def open_terminal(self, fnames):
        """Open terminal"""
        for path in sorted(fnames):
            self.parent_widget.open_terminal.emit(path)

    def open_interpreter(self, fnames):
        """Open interpreter"""
        for path in sorted(fnames):
            self.parent_widget.open_interpreter.emit(path)

    @Slot()
    def run(self, fnames=None):
        """Run Python scripts"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        for fname in fnames:
            self.parent_widget.run.emit(fname)

    def remove_tree(self, dirname):
        """Remove whole directory tree
        Reimplemented in project explorer widget"""
        shutil.rmtree(dirname, onerror=misc.onerror)

    def delete_file(self, fname, multiple, yes_to_all):
        """Delete file"""
        if multiple:
            buttons = QMessageBox.Yes | QMessageBox.YesAll | QMessageBox.No | QMessageBox.Cancel
        else:
            buttons = QMessageBox.Yes | QMessageBox.No
        if yes_to_all is None:
            answer = QMessageBox.warning(
                self, _("Delete"), _("Do you really want " "to delete <b>%s</b>?") % osp.basename(fname), buttons
            )
            if answer == QMessageBox.No:
                return yes_to_all
            elif answer == QMessageBox.Cancel:
                return False
            elif answer == QMessageBox.YesAll:
                yes_to_all = True
        try:
            if osp.isfile(fname):
                misc.remove_file(fname)
                self.parent_widget.removed.emit(fname)
            else:
                self.remove_tree(fname)
                self.parent_widget.removed_tree.emit(fname)
            return yes_to_all
        except EnvironmentError as error:
            action_str = _("delete")
            QMessageBox.critical(
                self,
                _("Project Explorer"),
                _("<b>Unable to %s <i>%s</i></b>" "<br><br>Error message:<br>%s")
                % (action_str, fname, to_text_string(error)),
            )
        return False

    @Slot()
    def delete(self, fnames=None):
        """Delete files"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        multiple = len(fnames) > 1
        yes_to_all = None
        for fname in fnames:
            yes_to_all = self.delete_file(fname, multiple, yes_to_all)
            if yes_to_all is not None and not yes_to_all:
                # Canceled
                return

    def convert_notebook(self, fname):
        """Convert an IPython notebook to a Python script in editor"""
        try:
            script = nbexporter().from_filename(fname)[0]
        except Exception as e:
            QMessageBox.critical(
                self,
                _("Conversion error"),
                _("It was not possible to convert this " "notebook. The error is:\n\n") + to_text_string(e),
            )
            return
        self.parent_widget.sig_new_file.emit(script)

    @Slot()
    def convert_notebooks(self):
        """Convert IPython notebooks to Python scripts in editor"""
        fnames = self.get_selected_filenames()
        if not isinstance(fnames, (tuple, list)):
            fnames = [fnames]
        for fname in fnames:
            self.convert_notebook(fname)

    def rename_file(self, fname):
        """Rename file"""
        path, valid = QInputDialog.getText(self, _("Rename"), _("New name:"), QLineEdit.Normal, osp.basename(fname))
        if valid:
            path = osp.join(osp.dirname(fname), to_text_string(path))
            if path == fname:
                return
            if osp.exists(path):
                if (
                    QMessageBox.warning(
                        self,
                        _("Rename"),
                        _("Do you really want to rename <b>%s</b> and " "overwrite the existing file <b>%s</b>?")
                        % (osp.basename(fname), osp.basename(path)),
                        QMessageBox.Yes | QMessageBox.No,
                    )
                    == QMessageBox.No
                ):
                    return
            try:
                misc.rename_file(fname, path)
                self.parent_widget.renamed.emit(fname, path)
                return path
            except EnvironmentError as error:
                QMessageBox.critical(
                    self,
                    _("Rename"),
                    _("<b>Unable to rename file <i>%s</i></b>" "<br><br>Error message:<br>%s")
                    % (osp.basename(fname), to_text_string(error)),
                )

    @Slot()
    def rename(self, fnames=None):
        """Rename files"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        if not isinstance(fnames, (tuple, list)):
            fnames = [fnames]
        for fname in fnames:
            self.rename_file(fname)

    @Slot()
    def move(self, fnames=None):
        """Move files/directories"""
        if fnames is None:
            fnames = self.get_selected_filenames()
        orig = fixpath(osp.dirname(fnames[0]))
        while True:
            self.parent_widget.redirect_stdio.emit(False)
            folder = getexistingdirectory(self, _("Select directory"), orig)
            self.parent_widget.redirect_stdio.emit(True)
            if folder:
                folder = fixpath(folder)
                if folder != orig:
                    break
            else:
                return
        for fname in fnames:
            basename = osp.basename(fname)
            try:
                misc.move_file(fname, osp.join(folder, basename))
            except EnvironmentError as error:
                QMessageBox.critical(
                    self,
                    _("Error"),
                    _("<b>Unable to move <i>%s</i></b>" "<br><br>Error message:<br>%s")
                    % (basename, to_text_string(error)),
                )

    def create_new_folder(self, current_path, title, subtitle, is_package):
        """Create new folder"""
        if current_path is None:
            current_path = ""
        if osp.isfile(current_path):
            current_path = osp.dirname(current_path)
        name, valid = QInputDialog.getText(self, title, subtitle, QLineEdit.Normal, "")
        if valid:
            dirname = osp.join(current_path, to_text_string(name))
            try:
                os.mkdir(dirname)
            except EnvironmentError as error:
                QMessageBox.critical(
                    self,
                    title,
                    _("<b>Unable " "to create folder <i>%s</i></b>" "<br><br>Error message:<br>%s")
                    % (dirname, to_text_string(error)),
                )
            finally:
                if is_package:
                    fname = osp.join(dirname, "__init__.py")
                    try:
                        with open(fname, "wb") as f:
                            f.write(to_binary_string("#"))
                        return dirname
                    except EnvironmentError as error:
                        QMessageBox.critical(
                            self,
                            title,
                            _("<b>Unable " "to create file <i>%s</i></b>" "<br><br>Error message:<br>%s")
                            % (fname, to_text_string(error)),
                        )

    def new_folder(self, basedir):
        """New folder"""
        title = _("New folder")
        subtitle = _("Folder name:")
        self.create_new_folder(basedir, title, subtitle, is_package=False)

    def new_package(self, basedir):
        """New package"""
        title = _("New package")
        subtitle = _("Package name:")
        self.create_new_folder(basedir, title, subtitle, is_package=True)

    def create_new_file(self, current_path, title, filters, create_func):
        """Create new file
        Returns True if successful"""
        if current_path is None:
            current_path = ""
        if osp.isfile(current_path):
            current_path = osp.dirname(current_path)
        self.parent_widget.redirect_stdio.emit(False)
        fname, _selfilter = getsavefilename(self, title, current_path, filters)
        self.parent_widget.redirect_stdio.emit(True)
        if fname:
            try:
                create_func(fname)
                return fname
            except EnvironmentError as error:
                QMessageBox.critical(
                    self,
                    _("New file"),
                    _("<b>Unable to create file <i>%s</i>" "</b><br><br>Error message:<br>%s")
                    % (fname, to_text_string(error)),
                )

    def new_file(self, basedir):
        """New file"""
        title = _("New file")
        filters = _("All files") + " (*)"

        def create_func(fname):
            """File creation callback"""
            if osp.splitext(fname)[1] in (".py", ".pyw", ".ipy"):
                create_script(fname)
            else:
                with open(fname, "wb") as f:
                    f.write(to_binary_string(""))

        fname = self.create_new_file(basedir, title, filters, create_func)
        if fname is not None:
            self.open([fname])

    def new_module(self, basedir):
        """New module"""
        title = _("New module")
        filters = _("Python scripts") + " (*.py *.pyw *.ipy)"
        create_func = lambda fname: self.parent_widget.create_module.emit(fname)
        self.create_new_file(basedir, title, filters, create_func)

    # ----- VCS actions
    def vcs_command(self, fnames, action):
        """VCS action (commit, browse)"""
        try:
            for path in sorted(fnames):
                vcs.run_vcs_tool(path, action)
        except vcs.ActionToolNotFound as error:
            msg = _("For %s support, please install one of the<br/> " "following tools:<br/><br/>  %s") % (
                error.vcsname,
                ", ".join(error.tools),
            )
            QMessageBox.critical(
                self, _("Error"), _("""<b>Unable to find external program.</b><br><br>%s""") % to_text_string(msg)
            )

    # ----- Settings
    def get_scrollbar_position(self):
        """Return scrollbar positions"""
        return (self.horizontalScrollBar().value(), self.verticalScrollBar().value())

    def set_scrollbar_position(self, position):
        """Set scrollbar positions"""
        # Scrollbars will be restored after the expanded state
        self._scrollbar_positions = position
        if self._to_be_loaded is not None and len(self._to_be_loaded) == 0:
            self.restore_scrollbar_positions()

    def restore_scrollbar_positions(self):
        """Restore scrollbar positions once tree is loaded"""
        hor, ver = self._scrollbar_positions
        self.horizontalScrollBar().setValue(hor)
        self.verticalScrollBar().setValue(ver)

    def get_expanded_state(self):
        """Return expanded state"""
        self.save_expanded_state()
        return self.__expanded_state

    def set_expanded_state(self, state):
        """Set expanded state"""
        self.__expanded_state = state
        self.restore_expanded_state()

    def save_expanded_state(self):
        """Save all items expanded state"""
        model = self.model()
        # If model is not installed, 'model' will be None: this happens when
        # using the Project Explorer without having selected a workspace yet
        if model is not None:
            self.__expanded_state = []
            for idx in model.persistentIndexList():
                if self.isExpanded(idx):
                    self.__expanded_state.append(self.get_filename(idx))

    def restore_directory_state(self, fname):
        """Restore directory expanded state"""
        root = osp.normpath(to_text_string(fname))
        if not osp.exists(root):
            # Directory has been (re)moved outside Spyder
            return
        for basename in os.listdir(root):
            path = osp.normpath(osp.join(root, basename))
            if osp.isdir(path) and path in self.__expanded_state:
                self.__expanded_state.pop(self.__expanded_state.index(path))
                if self._to_be_loaded is None:
                    self._to_be_loaded = []
                self._to_be_loaded.append(path)
                self.setExpanded(self.get_index(path), True)
        if not self.__expanded_state and not is_pyqt46:
            self.fsmodel.directoryLoaded.disconnect(self.restore_directory_state)

    def follow_directories_loaded(self, fname):
        """Follow directories loaded during startup"""
        if self._to_be_loaded is None:
            return
        path = osp.normpath(to_text_string(fname))
        if path in self._to_be_loaded:
            self._to_be_loaded.remove(path)
        if self._to_be_loaded is not None and len(self._to_be_loaded) == 0 and not is_pyqt46:
            self.fsmodel.directoryLoaded.disconnect(self.follow_directories_loaded)
            if self._scrollbar_positions is not None:
                # The tree view need some time to render branches:
                QTimer.singleShot(50, self.restore_scrollbar_positions)

    def restore_expanded_state(self):
        """Restore all items expanded state"""
        if self.__expanded_state is not None:
            # In the old project explorer, the expanded state was a dictionnary:
            if isinstance(self.__expanded_state, list) and not is_pyqt46:
                self.fsmodel.directoryLoaded.connect(self.restore_directory_state)
                self.fsmodel.directoryLoaded.connect(self.follow_directories_loaded)
コード例 #50
0
ファイル: shell.py プロジェクト: mikofski/spyder
class ShellBaseWidget(ConsoleBaseWidget, SaveHistoryMixin):
    """
    Shell base widget
    """

    redirect_stdio = Signal(bool)
    sig_keyboard_interrupt = Signal()
    execute = Signal(str)
    append_to_history = Signal(str, str)

    def __init__(self, parent, history_filename, profile=False):
        """
        parent : specifies the parent widget
        """
        ConsoleBaseWidget.__init__(self, parent)
        SaveHistoryMixin.__init__(self)

        # Prompt position: tuple (line, index)
        self.current_prompt_pos = None
        self.new_input_line = True

        # History
        self.histidx = None
        self.hist_wholeline = False
        assert is_text_string(history_filename)
        self.history_filename = history_filename
        self.history = self.load_history()

        # Session
        self.historylog_filename = CONF.get('main', 'historylog_filename',
                                            get_conf_path('history.log'))

        # Context menu
        self.menu = None
        self.setup_context_menu()

        # Simple profiling test
        self.profile = profile

        # Buffer to increase performance of write/flush operations
        self.__buffer = []
        self.__timestamp = 0.0
        self.__flushtimer = QTimer(self)
        self.__flushtimer.setSingleShot(True)
        self.__flushtimer.timeout.connect(self.flush)

        # Give focus to widget
        self.setFocus()

        # Completion
        completion_size = CONF.get('shell_appearance', 'completion/size')
        completion_font = get_font('console')
        self.completion_widget.setup_appearance(completion_size,
                                                completion_font)
        # Cursor width
        self.setCursorWidth(CONF.get('shell_appearance', 'cursor/width'))

    def toggle_wrap_mode(self, enable):
        """Enable/disable wrap mode"""
        self.set_wrap_mode('character' if enable else None)

    def set_font(self, font):
        """Set shell styles font"""
        self.setFont(font)
        self.set_pythonshell_font(font)
        cursor = self.textCursor()
        cursor.select(QTextCursor.Document)
        charformat = QTextCharFormat()
        charformat.setFontFamily(font.family())
        charformat.setFontPointSize(font.pointSize())
        cursor.mergeCharFormat(charformat)

    #------ Context menu
    def setup_context_menu(self):
        """Setup shell context menu"""
        self.menu = QMenu(self)
        self.cut_action = create_action(self,
                                        _("Cut"),
                                        shortcut=keybinding('Cut'),
                                        icon=ima.icon('editcut'),
                                        triggered=self.cut)
        self.copy_action = create_action(self,
                                         _("Copy"),
                                         shortcut=keybinding('Copy'),
                                         icon=ima.icon('editcopy'),
                                         triggered=self.copy)
        paste_action = create_action(self,
                                     _("Paste"),
                                     shortcut=keybinding('Paste'),
                                     icon=ima.icon('editpaste'),
                                     triggered=self.paste)
        save_action = create_action(self,
                                    _("Save history log..."),
                                    icon=ima.icon('filesave'),
                                    tip=_(
                                        "Save current history log (i.e. all "
                                        "inputs and outputs) in a text file"),
                                    triggered=self.save_historylog)
        self.delete_action = create_action(self,
                                           _("Delete"),
                                           shortcut=keybinding('Delete'),
                                           icon=ima.icon('editdelete'),
                                           triggered=self.delete)
        selectall_action = create_action(self,
                                         _("Select All"),
                                         shortcut=keybinding('SelectAll'),
                                         icon=ima.icon('selectall'),
                                         triggered=self.selectAll)
        add_actions(
            self.menu,
            (self.cut_action, self.copy_action, paste_action,
             self.delete_action, None, selectall_action, None, save_action))

    def contextMenuEvent(self, event):
        """Reimplement Qt method"""
        state = self.has_selected_text()
        self.copy_action.setEnabled(state)
        self.cut_action.setEnabled(state)
        self.delete_action.setEnabled(state)
        self.menu.popup(event.globalPos())
        event.accept()

    #------ Input buffer
    def get_current_line_from_cursor(self):
        return self.get_text('cursor', 'eof')

    def _select_input(self):
        """Select current line (without selecting console prompt)"""
        line, index = self.get_position('eof')
        if self.current_prompt_pos is None:
            pline, pindex = line, index
        else:
            pline, pindex = self.current_prompt_pos
        self.setSelection(pline, pindex, line, index)

    @Slot()
    def clear_line(self):
        """Clear current line (without clearing console prompt)"""
        if self.current_prompt_pos is not None:
            self.remove_text(self.current_prompt_pos, 'eof')

    @Slot()
    def clear_terminal(self):
        """
        Clear terminal window
        Child classes reimplement this method to write prompt
        """
        self.clear()

    # The buffer being edited
    def _set_input_buffer(self, text):
        """Set input buffer"""
        if self.current_prompt_pos is not None:
            self.replace_text(self.current_prompt_pos, 'eol', text)
        else:
            self.insert(text)
        self.set_cursor_position('eof')

    def _get_input_buffer(self):
        """Return input buffer"""
        input_buffer = ''
        if self.current_prompt_pos is not None:
            input_buffer = self.get_text(self.current_prompt_pos, 'eol')
            input_buffer = input_buffer.replace(os.linesep, '\n')
        return input_buffer

    input_buffer = Property("QString", _get_input_buffer, _set_input_buffer)

    #------ Prompt
    def new_prompt(self, prompt):
        """
        Print a new prompt and save its (line, index) position
        """
        if self.get_cursor_line_column()[1] != 0:
            self.write('\n')
        self.write(prompt, prompt=True)
        # now we update our cursor giving end of prompt
        self.current_prompt_pos = self.get_position('cursor')
        self.ensureCursorVisible()
        self.new_input_line = False

    def check_selection(self):
        """
        Check if selected text is r/w,
        otherwise remove read-only parts of selection
        """
        if self.current_prompt_pos is None:
            self.set_cursor_position('eof')
        else:
            self.truncate_selection(self.current_prompt_pos)

    #------ Copy / Keyboard interrupt
    @Slot()
    def copy(self):
        """Copy text to clipboard... or keyboard interrupt"""
        if self.has_selected_text():
            ConsoleBaseWidget.copy(self)
        elif not sys.platform == 'darwin':
            self.interrupt()

    def interrupt(self):
        """Keyboard interrupt"""
        self.sig_keyboard_interrupt.emit()

    @Slot()
    def cut(self):
        """Cut text"""
        self.check_selection()
        if self.has_selected_text():
            ConsoleBaseWidget.cut(self)

    @Slot()
    def delete(self):
        """Remove selected text"""
        self.check_selection()
        if self.has_selected_text():
            ConsoleBaseWidget.remove_selected_text(self)

    @Slot()
    def save_historylog(self):
        """Save current history log (all text in console)"""
        title = _("Save history log")
        self.redirect_stdio.emit(False)
        filename, _selfilter = getsavefilename(
            self, title, self.historylog_filename,
            "%s (*.log)" % _("History logs"))
        self.redirect_stdio.emit(True)
        if filename:
            filename = osp.normpath(filename)
            try:
                encoding.write(to_text_string(self.get_text_with_eol()),
                               filename)
                self.historylog_filename = filename
                CONF.set('main', 'historylog_filename', filename)
            except EnvironmentError as error:
                QMessageBox.critical(
                    self, title,
                    _("<b>Unable to save file '%s'</b>"
                      "<br><br>Error message:<br>%s") %
                    (osp.basename(filename), to_text_string(error)))

    #------ Basic keypress event handler
    def on_enter(self, command):
        """on_enter"""
        self.execute_command(command)

    def execute_command(self, command):
        self.execute.emit(command)
        self.add_to_history(command)
        self.new_input_line = True

    def on_new_line(self):
        """On new input line"""
        self.set_cursor_position('eof')
        self.current_prompt_pos = self.get_position('cursor')
        self.new_input_line = False

    @Slot()
    def paste(self):
        """Reimplemented slot to handle multiline paste action"""
        if self.new_input_line:
            self.on_new_line()
        ConsoleBaseWidget.paste(self)

    def keyPressEvent(self, event):
        """
        Reimplement Qt Method
        Basic keypress event handler
        (reimplemented in InternalShell to add more sophisticated features)
        """
        if self.preprocess_keyevent(event):
            # Event was accepted in self.preprocess_keyevent
            return
        self.postprocess_keyevent(event)

    def preprocess_keyevent(self, event):
        """Pre-process keypress event:
        return True if event is accepted, false otherwise"""
        # Copy must be done first to be able to copy read-only text parts
        # (otherwise, right below, we would remove selection
        #  if not on current line)
        ctrl = event.modifiers() & Qt.ControlModifier
        meta = event.modifiers() & Qt.MetaModifier  # meta=ctrl in OSX
        if event.key() == Qt.Key_C and \
          ((Qt.MetaModifier | Qt.ControlModifier) & event.modifiers()):
            if meta and sys.platform == 'darwin':
                self.interrupt()
            elif ctrl:
                self.copy()
            event.accept()
            return True

        if self.new_input_line and ( len(event.text()) or event.key() in \
           (Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right) ):
            self.on_new_line()

        return False

    def postprocess_keyevent(self, event):
        """Post-process keypress event:
        in InternalShell, this is method is called when shell is ready"""
        event, text, key, ctrl, shift = restore_keyevent(event)

        # Is cursor on the last line? and after prompt?
        if len(text):
            #XXX: Shouldn't it be: `if len(unicode(text).strip(os.linesep))` ?
            if self.has_selected_text():
                self.check_selection()
            self.restrict_cursor_position(self.current_prompt_pos, 'eof')

        cursor_position = self.get_position('cursor')

        if key in (Qt.Key_Return, Qt.Key_Enter):
            if self.is_cursor_on_last_line():
                self._key_enter()
            # add and run selection
            else:
                self.insert_text(self.get_selected_text(), at_end=True)

        elif key == Qt.Key_Insert and not shift and not ctrl:
            self.setOverwriteMode(not self.overwriteMode())

        elif key == Qt.Key_Delete:
            if self.has_selected_text():
                self.check_selection()
                self.remove_selected_text()
            elif self.is_cursor_on_last_line():
                self.stdkey_clear()

        elif key == Qt.Key_Backspace:
            self._key_backspace(cursor_position)

        elif key == Qt.Key_Tab:
            self._key_tab()

        elif key == Qt.Key_Space and ctrl:
            self._key_ctrl_space()

        elif key == Qt.Key_Left:
            if self.current_prompt_pos == cursor_position:
                # Avoid moving cursor on prompt
                return
            method = self.extend_selection_to_next if shift \
                     else self.move_cursor_to_next
            method('word' if ctrl else 'character', direction='left')

        elif key == Qt.Key_Right:
            if self.is_cursor_at_end():
                return
            method = self.extend_selection_to_next if shift \
                     else self.move_cursor_to_next
            method('word' if ctrl else 'character', direction='right')

        elif (key == Qt.Key_Home) or ((key == Qt.Key_Up) and ctrl):
            self._key_home(shift, ctrl)

        elif (key == Qt.Key_End) or ((key == Qt.Key_Down) and ctrl):
            self._key_end(shift, ctrl)

        elif key == Qt.Key_Up:
            if not self.is_cursor_on_last_line():
                self.set_cursor_position('eof')
            y_cursor = self.get_coordinates(cursor_position)[1]
            y_prompt = self.get_coordinates(self.current_prompt_pos)[1]
            if y_cursor > y_prompt:
                self.stdkey_up(shift)
            else:
                self.browse_history(backward=True)

        elif key == Qt.Key_Down:
            if not self.is_cursor_on_last_line():
                self.set_cursor_position('eof')
            y_cursor = self.get_coordinates(cursor_position)[1]
            y_end = self.get_coordinates('eol')[1]
            if y_cursor < y_end:
                self.stdkey_down(shift)
            else:
                self.browse_history(backward=False)

        elif key in (Qt.Key_PageUp, Qt.Key_PageDown):
            #XXX: Find a way to do this programmatically instead of calling
            # widget keyhandler (this won't work if the *event* is coming from
            # the event queue - i.e. if the busy buffer is ever implemented)
            ConsoleBaseWidget.keyPressEvent(self, event)

        elif key == Qt.Key_Escape and shift:
            self.clear_line()

        elif key == Qt.Key_Escape:
            self._key_escape()

        elif key == Qt.Key_L and ctrl:
            self.clear_terminal()

        elif key == Qt.Key_V and ctrl:
            self.paste()

        elif key == Qt.Key_X and ctrl:
            self.cut()

        elif key == Qt.Key_Z and ctrl:
            self.undo()

        elif key == Qt.Key_Y and ctrl:
            self.redo()

        elif key == Qt.Key_A and ctrl:
            self.selectAll()

        elif key == Qt.Key_Question and not self.has_selected_text():
            self._key_question(text)

        elif key == Qt.Key_ParenLeft and not self.has_selected_text():
            self._key_parenleft(text)

        elif key == Qt.Key_Period and not self.has_selected_text():
            self._key_period(text)

        elif len(text) and not self.isReadOnly():
            self.hist_wholeline = False
            self.insert_text(text)
            self._key_other(text)

        else:
            # Let the parent widget handle the key press event
            ConsoleBaseWidget.keyPressEvent(self, event)

    #------ Key handlers
    def _key_enter(self):
        command = self.input_buffer
        self.insert_text('\n', at_end=True)
        self.on_enter(command)
        self.flush()

    def _key_other(self, text):
        raise NotImplementedError

    def _key_backspace(self, cursor_position):
        raise NotImplementedError

    def _key_tab(self):
        raise NotImplementedError

    def _key_ctrl_space(self):
        raise NotImplementedError

    def _key_home(self, shift, ctrl):
        if self.is_cursor_on_last_line():
            self.stdkey_home(shift, ctrl, self.current_prompt_pos)

    def _key_end(self, shift, ctrl):
        if self.is_cursor_on_last_line():
            self.stdkey_end(shift, ctrl)

    def _key_pageup(self):
        raise NotImplementedError

    def _key_pagedown(self):
        raise NotImplementedError

    def _key_escape(self):
        raise NotImplementedError

    def _key_question(self, text):
        raise NotImplementedError

    def _key_parenleft(self, text):
        raise NotImplementedError

    def _key_period(self, text):
        raise NotImplementedError

    #------ History Management
    def load_history(self):
        """Load history from a .py file in user home directory"""
        if osp.isfile(self.history_filename):
            rawhistory, _ = encoding.readlines(self.history_filename)
            rawhistory = [line.replace('\n', '') for line in rawhistory]
            if rawhistory[1] != self.INITHISTORY[1]:
                rawhistory[1] = self.INITHISTORY[1]
        else:
            rawhistory = self.INITHISTORY
        history = [line for line in rawhistory \
                   if line and not line.startswith('#')]

        # Truncating history to X entries:
        while len(history) >= CONF.get('historylog', 'max_entries'):
            del history[0]
            while rawhistory[0].startswith('#'):
                del rawhistory[0]
            del rawhistory[0]
        # Saving truncated history:
        encoding.writelines(rawhistory, self.history_filename)
        return history

    def browse_history(self, backward):
        """Browse history"""
        if self.is_cursor_before('eol') and self.hist_wholeline:
            self.hist_wholeline = False
        tocursor = self.get_current_line_to_cursor()
        text, self.histidx = self.__find_in_history(tocursor, self.histidx,
                                                    backward)
        if text is not None:
            if self.hist_wholeline:
                self.clear_line()
                self.insert_text(text)
            else:
                cursor_position = self.get_position('cursor')
                # Removing text from cursor to the end of the line
                self.remove_text('cursor', 'eol')
                # Inserting history text
                self.insert_text(text)
                self.set_cursor_position(cursor_position)

    def __find_in_history(self, tocursor, start_idx, backward):
        """Find text 'tocursor' in history, from index 'start_idx'"""
        if start_idx is None:
            start_idx = len(self.history)
        # Finding text in history
        step = -1 if backward else 1
        idx = start_idx
        if len(tocursor) == 0 or self.hist_wholeline:
            idx += step
            if idx >= len(self.history) or len(self.history) == 0:
                return "", len(self.history)
            elif idx < 0:
                idx = 0
            self.hist_wholeline = True
            return self.history[idx], idx
        else:
            for index in range(len(self.history)):
                idx = (start_idx + step * (index + 1)) % len(self.history)
                entry = self.history[idx]
                if entry.startswith(tocursor):
                    return entry[len(tocursor):], idx
            else:
                return None, start_idx

    #------ Simulation standards input/output
    def write_error(self, text):
        """Simulate stderr"""
        self.flush()
        self.write(text, flush=True, error=True)
        if DEBUG:
            STDERR.write(text)

    def write(self, text, flush=False, error=False, prompt=False):
        """Simulate stdout and stderr"""
        if prompt:
            self.flush()
        if not is_string(text):
            # This test is useful to discriminate QStrings from decoded str
            text = to_text_string(text)
        self.__buffer.append(text)
        ts = time.time()
        if flush or prompt:
            self.flush(error=error, prompt=prompt)
        elif ts - self.__timestamp > 0.05:
            self.flush(error=error)
            self.__timestamp = ts
            # Timer to flush strings cached by last write() operation in series
            self.__flushtimer.start(50)

    def flush(self, error=False, prompt=False):
        """Flush buffer, write text to console"""
        # Fix for Issue 2452
        if PY3:
            try:
                text = "".join(self.__buffer)
            except TypeError:
                text = b"".join(self.__buffer)
                try:
                    text = text.decode(locale.getdefaultlocale()[1])
                except:
                    pass
        else:
            text = "".join(self.__buffer)

        self.__buffer = []
        self.insert_text(text, at_end=True, error=error, prompt=prompt)
        QCoreApplication.processEvents()
        self.repaint()
        # Clear input buffer:
        self.new_input_line = True

    #------ Text Insertion
    def insert_text(self, text, at_end=False, error=False, prompt=False):
        """
        Insert text at the current cursor position
        or at the end of the command line
        """
        if at_end:
            # Insert text at the end of the command line
            self.append_text_to_shell(text, error, prompt)
        else:
            # Insert text at current cursor position
            ConsoleBaseWidget.insert_text(self, text)

    #------ Re-implemented Qt Methods
    def focusNextPrevChild(self, next):
        """
        Reimplemented to stop Tab moving to the next window
        """
        if next:
            return False
        return ConsoleBaseWidget.focusNextPrevChild(self, next)

    #------ Drag and drop
    def dragEnterEvent(self, event):
        """Drag and Drop - Enter event"""
        event.setAccepted(event.mimeData().hasFormat("text/plain"))

    def dragMoveEvent(self, event):
        """Drag and Drop - Move event"""
        if (event.mimeData().hasFormat("text/plain")):
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        """Drag and Drop - Drop event"""
        if (event.mimeData().hasFormat("text/plain")):
            text = to_text_string(event.mimeData().text())
            if self.new_input_line:
                self.on_new_line()
            self.insert_text(text, at_end=True)
            self.setFocus()
            event.setDropAction(Qt.MoveAction)
            event.accept()
        else:
            event.ignore()

    def drop_pathlist(self, pathlist):
        """Drop path list"""
        raise NotImplementedError
コード例 #51
0
 def get_plugin_actions(self):
     """Return a list of actions related to plugin"""
     quit_action = create_action(self, _("&Quit"),
                                 icon=ima.icon('exit'), 
                                 tip=_("Quit"),
                                 triggered=self.quit)
     self.register_shortcut(quit_action, "_", "Quit", "Ctrl+Q")
     run_action = create_action(self, _("&Run..."), None,
                         ima.icon('run_small'),
                         _("Run a Python script"),
                         triggered=self.run_script)
     environ_action = create_action(self,
                         _("Environment variables..."),
                         icon=ima.icon('environ'),
                         tip=_("Show and edit environment variables"
                                     " (for current session)"),
                         triggered=self.show_env)
     syspath_action = create_action(self,
                         _("Show sys.path contents..."),
                         icon=ima.icon('syspath'),
                         tip=_("Show (read-only) sys.path"),
                         triggered=self.show_syspath)
     buffer_action = create_action(self,
                         _("Buffer..."), None,
                         tip=_("Set maximum line count"),
                         triggered=self.change_max_line_count)
     font_action = create_action(self,
                         _("&Font..."), None,
                         ima.icon('font'), _("Set shell font style"),
                         triggered=self.change_font)
     exteditor_action = create_action(self,
                         _("External editor path..."), None, None,
                         _("Set external editor executable path"),
                         triggered=self.change_exteditor)
     wrap_action = create_action(self,
                         _("Wrap lines"),
                         toggled=self.toggle_wrap_mode)
     wrap_action.setChecked(self.get_option('wrap'))
     calltips_action = create_action(self, _("Display balloon tips"),
         toggled=self.toggle_calltips)
     calltips_action.setChecked(self.get_option('calltips'))
     codecompletion_action = create_action(self,
                                       _("Automatic code completion"),
                                       toggled=self.toggle_codecompletion)
     codecompletion_action.setChecked(self.get_option('codecompletion/auto'))
     codecompenter_action = create_action(self,
                                 _("Enter key selects completion"),
                                 toggled=self.toggle_codecompletion_enter)
     codecompenter_action.setChecked(self.get_option(
                                                 'codecompletion/enter_key'))
     
     option_menu = QMenu(_('Internal console settings'), self)
     option_menu.setIcon(ima.icon('tooloptions'))
     add_actions(option_menu, (buffer_action, font_action, wrap_action,
                               calltips_action, codecompletion_action,
                               codecompenter_action, exteditor_action))
                 
     plugin_actions = [None, run_action, environ_action, syspath_action,
                       option_menu, None, quit_action]
     
     # Add actions to context menu
     add_actions(self.shell.menu, plugin_actions)
     
     return plugin_actions
コード例 #52
0
class OneColumnTree(QTreeWidget):
    """One-column tree widget with context menu, ..."""
    def __init__(self, parent):
        QTreeWidget.__init__(self, parent)
        self.setItemsExpandable(True)
        self.setColumnCount(1)
        self.itemActivated.connect(self.activated)
        self.itemClicked.connect(self.clicked)
        # Setup context menu
        self.menu = QMenu(self)
        self.collapse_all_action = None
        self.collapse_selection_action = None
        self.expand_all_action = None
        self.expand_selection_action = None
        self.common_actions = self.setup_common_actions()

        self.__expanded_state = None

        self.itemSelectionChanged.connect(self.item_selection_changed)
        self.item_selection_changed()

    def activated(self, item):
        """Double-click event"""
        raise NotImplementedError

    def clicked(self, item):
        pass

    def set_title(self, title):
        self.setHeaderLabels([title])

    def setup_common_actions(self):
        """Setup context menu common actions"""
        self.collapse_all_action = create_action(self,
                                                 text=_('Collapse all'),
                                                 icon=get_icon('collapse.png'),
                                                 triggered=self.collapseAll)
        self.expand_all_action = create_action(self,
                                               text=_('Expand all'),
                                               icon=get_icon('expand.png'),
                                               triggered=self.expandAll)
        self.restore_action = create_action(
            self,
            text=_('Restore'),
            tip=_('Restore original tree layout'),
            icon=get_icon('restore.png'),
            triggered=self.restore)
        self.collapse_selection_action = create_action(
            self,
            text=_('Collapse selection'),
            icon=get_icon('collapse_selection.png'),
            triggered=self.collapse_selection)
        self.expand_selection_action = create_action(
            self,
            text=_('Expand selection'),
            icon=get_icon('expand_selection.png'),
            triggered=self.expand_selection)
        return [
            self.collapse_all_action, self.expand_all_action,
            self.restore_action, None, self.collapse_selection_action,
            self.expand_selection_action
        ]

    def update_menu(self):
        self.menu.clear()
        items = self.selectedItems()
        actions = self.get_actions_from_items(items)
        if actions:
            actions.append(None)
        actions += self.common_actions
        add_actions(self.menu, actions)

    def get_actions_from_items(self, items):
        # Right here: add other actions if necessary
        # (reimplement this method)
        return []

    @Slot()
    def restore(self):
        self.collapseAll()
        for item in self.get_top_level_items():
            self.expandItem(item)

    def is_item_expandable(self, item):
        """To be reimplemented in child class
        See example in project explorer widget"""
        return True

    def __expand_item(self, item):
        if self.is_item_expandable(item):
            self.expandItem(item)
            for index in range(item.childCount()):
                child = item.child(index)
                self.__expand_item(child)

    @Slot()
    def expand_selection(self):
        items = self.selectedItems()
        if not items:
            items = self.get_top_level_items()
        for item in items:
            self.__expand_item(item)
        if items:
            self.scrollToItem(items[0])

    def __collapse_item(self, item):
        self.collapseItem(item)
        for index in range(item.childCount()):
            child = item.child(index)
            self.__collapse_item(child)

    @Slot()
    def collapse_selection(self):
        items = self.selectedItems()
        if not items:
            items = self.get_top_level_items()
        for item in items:
            self.__collapse_item(item)
        if items:
            self.scrollToItem(items[0])

    def item_selection_changed(self):
        """Item selection has changed"""
        is_selection = len(self.selectedItems()) > 0
        self.expand_selection_action.setEnabled(is_selection)
        self.collapse_selection_action.setEnabled(is_selection)

    def get_top_level_items(self):
        """Iterate over top level items"""
        return [
            self.topLevelItem(_i) for _i in range(self.topLevelItemCount())
        ]

    def get_items(self):
        """Return items (excluding top level items)"""
        itemlist = []

        def add_to_itemlist(item):
            for index in range(item.childCount()):
                citem = item.child(index)
                itemlist.append(citem)
                add_to_itemlist(citem)

        for tlitem in self.get_top_level_items():
            add_to_itemlist(tlitem)
        return itemlist

    def get_scrollbar_position(self):
        return (self.horizontalScrollBar().value(),
                self.verticalScrollBar().value())

    def set_scrollbar_position(self, position):
        hor, ver = position
        self.horizontalScrollBar().setValue(hor)
        self.verticalScrollBar().setValue(ver)

    def get_expanded_state(self):
        self.save_expanded_state()
        return self.__expanded_state

    def set_expanded_state(self, state):
        self.__expanded_state = state
        self.restore_expanded_state()

    def save_expanded_state(self):
        """Save all items expanded state"""
        self.__expanded_state = {}

        def add_to_state(item):
            user_text = get_item_user_text(item)
            self.__expanded_state[hash(user_text)] = item.isExpanded()

        def browse_children(item):
            add_to_state(item)
            for index in range(item.childCount()):
                citem = item.child(index)
                user_text = get_item_user_text(citem)
                self.__expanded_state[hash(user_text)] = citem.isExpanded()
                browse_children(citem)

        for tlitem in self.get_top_level_items():
            browse_children(tlitem)

    def restore_expanded_state(self):
        """Restore all items expanded state"""
        if self.__expanded_state is None:
            return
        for item in self.get_items() + self.get_top_level_items():
            user_text = get_item_user_text(item)
            is_expanded = self.__expanded_state.get(hash(user_text))
            if is_expanded is not None:
                item.setExpanded(is_expanded)

    def sort_top_level_items(self, key):
        """Sorting tree wrt top level items"""
        self.save_expanded_state()
        items = sorted([
            self.takeTopLevelItem(0)
            for index in range(self.topLevelItemCount())
        ],
                       key=key)
        for index, item in enumerate(items):
            self.insertTopLevelItem(index, item)
        self.restore_expanded_state()

    def contextMenuEvent(self, event):
        """Override Qt method"""
        self.update_menu()
        self.menu.popup(event.globalPos())
コード例 #53
0
ファイル: tabs.py プロジェクト: MarvinLiu0810/spyder
class BaseTabs(QTabWidget):
    """TabWidget with context menu and corner widgets"""
    sig_close_tab = Signal(int)
    
    def __init__(self, parent, actions=None, menu=None,
                 corner_widgets=None, menu_use_tooltips=False):
        QTabWidget.__init__(self, parent)
        self.setUsesScrollButtons(True)

        # To style tabs on Mac
        if sys.platform == 'darwin':
            self.setObjectName('plugin-tab')

        self.corner_widgets = {}
        self.menu_use_tooltips = menu_use_tooltips
        
        if menu is None:
            self.menu = QMenu(self)
            if actions:
                add_actions(self.menu, actions)
        else:
            self.menu = menu
            
        # Corner widgets
        if corner_widgets is None:
            corner_widgets = {}
        corner_widgets.setdefault(Qt.TopLeftCorner, [])
        corner_widgets.setdefault(Qt.TopRightCorner, [])
        self.browse_button = create_toolbutton(self,
                                          icon=ima.icon('browse_tab'),
                                          tip=_("Browse tabs"))
        self.browse_tabs_menu = QMenu(self)
        self.browse_button.setMenu(self.browse_tabs_menu)
        self.browse_button.setPopupMode(self.browse_button.InstantPopup)
        self.browse_tabs_menu.aboutToShow.connect(self.update_browse_tabs_menu)
        corner_widgets[Qt.TopLeftCorner] += [self.browse_button]

        self.set_corner_widgets(corner_widgets)
        
    def update_browse_tabs_menu(self):
        """Update browse tabs menu"""
        self.browse_tabs_menu.clear()
        names = []
        dirnames = []
        for index in range(self.count()):
            if self.menu_use_tooltips:
                text = to_text_string(self.tabToolTip(index))
            else:
                text = to_text_string(self.tabText(index))
            names.append(text)
            if osp.isfile(text):
                # Testing if tab names are filenames
                dirnames.append(osp.dirname(text))
        offset = None
        
        # If tab names are all filenames, removing common path:
        if len(names) == len(dirnames):
            common = get_common_path(dirnames)
            if common is None:
                offset = None
            else:
                offset = len(common)+1
                if offset <= 3:
                    # Common path is not a path but a drive letter...
                    offset = None
                
        for index, text in enumerate(names):
            tab_action = create_action(self, text[offset:],
                                       icon=self.tabIcon(index),
                                       toggled=lambda state, index=index:
                                               self.setCurrentIndex(index),
                                       tip=self.tabToolTip(index))
            tab_action.setChecked(index == self.currentIndex())
            self.browse_tabs_menu.addAction(tab_action)
        
    def set_corner_widgets(self, corner_widgets):
        """
        Set tabs corner widgets
        corner_widgets: dictionary of (corner, widgets)
        corner: Qt.TopLeftCorner or Qt.TopRightCorner
        widgets: list of widgets (may contains integers to add spacings)
        """
        assert isinstance(corner_widgets, dict)
        assert all(key in (Qt.TopLeftCorner, Qt.TopRightCorner)
                   for key in corner_widgets)
        self.corner_widgets.update(corner_widgets)
        for corner, widgets in list(self.corner_widgets.items()):
            cwidget = QWidget()
            cwidget.hide()
            prev_widget = self.cornerWidget(corner)
            if prev_widget:
                prev_widget.close()
            self.setCornerWidget(cwidget, corner)
            clayout = QHBoxLayout()
            clayout.setContentsMargins(0, 0, 0, 0)
            for widget in widgets:
                if isinstance(widget, int):
                    clayout.addSpacing(widget)
                else:
                    clayout.addWidget(widget)
            cwidget.setLayout(clayout)
            cwidget.show()
            
    def add_corner_widgets(self, widgets, corner=Qt.TopRightCorner):
        self.set_corner_widgets({corner:
                                 self.corner_widgets.get(corner, [])+widgets})
        
    def contextMenuEvent(self, event):
        """Override Qt method"""
        if self.menu:
            self.menu.popup(event.globalPos())
            
    def mousePressEvent(self, event):
        """Override Qt method"""
        if event.button() == Qt.MidButton:
            index = self.tabBar().tabAt(event.pos())
            if index >= 0:
                self.sig_close_tab.emit(index)
                event.accept()
                return
        QTabWidget.mousePressEvent(self, event)
        
    def keyPressEvent(self, event):
        """Override Qt method"""
        ctrl = event.modifiers() & Qt.ControlModifier
        key = event.key()
        handled = False
        if ctrl and self.count() > 0:
            index = self.currentIndex()
            if key == Qt.Key_PageUp:
                if index > 0:
                    self.setCurrentIndex(index - 1)
                else:
                    self.setCurrentIndex(self.count() - 1)
                handled = True
            elif key == Qt.Key_PageDown:
                if index < self.count() - 1:
                    self.setCurrentIndex(index + 1)
                else:
                    self.setCurrentIndex(0)
                handled = True
        if not handled:
            QTabWidget.keyPressEvent(self, event)
        
    def set_close_function(self, func):
        """Setting Tabs close function
        None -> tabs are not closable"""
        state = func is not None
        if state:
            self.sig_close_tab.connect(func)
        try:
            # Assuming Qt >= 4.5
            QTabWidget.setTabsClosable(self, state)
            self.tabCloseRequested.connect(func)
        except AttributeError:
            # Workaround for Qt < 4.5
            close_button = create_toolbutton(self, triggered=func,
                                             icon=ima.icon('fileclose'),
                                             tip=_("Close current tab"))
            self.setCornerWidget(close_button if state else None)
コード例 #54
0
    def __init__(self, parent):
        SpyderPluginWidget.__init__(self, parent)

        self.internal_shell = None

        # Initialize plugin
        self.initialize_plugin()

        self.no_doc_string = _("No documentation available")

        self._last_console_cb = None
        self._last_editor_cb = None

        self.set_default_color_scheme()

        self.plain_text = PlainText(self)
        self.rich_text = RichText(self)

        color_scheme = get_color_scheme(self.get_option('color_scheme_name'))
        self.set_plain_text_font(self.get_plugin_font(), color_scheme)
        self.plain_text.editor.toggle_wrap_mode(self.get_option('wrap'))

        # Add entries to read-only editor context-menu
        font_action = create_action(self,
                                    _("&Font..."),
                                    None,
                                    'font.png',
                                    _("Set font style"),
                                    triggered=self.change_font)
        self.wrap_action = create_action(self,
                                         _("Wrap lines"),
                                         toggled=self.toggle_wrap_mode)
        self.wrap_action.setChecked(self.get_option('wrap'))
        self.plain_text.editor.readonly_menu.addSeparator()
        add_actions(self.plain_text.editor.readonly_menu,
                    (font_action, self.wrap_action))

        self.set_rich_text_font(self.get_plugin_font('rich_text'))

        self.shell = None

        self.external_console = None

        # locked = disable link with Console
        self.locked = False
        self._last_texts = [None, None]
        self._last_editor_doc = None

        # Object name
        layout_edit = QHBoxLayout()
        layout_edit.setContentsMargins(0, 0, 0, 0)
        txt = _("Source")
        if sys.platform == 'darwin':
            source_label = QLabel("  " + txt)
        else:
            source_label = QLabel(txt)
        layout_edit.addWidget(source_label)
        self.source_combo = QComboBox(self)
        self.source_combo.addItems([_("Console"), _("Editor")])
        self.connect(self.source_combo, SIGNAL('currentIndexChanged(int)'),
                     self.source_changed)
        if (not programs.is_module_installed('rope')
                and not programs.is_module_installed('jedi', '>=0.7.0')):
            self.source_combo.hide()
            source_label.hide()
        layout_edit.addWidget(self.source_combo)
        layout_edit.addSpacing(10)
        layout_edit.addWidget(QLabel(_("Object")))
        self.combo = ObjectComboBox(self)
        layout_edit.addWidget(self.combo)
        self.object_edit = QLineEdit(self)
        self.object_edit.setReadOnly(True)
        layout_edit.addWidget(self.object_edit)
        self.combo.setMaxCount(self.get_option('max_history_entries'))
        self.combo.addItems(self.load_history())
        self.combo.setItemText(0, '')
        self.connect(self.combo, SIGNAL("valid(bool)"),
                     lambda valid: self.force_refresh())

        # Plain text docstring option
        self.docstring = True
        self.rich_help = sphinxify is not None \
                         and self.get_option('rich_mode', True)
        self.plain_text_action = create_action(self,
                                               _("Plain Text"),
                                               toggled=self.toggle_plain_text)

        # Source code option
        self.show_source_action = create_action(
            self, _("Show Source"), toggled=self.toggle_show_source)

        # Rich text option
        self.rich_text_action = create_action(self,
                                              _("Rich Text"),
                                              toggled=self.toggle_rich_text)

        # Add the help actions to an exclusive QActionGroup
        help_actions = QActionGroup(self)
        help_actions.setExclusive(True)
        help_actions.addAction(self.plain_text_action)
        help_actions.addAction(self.rich_text_action)

        # Automatic import option
        self.auto_import_action = create_action(
            self, _("Automatic import"), toggled=self.toggle_auto_import)
        auto_import_state = self.get_option('automatic_import')
        self.auto_import_action.setChecked(auto_import_state)

        # Lock checkbox
        self.locked_button = create_toolbutton(self,
                                               triggered=self.toggle_locked)
        layout_edit.addWidget(self.locked_button)
        self._update_lock_icon()

        # Option menu
        options_button = create_toolbutton(self,
                                           text=_("Options"),
                                           icon=get_icon('tooloptions.png'))
        options_button.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self)
        add_actions(menu, [
            self.rich_text_action, self.plain_text_action,
            self.show_source_action, None, self.auto_import_action
        ])
        options_button.setMenu(menu)
        layout_edit.addWidget(options_button)

        if self.rich_help:
            self.switch_to_rich_text()
        else:
            self.switch_to_plain_text()
        self.plain_text_action.setChecked(not self.rich_help)
        self.rich_text_action.setChecked(self.rich_help)
        self.rich_text_action.setEnabled(sphinxify is not None)
        self.source_changed()

        # Main layout
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addLayout(layout_edit)
        layout.addWidget(self.plain_text)
        layout.addWidget(self.rich_text)
        self.setLayout(layout)

        # Add worker thread for handling rich text rendering
        if sphinxify is None:
            self._sphinx_thread = None
        else:
            self._sphinx_thread = SphinxThread(
                html_text_no_doc=warning(self.no_doc_string))
            self.connect(self._sphinx_thread, SIGNAL('html_ready(QString)'),
                         self._on_sphinx_thread_html_ready)
            self.connect(self._sphinx_thread, SIGNAL('error_msg(QString)'),
                         self._on_sphinx_thread_error_msg)

        # Render internal links
        view = self.rich_text.webview
        view.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
        view.linkClicked.connect(self.handle_link_clicks)

        self._starting_up = True