Exemple #1
0
class PydocServer(QThread):
    """Pydoc server"""
    server_started = Signal()

    def __init__(self, port=7464):
        QThread.__init__(self)
        self.port = port
        self.server = None
        self.complete = False

    def run(self):
        import pydoc
        if PY3:
            # Python 3
            self.callback(pydoc._start_server(pydoc._url_handler, self.port))
        else:
            # Python 2
            pydoc.serve(self.port, self.callback, self.completer)

    def callback(self, server):
        self.server = server
        self.server_started.emit()

    def completer(self):
        self.complete = True

    def quit_server(self):
        if PY3:
            # Python 3
            if self.server.serving:
                self.server.stop()
        else:
            # Python 2
            self.server.quit = 1
Exemple #2
0
class SysOutput(QObject):
    """Handle standard I/O queue"""
    data_avail = Signal()

    def __init__(self):
        QObject.__init__(self)
        self.queue = []
        self.lock = threading.Lock()

    def write(self, val):
        self.lock.acquire()
        self.queue.append(val)
        self.lock.release()
        self.data_avail.emit()

    def empty_queue(self):
        self.lock.acquire()
        s = "".join(self.queue)
        self.queue = []
        self.lock.release()
        return s

    # We need to add this method to fix Issue 1789
    def flush(self):
        pass
Exemple #3
0
class FormComboWidget(QWidget):
    update_buttons = Signal()
    
    def __init__(self, datalist, comment="", parent=None):
        QWidget.__init__(self, parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.combobox = QComboBox()
        layout.addWidget(self.combobox)
        
        self.stackwidget = QStackedWidget(self)
        layout.addWidget(self.stackwidget)
        self.combobox.currentIndexChanged.connect(
                                              self.stackwidget.setCurrentIndex)
        
        self.widgetlist = []
        for data, title, comment in datalist:
            self.combobox.addItem(title)
            widget = FormWidget(data, comment=comment, parent=self)
            self.stackwidget.addWidget(widget)
            self.widgetlist.append(widget)
            
    def setup(self):
        for widget in self.widgetlist:
            widget.setup()

    def get(self):
        return [ widget.get() for widget in self.widgetlist]
Exemple #4
0
class FrameWebView(QFrame):
    """
    Framed QWebView for UI consistency in Spyder.
    """
    linkClicked = Signal(QUrl)

    def __init__(self, parent):
        QFrame.__init__(self, parent)

        self._webview = WebView(self)

        layout = QHBoxLayout()
        layout.addWidget(self._webview)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)

        self._webview.linkClicked.connect(self.linkClicked)

    def set_font(self, font, fixed_font=None):
        self._webview.set_font(font, fixed_font=fixed_font)

    def setHtml(self, html_text, base_url):
        self._webview.setHtml(html_text, base_url)

    def url(self):
        return self._webview.url()

    def load(self, url):
        self._webview.load(url)

    def page(self):
        return self._webview.page()
class RequestHandler(QObject):
    """Handle introspection request.
    """

    introspection_complete = Signal()

    def __init__(self, code_info, plugins):
        super(RequestHandler, self).__init__()
        self.info = code_info
        self.timer = QTimer()
        self.timer.singleShot(LEAD_TIME_SEC * 1000, self._handle_timeout)
        self.waiting = True
        self.pending = {}
        self.result = None
        self.plugins = plugins
        self._start_time = time.time()
        self._threads = {}
        for plugin in plugins:
            self._make_async_call(plugin, code_info)

    def _handle_timeout(self):
        debug_print('got timeout: %s' % self.plugins)
        if self.pending:
            for plugin in self.plugins:
                if plugin.name in self.pending:
                    self._finalize(plugin.name, self.pending[plugin.name])
                    return
        self.waiting = False

    def _handle_incoming(self, name):
        # coerce to a str in case it is a QString
        name = str(name)
        self._threads[name].wait()
        if self.result:
            return
        result = self._threads[name].result
        if name == self.plugins[0].name or not self.waiting:
            if result:
                self._finalize(name, result)
            else:
                debug_print('No valid responses acquired')
                self.introspection_complete.emit()
        else:
            self.pending[name] = result

    def _make_async_call(self, plugin, info):
        """Trigger an introspection job in a thread"""
        self._threads[str(plugin.name)] = thread = IntrospectionThread(
            plugin, info)
        thread.request_handled.connect(self._handle_incoming)
        thread.start()

    def _finalize(self, name, result):
        self.result = result
        self.waiting = False
        self.pending = None
        delta = time.time() - self._start_time
        debug_print('%s request from %s finished: "%s" in %.1f sec' %
                    (self.info.name, name, str(result)[:100], delta))
        self.introspection_complete.emit()
Exemple #6
0
class PathComboBox(EditableComboBox):
    """
    QComboBox handling path locations
    """
    open_dir = Signal(str)

    def __init__(self, parent, adjust_to_contents=False):
        EditableComboBox.__init__(self, parent)
        if adjust_to_contents:
            self.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        else:
            self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
            self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.tips = {
            True:
            _("Press enter to validate this path"),
            False:
            _('This path is incorrect.\n'
              'Enter a correct directory path,\n'
              'then press enter to validate')
        }

    def is_valid(self, qstr=None):
        """Return True if string is valid"""
        if qstr is None:
            qstr = self.currentText()
        return osp.isdir(to_text_string(qstr))

    def selected(self):
        """Action to be executed when a valid item has been selected"""
        EditableComboBox.selected(self)
        self.open_dir.emit(self.currentText())
Exemple #7
0
class ColorButton(QPushButton):
    """
    Color choosing push button
    """
    colorChanged = Signal(QColor)
    
    def __init__(self, parent=None):
        QPushButton.__init__(self, parent)
        self.setFixedSize(20, 20)
        self.setIconSize(QSize(12, 12))
        self.clicked.connect(self.choose_color)
        self._color = QColor()
    
    def choose_color(self):
        color = QColorDialog.getColor(self._color, self.parentWidget(),
                                      'Select Color',
                                      QColorDialog.ShowAlphaChannel)
        if color.isValid():
            self.set_color(color)
    
    def get_color(self):
        return self._color
    
    @Slot(QColor)
    def set_color(self, color):
        if color != self._color:
            self._color = color
            self.colorChanged.emit(self._color)
            pixmap = QPixmap(self.iconSize())
            pixmap.fill(color)
            self.setIcon(QIcon(pixmap))
    
    color = Property("QColor", get_color, set_color)
Exemple #8
0
class FormTabWidget(QWidget):
    update_buttons = Signal()

    def __init__(self, datalist, comment="", parent=None):
        QWidget.__init__(self, parent)
        layout = QVBoxLayout()
        self.tabwidget = QTabWidget()
        layout.addWidget(self.tabwidget)
        self.setLayout(layout)
        self.widgetlist = []
        for data, title, comment in datalist:
            if len(data[0]) == 3:
                widget = FormComboWidget(data, comment=comment, parent=self)
            else:
                widget = FormWidget(data, comment=comment, parent=self)
            index = self.tabwidget.addTab(widget, title)
            self.tabwidget.setTabToolTip(index, comment)
            self.widgetlist.append(widget)

    def setup(self):
        for widget in self.widgetlist:
            widget.setup()

    def get(self):
        return [widget.get() for widget in self.widgetlist]
Exemple #9
0
class KeyPressFilter(QObject):
    """
    Use with `installEventFilter` to get up/down arrow key press signal.
    """
    UP, DOWN = [-1, 1]  # Step constants

    sig_up_key_pressed = Signal()
    sig_down_key_pressed = Signal()

    def eventFilter(self, src, e):
        if e.type() == QEvent.KeyPress:
            if e.key() == Qt.Key_Up:
                self.sig_up_key_pressed.emit()
            elif e.key() == Qt.Key_Down:
                self.sig_down_key_pressed.emit()

        return super(KeyPressFilter, self).eventFilter(src, e)
Exemple #10
0
class SpyderPluginWidget(QWidget, SpyderPluginMixin):
    """
    Spyder base widget class
    Spyder's widgets either inherit this class or reimplement its interface
    """
    sig_option_changed = Signal(str, object)

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        SpyderPluginMixin.__init__(self, parent)

    def get_plugin_title(self):
        """
        Return plugin title
        Note: after some thinking, it appears that using a method
        is more flexible here than using a class attribute
        """
        raise NotImplementedError

    def get_plugin_icon(self):
        """
        Return plugin icon (QIcon instance)
        Note: this is required for plugins creating a main window
              (see SpyderPluginMixin.create_mainwindow)
              and for configuration dialog widgets creation
        """
        return get_icon('qt.png')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        pass

    def closing_plugin(self, cancelable=False):
        """
        Perform actions before parent main window is closed
        Return True or False whether the plugin may be closed immediately or not
        Note: returned value is ignored if *cancelable* is False
        """
        raise NotImplementedError

    def refresh_plugin(self):
        """Refresh widget"""
        raise NotImplementedError

    def get_plugin_actions(self):
        """
        Return a list of actions related to plugin
        Note: these actions will be enabled when plugin's dockwidget is visible
              and they will be disabled when it's hidden
        """
        raise NotImplementedError

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        raise NotImplementedError
Exemple #11
0
class ObjectComboBox(EditableComboBox):
    """
    QComboBox handling object names
    """
    # Signals
    valid = Signal(bool)

    def __init__(self, parent):
        EditableComboBox.__init__(self, parent)
        self.object_inspector = parent
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.tips = {True: '', False: ''}

    def is_valid(self, qstr=None):
        """Return True if string is valid"""
        if not self.object_inspector.source_is_console():
            return True
        if qstr is None:
            qstr = self.currentText()
        if not re.search('^[a-zA-Z0-9_\.]*$', str(qstr), 0):
            return False
        objtxt = to_text_string(qstr)
        if self.object_inspector.get_option('automatic_import'):
            shell = self.object_inspector.internal_shell
            if shell is not None:
                return shell.is_defined(objtxt, force_import=True)
        shell = self.object_inspector.get_shell()
        if shell is not None:
            try:
                return shell.is_defined(objtxt)
            except socket.error:
                shell = self.object_inspector.get_shell()
                try:
                    return shell.is_defined(objtxt)
                except socket.error:
                    # Well... too bad!
                    pass

    def validate_current_text(self):
        self.validate(self.currentText())

    def validate(self, qstr, editing=True):
        """Reimplemented to avoid formatting actions"""
        valid = self.is_valid(qstr)
        if self.hasFocus() and valid is not None:
            if editing:
                # Combo box text is being modified: invalidate the entry
                self.show_tip(self.tips[valid])
                self.valid.emit(False)
            else:
                # A new item has just been selected
                if valid:
                    self.selected()
                else:
                    self.valid.emit(False)
        else:
            self.set_default_style()
Exemple #12
0
class BreakpointWidget(QWidget):
    """
    Breakpoint widget
    """
    VERSION = '1.0.0'
    clear_all_breakpoints = Signal()
    set_or_edit_conditional_breakpoint = Signal()
    clear_breakpoint = Signal(str, int)
    edit_goto = Signal(str, int, str)

    def __init__(self, parent):
        QWidget.__init__(self, parent)

        self.setWindowTitle("Breakpoints")
        self.dictwidget = BreakpointTableView(self,
                                              self._load_all_breakpoints())
        layout = QVBoxLayout()
        layout.addWidget(self.dictwidget)
        self.setLayout(layout)
        self.dictwidget.clear_all_breakpoints.connect(
            lambda: self.clear_all_breakpoints.emit())
        self.dictwidget.clear_breakpoint.connect(
            lambda s1, lino: self.clear_breakpoint.emit(s1, lino))
        self.dictwidget.edit_goto.connect(
            lambda s1, lino, s2: self.edit_goto.emit(s1, lino, s2))
        self.dictwidget.set_or_edit_conditional_breakpoint.connect(
            lambda: self.set_or_edit_conditional_breakpoint.emit())

    def _load_all_breakpoints(self):
        bp_dict = CONF.get('run', 'breakpoints', {})
        for filename in list(bp_dict.keys()):
            if not osp.isfile(filename):
                bp_dict.pop(filename)
        return bp_dict

    def get_data(self):
        pass

    def set_data(self):
        bp_dict = self._load_all_breakpoints()
        self.dictwidget.model.set_data(bp_dict)
        self.dictwidget.adjust_columns()
        self.dictwidget.sortByColumn(0, Qt.DescendingOrder)
Exemple #13
0
class MacApplication(QApplication):
    """Subclass to be able to open external files with our Mac app"""
    open_external_file = Signal(str)

    def __init__(self, *args):
        QApplication.__init__(self, *args)

    def event(self, event):
        if event.type() == QEvent.FileOpen:
            fname = str(event.file())
            self.open_external_file.emit(fname)
        return QApplication.event(self, event)
Exemple #14
0
class BaseRunConfigDialog(QDialog):
    """Run configuration dialog box, base widget"""
    size_change = Signal(QSize)
    
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.setWindowIcon(get_icon("run_settings.png"))
        layout = QVBoxLayout()
        self.setLayout(layout)
    
    def add_widgets(self, *widgets_or_spacings):
        """Add widgets/spacing to dialog vertical layout"""
        layout = self.layout()
        for widget_or_spacing in widgets_or_spacings:
            if isinstance(widget_or_spacing, int):
                layout.addSpacing(widget_or_spacing)
            else:
                layout.addWidget(widget_or_spacing)
    
    def add_button_box(self, stdbtns):
        """Create dialog button box and add it to the dialog layout"""
        bbox = QDialogButtonBox(stdbtns)
        run_btn = bbox.addButton(_("Run"), QDialogButtonBox.AcceptRole)
        run_btn.clicked.connect(self.run_btn_clicked)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        btnlayout = QHBoxLayout()
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)
        self.layout().addLayout(btnlayout)
    
    def resizeEvent(self, event):
        """
        Reimplement Qt method to be able to save the widget's size from the
        main application
        """
        QDialog.resizeEvent(self, event)
        self.size_change.emit(self.size())
    
    def run_btn_clicked(self):
        """Run button was just clicked"""
        pass
        
    def setup(self, fname):
        """Setup Run Configuration dialog with filename *fname*"""
        raise NotImplementedError
Exemple #15
0
class PlainText(QWidget):
    """
    Read-only editor widget with find dialog
    """
    # Signals
    focus_changed = Signal()

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.editor = None

        # Read-only editor
        self.editor = codeeditor.CodeEditor(self)
        self.editor.setup_editor(linenumbers=False,
                                 language='py',
                                 scrollflagarea=False,
                                 edge_line=False)
        self.editor.focus_changed.connect(lambda: self.focus_changed.emit())
        self.editor.setReadOnly(True)

        # Find/replace widget
        self.find_widget = FindReplace(self)
        self.find_widget.set_editor(self.editor)
        self.find_widget.hide()

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.editor)
        layout.addWidget(self.find_widget)
        self.setLayout(layout)

    def set_font(self, font, color_scheme=None):
        """Set font"""
        self.editor.set_font(font, color_scheme=color_scheme)

    def set_color_scheme(self, color_scheme):
        """Set color scheme"""
        self.editor.set_color_scheme(color_scheme)

    def set_text(self, text, is_code):
        self.editor.set_highlight_current_line(is_code)
        self.editor.set_occurence_highlighting(is_code)
        if is_code:
            self.editor.set_language('py')
        else:
            self.editor.set_language(None)
        self.editor.set_text(text)
        self.editor.set_cursor_position('sof')

    def clear(self):
        self.editor.clear()
Exemple #16
0
class PluginListener(QThread):
    """A plugin response listener.
    """

    # Emitted when the plugin has intitialized.
    initialized = Signal(int)

    # Emitted when a request response has been received.
    request_handled = Signal(object)

    def __init__(self, sock):
        super(PluginListener, self).__init__()
        self.sock = sock
        self._initialized = False

    def run(self):
        while True:
            try:
                conn, _addr = self.sock.accept()
            except socket.error as e:
                badfd = errno.WSAEBADF if os.name == 'nt' else errno.EBADF
                extra = errno.WSAENOTSOCK if os.name == 'nt' else badfd
                if e.args[0] in [errno.ECONNABORTED, badfd, extra]:
                    return
                # See Issue 1275 for details on why errno EINTR is
                # silently ignored here.
                eintr = errno.WSAEINTR if os.name == 'nt' else errno.EINTR
                if e.args[0] == eintr:
                    continue
                raise
            if not self._initialized:
                server_port = read_packet(conn)
                if isinstance(server_port, int):
                    self._initialized = True
                    self.initialized.emit(server_port)
            else:
                self.request_handled.emit(read_packet(conn))
Exemple #17
0
class SpyderDockWidget(QDockWidget):
    """Subclass to override needed methods"""
    plugin_closed = Signal()

    def __init__(self, title, parent):
        super(SpyderDockWidget, self).__init__(title, parent)

        # Needed for the installation of the event filter
        self.title = title
        self.main = parent
        self.dock_tabbar = None

        # To track dockwidget changes the filter is installed when dockwidget
        # visibility changes. This installs the filter on startup and also
        # on dockwidgets that are undocked and then docked to a new location.
        self.visibilityChanged.connect(self.install_tab_event_filter)

    def closeEvent(self, event):
        """
        Reimplement Qt method to send a signal on close so that "Panes" main
        window menu can be updated correctly
        """
        self.plugin_closed.emit()

    def install_tab_event_filter(self, value):
        """
        Install an event filter to capture mouse events in the tabs of a
        QTabBar holding tabified dockwidgets.
        """
        dock_tabbar = None
        tabbars = self.main.findChildren(QTabBar)
        for tabbar in tabbars:
            for tab in range(tabbar.count()):
                title = tabbar.tabText(tab)
                if title == self.title:
                    dock_tabbar = tabbar
                    break
        if dock_tabbar is not None:
            self.dock_tabbar = dock_tabbar
            # Install filter only once per QTabBar
            if getattr(self.dock_tabbar, 'filter', None) is None:
                self.dock_tabbar.filter = TabFilter(self.dock_tabbar,
                                                    self.main)
                self.dock_tabbar.installEventFilter(self.dock_tabbar.filter)
class IntrospectionThread(QThread):
    """
    A thread to perform an introspection task
    """

    request_handled = Signal(str)

    def __init__(self, plugin, info):
        super(IntrospectionThread, self).__init__()
        self.plugin = plugin
        self.info = info
        self.result = None

    def run(self):
        func = getattr(self.plugin, 'get_%s' % self.info.name)
        self.plugin.busy = True
        try:
            self.result = func(self.info)
        except Exception as e:
            debug_print(e)
        self.plugin.busy = False
        self.request_handled.emit(self.plugin.name)
Exemple #19
0
class ExtPythonShellWidget(PythonShellWidget):

    wait_for_ready_read = Signal()
    go_to_error = Signal(str)
    focus_changed = Signal()

    def __init__(self, parent, history_filename, profile=False):
        PythonShellWidget.__init__(self, parent, history_filename, profile)
        self.path = []

    def set_externalshell(self, externalshell):
        # ExternalShellBase instance:
        self.externalshell = externalshell

    def clear_terminal(self):
        """Reimplement ShellBaseWidget method"""
        self.clear()
        self.execute.emit("\n")

    def execute_lines(self, lines):
        """
        Execute a set of lines as multiple command
        lines: multiple lines of text to be executed as single commands
        """
        for line in lines.splitlines():
            stripped_line = line.strip()
            if stripped_line.startswith('#'):
                continue
            self.write(line + os.linesep, flush=True)
            self.execute_command(line)
            # Workaround for Issue 502
            # Emmiting wait_for_ready_read was making the console hang
            # in Mac OS X
            if sys.platform.startswith("darwin"):
                import time
                time.sleep(0.025)
            else:
                self.wait_for_ready_read.emit()
            self.flush()

    #------ Code completion / Calltips
    def ask_monitor(self, command, settings=[]):
        sock = self.externalshell.introspection_socket
        if sock is None:
            return
        try:
            return communicate(sock, command, settings=settings)
        except socket.error:
            # Process was just closed
            pass
        except MemoryError:
            # Happens when monitor is not ready on slow machines
            pass

    def get_dir(self, objtxt):
        """Return dir(object)"""
        return self.ask_monitor("__get_dir__('%s')" % objtxt)

    def get_globals_keys(self):
        """Return shell globals() keys"""
        return self.ask_monitor("get_globals_keys()")

    def get_cdlistdir(self):
        """Return shell current directory list dir"""
        return self.ask_monitor("getcdlistdir()")

    def iscallable(self, objtxt):
        """Is object callable?"""
        return self.ask_monitor("__iscallable__('%s')" % objtxt)

    def get_arglist(self, objtxt):
        """Get func/method argument list"""
        return self.ask_monitor("__get_arglist__('%s')" % objtxt)

    def get__doc__(self, objtxt):
        """Get object __doc__"""
        return self.ask_monitor("__get__doc____('%s')" % objtxt)

    def get_doc(self, objtxt):
        """Get object documentation dictionary"""
        return self.ask_monitor("__get_doc__('%s')" % objtxt)

    def get_source(self, objtxt):
        """Get object source"""
        return self.ask_monitor("__get_source__('%s')" % objtxt)

    def is_defined(self, objtxt, force_import=False):
        """Return True if object is defined"""
        return self.ask_monitor("isdefined('%s', force_import=%s)" %
                                (objtxt, force_import))

    def get_module_completion(self, objtxt):
        """Return module completion list associated to object name"""
        return self.ask_monitor("getmodcomplist('%s', %s)" % \
                                                           (objtxt, self.path))

    def get_cwd(self):
        """Return shell current working directory"""
        return self.ask_monitor("getcwd()")

    def set_cwd(self, dirname):
        """Set shell current working directory"""
        return self.ask_monitor("setcwd(r'%s')" % dirname)

    def get_env(self):
        """Return environment variables: os.environ"""
        return self.ask_monitor("getenv()")

    def set_env(self, env):
        """Set environment variables via os.environ"""
        return self.ask_monitor('setenv()', settings=[env])

    def get_syspath(self):
        """Return sys.path[:]"""
        return self.ask_monitor("getsyspath()")

    def set_spyder_breakpoints(self):
        """Set Spyder breakpoints into debugging session"""
        return self.ask_monitor("set_spyder_breakpoints()")
Exemple #20
0
class ExternalPythonShell(ExternalShellBase):
    """External Shell widget: execute Python script in a separate process"""
    SHELL_CLASS = ExtPythonShellWidget
    sig_pdb = Signal(str, int)
    open_file = Signal(str, int)
    ipython_kernel_start_error = Signal(str)
    create_ipython_client = Signal(str)
    started = Signal()
    sig_finished = Signal()

    def __init__(self,
                 parent=None,
                 fname=None,
                 wdir=None,
                 interact=False,
                 debug=False,
                 post_mortem=False,
                 path=[],
                 python_args='',
                 ipykernel=False,
                 arguments='',
                 stand_alone=None,
                 umr_enabled=True,
                 umr_namelist=[],
                 umr_verbose=True,
                 pythonstartup=None,
                 pythonexecutable=None,
                 monitor_enabled=True,
                 mpl_backend=None,
                 ets_backend='qt4',
                 qt_api=None,
                 pyqt_api=0,
                 ignore_sip_setapi_errors=False,
                 merge_output_channels=False,
                 colorize_sys_stderr=False,
                 autorefresh_timeout=3000,
                 autorefresh_state=True,
                 light_background=True,
                 menu_actions=None,
                 show_buttons_inside=True,
                 show_elapsed_time=True):

        assert qt_api in (None, 'pyqt', 'pyside')

        self.namespacebrowser = None  # namespace browser widget!

        self.dialog_manager = DialogManager()

        self.stand_alone = stand_alone  # stand alone settings (None: plugin)
        self.interact = interact
        self.is_ipykernel = ipykernel
        self.pythonstartup = pythonstartup
        self.pythonexecutable = pythonexecutable
        self.monitor_enabled = monitor_enabled
        self.mpl_backend = mpl_backend
        self.ets_backend = ets_backend
        self.qt_api = qt_api
        self.pyqt_api = pyqt_api
        self.ignore_sip_setapi_errors = ignore_sip_setapi_errors
        self.merge_output_channels = merge_output_channels
        self.colorize_sys_stderr = colorize_sys_stderr
        self.umr_enabled = umr_enabled
        self.umr_namelist = umr_namelist
        self.umr_verbose = umr_verbose
        self.autorefresh_timeout = autorefresh_timeout
        self.autorefresh_state = autorefresh_state

        self.namespacebrowser_button = None
        self.cwd_button = None
        self.env_button = None
        self.syspath_button = None
        self.terminate_button = None

        self.notification_thread = None

        ExternalShellBase.__init__(self,
                                   parent=parent,
                                   fname=fname,
                                   wdir=wdir,
                                   history_filename='history.py',
                                   light_background=light_background,
                                   menu_actions=menu_actions,
                                   show_buttons_inside=show_buttons_inside,
                                   show_elapsed_time=show_elapsed_time)

        if self.pythonexecutable is None:
            self.pythonexecutable = get_python_executable()

        self.python_args = None
        if python_args:
            assert is_text_string(python_args)
            self.python_args = python_args

        assert is_text_string(arguments)
        self.arguments = arguments

        self.connection_file = None

        if self.is_ipykernel:
            self.interact = False
            # Running our custom startup script for IPython kernels:
            # (see spyderlib/widgets/externalshell/start_ipython_kernel.py)
            self.fname = get_module_source_path(
                'spyderlib.widgets.externalshell', 'start_ipython_kernel.py')

        self.shell.set_externalshell(self)

        self.toggle_globals_explorer(False)
        self.interact_action.setChecked(self.interact)
        self.debug_action.setChecked(debug)

        self.introspection_socket = None
        self.is_interpreter = fname is None

        if self.is_interpreter:
            self.terminate_button.hide()

        self.post_mortem_action.setChecked(post_mortem
                                           and not self.is_interpreter)

        # Additional python path list
        self.path = path
        self.shell.path = path

    def set_introspection_socket(self, introspection_socket):
        self.introspection_socket = introspection_socket
        if self.namespacebrowser is not None:
            settings = self.namespacebrowser.get_view_settings()
            communicate(introspection_socket,
                        'set_remote_view_settings()',
                        settings=[settings])

    def set_autorefresh_timeout(self, interval):
        if self.introspection_socket is not None:
            try:
                communicate(self.introspection_socket,
                            "set_monitor_timeout(%d)" % interval)
            except socket.error:
                pass

    def closeEvent(self, event):
        self.quit_monitor()
        ExternalShellBase.closeEvent(self, event)

    def get_toolbar_buttons(self):
        ExternalShellBase.get_toolbar_buttons(self)
        if self.namespacebrowser_button is None \
           and self.stand_alone is not None:
            self.namespacebrowser_button = create_toolbutton(
                self,
                text=_("Variables"),
                icon=get_icon('dictedit.png'),
                tip=_("Show/hide global variables explorer"),
                toggled=self.toggle_globals_explorer,
                text_beside_icon=True)
        if self.terminate_button is None:
            self.terminate_button = create_toolbutton(
                self,
                text=_("Terminate"),
                icon=get_icon('stop.png'),
                tip=_("Attempts to stop the process. The process\n"
                      "may not exit as a result of clicking this\n"
                      "button (it is given the chance to prompt\n"
                      "the user for any unsaved files, etc)."))
        buttons = []
        if self.namespacebrowser_button is not None:
            buttons.append(self.namespacebrowser_button)
        buttons += [
            self.run_button, self.terminate_button, self.kill_button,
            self.options_button
        ]
        return buttons

    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=get_std_icon('DirOpenIcon'),
            tip=_("Set current working directory"),
            triggered=self.set_current_working_directory)
        self.env_button = create_action(self,
                                        _("Environment variables"),
                                        icon=get_icon('environ.png'),
                                        triggered=self.show_env)
        self.syspath_button = create_action(self,
                                            _("Show sys.path contents"),
                                            icon=get_icon('syspath.png'),
                                            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

    def is_interpreter(self):
        """Return True if shellwidget is a Python interpreter"""
        return self.is_interpreter

    def get_shell_widget(self):
        if self.stand_alone is None:
            return self.shell
        else:
            self.namespacebrowser = NamespaceBrowser(self)
            settings = self.stand_alone
            self.namespacebrowser.set_shellwidget(self)
            self.namespacebrowser.setup(**settings)
            self.namespacebrowser.sig_collapse.connect(
                lambda: self.toggle_globals_explorer(False))
            # Shell splitter
            self.splitter = splitter = QSplitter(Qt.Vertical, self)
            self.splitter.splitterMoved.connect(self.splitter_moved)
            splitter.addWidget(self.shell)
            splitter.setCollapsible(0, False)
            splitter.addWidget(self.namespacebrowser)
            splitter.setStretchFactor(0, 1)
            splitter.setStretchFactor(1, 0)
            splitter.setHandleWidth(5)
            splitter.setSizes([2, 1])
            return splitter

    def get_icon(self):
        return get_icon('python.png')

    def set_buttons_runnning_state(self, state):
        ExternalShellBase.set_buttons_runnning_state(self, state)
        self.interact_action.setEnabled(not state and not self.is_interpreter)
        self.debug_action.setEnabled(not state and not self.is_interpreter)
        self.args_action.setEnabled(not state and not self.is_interpreter)
        self.post_mortem_action.setEnabled(not state
                                           and not self.is_interpreter)
        if state:
            if self.arguments:
                argstr = _("Arguments: %s") % self.arguments
            else:
                argstr = _("No argument")
        else:
            argstr = _("Arguments...")
        self.args_action.setText(argstr)
        self.terminate_button.setVisible(not self.is_interpreter and state)
        if not state:
            self.toggle_globals_explorer(False)
        for btn in (self.cwd_button, self.env_button, self.syspath_button):
            btn.setEnabled(state and self.monitor_enabled)
        if self.namespacebrowser_button is not None:
            self.namespacebrowser_button.setEnabled(state)

    def set_namespacebrowser(self, namespacebrowser):
        """
        Set namespace browser *widget*
        Note: this method is not used in stand alone mode
        """
        self.namespacebrowser = namespacebrowser
        self.configure_namespacebrowser()

    def configure_namespacebrowser(self):
        """Connect the namespace browser to the notification thread"""
        if self.notification_thread is not None:
            self.notification_thread.refresh_namespace_browser.connect(
                self.namespacebrowser.refresh_table)
            signal = self.notification_thread.sig_process_remote_view
            signal.connect(
                lambda data: self.namespacebrowser.process_remote_view(data))

    def create_process(self):
        self.shell.clear()

        self.process = QProcess(self)
        if self.merge_output_channels:
            self.process.setProcessChannelMode(QProcess.MergedChannels)
        else:
            self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.shell.wait_for_ready_read.connect(
            lambda: self.process.waitForReadyRead(250))

        # Working directory
        if self.wdir is not None:
            self.process.setWorkingDirectory(self.wdir)

        #-------------------------Python specific------------------------------
        # Python arguments
        p_args = ['-u']
        if DEBUG >= 3:
            p_args += ['-v']
        p_args += get_python_args(self.fname, self.python_args,
                                  self.interact_action.isChecked(),
                                  self.debug_action.isChecked(),
                                  self.arguments)

        env = [
            to_text_string(_path)
            for _path in self.process.systemEnvironment()
        ]
        if self.pythonstartup:
            env.append('PYTHONSTARTUP=%s' % self.pythonstartup)

        #-------------------------Python specific-------------------------------
        # Post mortem debugging
        if self.post_mortem_action.isChecked():
            env.append('SPYDER_EXCEPTHOOK=True')

        # Set standard input/output encoding for Python consoles
        # (IPython handles it on its own)
        # See http://stackoverflow.com/q/26312400/438386, specifically
        # the comments of Martijn Pieters
        if not self.is_ipykernel:
            env.append('PYTHONIOENCODING=UTF-8')

        # Monitor
        if self.monitor_enabled:
            env.append('SPYDER_SHELL_ID=%s' % id(self))
            env.append('SPYDER_AR_TIMEOUT=%d' % self.autorefresh_timeout)
            env.append('SPYDER_AR_STATE=%r' % self.autorefresh_state)
            from spyderlib.widgets.externalshell import introspection
            introspection_server = introspection.start_introspection_server()
            introspection_server.register(self)
            notification_server = introspection.start_notification_server()
            self.notification_thread = notification_server.register(self)
            self.notification_thread.sig_pdb.connect(
                lambda fname, lineno: self.sig_pdb.emit(fname, lineno))
            self.notification_thread.new_ipython_kernel.connect(
                lambda args: self.create_ipython_client.emit(args))
            self.notification_thread.open_file.connect(
                lambda fname, lineno: self.open_file.emit(fname, lineno))
            if self.namespacebrowser is not None:
                self.configure_namespacebrowser()
            env.append('SPYDER_I_PORT=%d' % introspection_server.port)
            env.append('SPYDER_N_PORT=%d' % notification_server.port)

        # External modules options
        env.append('ETS_TOOLKIT=%s' % self.ets_backend)
        if self.mpl_backend:
            env.append('MATPLOTLIB_BACKEND=%s' % self.mpl_backend)
        if self.qt_api:
            env.append('QT_API=%s' % self.qt_api)
        env.append('COLORIZE_SYS_STDERR=%s' % self.colorize_sys_stderr)
        #        # Socket-based alternative (see input hook in sitecustomize.py):
        #        if self.install_qt_inputhook:
        #            from PyQt4.QtNetwork import QLocalServer
        #            self.local_server = QLocalServer()
        #            self.local_server.listen(str(id(self)))
        if self.pyqt_api:
            env.append('PYQT_API=%d' % self.pyqt_api)
        env.append('IGNORE_SIP_SETAPI_ERRORS=%s' %
                   self.ignore_sip_setapi_errors)

        # User Module Deleter
        if self.is_interpreter:
            env.append('UMR_ENABLED=%r' % self.umr_enabled)
            env.append('UMR_NAMELIST=%s' % ','.join(self.umr_namelist))
            env.append('UMR_VERBOSE=%r' % self.umr_verbose)
            env.append('MATPLOTLIB_ION=True')
        else:
            if self.interact:
                env.append('MATPLOTLIB_ION=True')
            else:
                env.append('MATPLOTLIB_ION=False')

        # IPython kernel
        env.append('IPYTHON_KERNEL=%r' % self.is_ipykernel)

        # Add sitecustomize path to path list
        pathlist = []
        scpath = osp.dirname(osp.abspath(__file__))
        pathlist.append(scpath)

        # Adding Spyder path
        pathlist += self.path

        # Adding path list to PYTHONPATH environment variable
        add_pathlist_to_PYTHONPATH(env, pathlist)

        #-------------------------Python specific------------------------------

        self.process.readyReadStandardOutput.connect(self.write_output)
        self.process.readyReadStandardError.connect(self.write_error)
        self.process.finished.connect(
            lambda ec, es=QProcess.ExitStatus: self.finished(ec, es))
        self.sig_finished.connect(self.dialog_manager.close_all)
        self.terminate_button.clicked.connect(self.process.terminate)
        self.kill_button.clicked.connect(self.process.kill)

        #-------------------------Python specific------------------------------
        # Fixes for our Mac app:
        # 1. PYTHONPATH and PYTHONHOME are set while bootstrapping the app,
        #    but their values are messing sys.path for external interpreters
        #    (e.g. EPD) so we need to remove them from the environment.
        # 2. Set PYTHONPATH again but without grabbing entries defined in the
        #    environment (Fixes Issue 1321)
        # 3. Remove PYTHONOPTIMIZE from env so that we can have assert
        #    statements working with our interpreters (See Issue 1281)
        if running_in_mac_app():
            env.append('SPYDER_INTERPRETER=%s' % self.pythonexecutable)
            if MAC_APP_NAME not in self.pythonexecutable:
                env = [p for p in env if not (p.startswith('PYTHONPATH') or \
                                              p.startswith('PYTHONHOME'))] # 1.

                add_pathlist_to_PYTHONPATH(env, pathlist, drop_env=True)  # 2.
            env = [p for p in env if not p.startswith('PYTHONOPTIMIZE')]  # 3.

        processEnvironment = QProcessEnvironment()
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)
        self.process.setProcessEnvironment(processEnvironment)
        self.process.start(self.pythonexecutable, p_args)
        #-------------------------Python specific------------------------------

        running = self.process.waitForStarted(3000)
        self.set_running_state(running)
        if not running:
            if self.is_ipykernel:
                self.ipython_kernel_start_error.emit(
                    _("The kernel failed to start!! That's all we know... "
                      "Please close this console and open a new one."))
            else:
                QMessageBox.critical(self, _("Error"),
                                     _("A Python console failed to start!"))
        else:
            self.shell.setFocus()
            self.started.emit()
        return self.process

    def finished(self, exit_code, exit_status):
        """Reimplement ExternalShellBase method"""
        if self.is_ipykernel and exit_code == 1:
            self.ipython_kernel_start_error.emit(
                self.shell.get_text_with_eol())
        ExternalShellBase.finished(self, exit_code, exit_status)
        self.introspection_socket = None

#==============================================================================
#    Input/Output
#==============================================================================

    def write_error(self):
        if os.name == 'nt':
            #---This is apparently necessary only on Windows (not sure though):
            #   emptying standard output buffer before writing error output
            self.process.setReadChannel(QProcess.StandardOutput)
            if self.process.waitForReadyRead(1):
                self.write_output()
        self.shell.write_error(self.get_stderr())
        QApplication.processEvents()

    def send_to_process(self, text):
        if not self.is_running():
            return

        if not is_text_string(text):
            text = to_text_string(text)
        if self.mpl_backend == 'Qt4Agg' and os.name == 'nt' and \
          self.introspection_socket is not None:
            communicate(self.introspection_socket,
                        "toggle_inputhook_flag(True)")
#            # Socket-based alternative (see input hook in sitecustomize.py):
#            while self.local_server.hasPendingConnections():
#                self.local_server.nextPendingConnection().write('go!')
        if any([text == cmd for cmd in ['%ls', '%pwd', '%scientific']]) or \
          any([text.startswith(cmd) for cmd in ['%cd ', '%clear ']]):
            text = 'evalsc(r"%s")\n' % text
        if not text.endswith('\n'):
            text += '\n'
        self.process.write(to_binary_string(text, 'utf8'))
        self.process.waitForBytesWritten(-1)

        # Eventually write prompt faster (when hitting Enter continuously)
        # -- necessary/working on Windows only:
        if os.name == 'nt':
            self.write_error()

    def keyboard_interrupt(self):
        if self.introspection_socket is not None:
            communicate(self.introspection_socket, "thread.interrupt_main()")

    def quit_monitor(self):
        if self.introspection_socket is not None:
            try:
                write_packet(self.introspection_socket, "thread.exit()")
            except socket.error:
                pass

#==============================================================================
#    Globals explorer
#==============================================================================

    @Slot(bool)
    def toggle_globals_explorer(self, state):
        if self.stand_alone is not None:
            self.splitter.setSizes([1, 1 if state else 0])
            self.namespacebrowser_button.setChecked(state)
            if state and self.namespacebrowser is not None:
                self.namespacebrowser.refresh_table()

    def splitter_moved(self, pos, index):
        self.namespacebrowser_button.setChecked(self.splitter.sizes()[1])

#==============================================================================
#    Misc.
#==============================================================================

    @Slot()
    def set_current_working_directory(self):
        """Set current working directory"""
        cwd = self.shell.get_cwd()
        self.redirect_stdio.emit(False)
        directory = getexistingdirectory(self, _("Select directory"), cwd)
        if directory:
            self.shell.set_cwd(directory)
        self.redirect_stdio.emit(True)

    @Slot()
    def show_env(self):
        """Show environment variables"""
        get_func = self.shell.get_env
        set_func = self.shell.set_env
        self.dialog_manager.show(RemoteEnvDialog(get_func, set_func))

    @Slot()
    def show_syspath(self):
        """Show sys.path contents"""
        editor = DictEditor()
        editor.setup(self.shell.get_syspath(),
                     title="sys.path",
                     readonly=True,
                     width=600,
                     icon='syspath.png')
        self.dialog_manager.show(editor)
Exemple #21
0
class ExternalSystemShell(ExternalShellBase):
    """External Shell widget: execute Python script in a separate process"""
    SHELL_CLASS = TerminalWidget
    started = Signal()

    def __init__(self,
                 parent=None,
                 wdir=None,
                 path=[],
                 light_background=True,
                 menu_actions=None,
                 show_buttons_inside=True,
                 show_elapsed_time=True):
        ExternalShellBase.__init__(self,
                                   parent=parent,
                                   fname=None,
                                   wdir=wdir,
                                   history_filename='.history',
                                   light_background=light_background,
                                   menu_actions=menu_actions,
                                   show_buttons_inside=show_buttons_inside,
                                   show_elapsed_time=show_elapsed_time)

        # Additional python path list
        self.path = path

        # For compatibility with the other shells that can live in the external
        # console
        self.is_ipykernel = False
        self.connection_file = None

    def get_icon(self):
        return ima.icon('cmdprompt')

    def create_process(self):
        self.shell.clear()

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.MergedChannels)

        # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe)
        env = [
            to_text_string(_path)
            for _path in self.process.systemEnvironment()
        ]

        processEnvironment = QProcessEnvironment()
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)

        add_pathlist_to_PYTHONPATH(env, self.path)
        self.process.setProcessEnvironment(processEnvironment)

        # Working directory
        if self.wdir is not None:
            self.process.setWorkingDirectory(self.wdir)

        # Shell arguments
        if os.name == 'nt':
            p_args = ['/Q']
        else:
            p_args = ['-i']

        if self.arguments:
            p_args.extend(shell_split(self.arguments))

        self.process.readyReadStandardOutput.connect(self.write_output)
        self.process.finished.connect(self.finished)
        self.kill_button.clicked.connect(self.process.kill)

        if os.name == 'nt':
            self.process.start('cmd.exe', p_args)
        else:
            # Using bash:
            self.process.start('bash', p_args)
            self.send_to_process('PS1="\\u@\\h:\\w> "\n')

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))
        else:
            self.shell.setFocus()
            self.started.emit()

        return self.process

#===============================================================================
#    Input/Output
#===============================================================================

    def transcode(self, qba):
        if os.name == 'nt':
            return to_text_string(CP850_CODEC.toUnicode(qba.data()))
        else:
            return ExternalShellBase.transcode(self, qba)

    def send_to_process(self, text):
        if not is_text_string(text):
            text = to_text_string(text)
        if text[:-1] in ["clear", "cls", "CLS"]:
            self.shell.clear()
            self.send_to_process(os.linesep)
            return
        if not text.endswith('\n'):
            text += '\n'
        if os.name == 'nt':
            self.process.write(text.encode('cp850'))
        else:
            self.process.write(LOCALE_CODEC.fromUnicode(text))
        self.process.waitForBytesWritten(-1)

    def keyboard_interrupt(self):
        # This does not work on Windows:
        # (unfortunately there is no easy way to send a Ctrl+C to cmd.exe)
        self.send_ctrl_to_process('c')
Exemple #22
0
class IPythonClient(QWidget, SaveHistoryMixin):
    """
    IPython client or frontend for Spyder

    This is a widget composed of a shell widget (i.e. RichIPythonWidget
    + our additions = IPythonShellWidget) and an WebView info widget to 
    print kernel error and other messages.
    """

    SEPARATOR = '%s##---(%s)---' % (os.linesep * 2, time.ctime())
    append_to_history = Signal(str, str)

    def __init__(self,
                 plugin,
                 name,
                 history_filename,
                 connection_file=None,
                 hostname=None,
                 sshkey=None,
                 password=None,
                 kernel_widget_id=None,
                 menu_actions=None):
        super(IPythonClient, self).__init__(plugin)
        SaveHistoryMixin.__init__(self)
        self.options_button = None

        # stop button and icon
        self.stop_button = None
        self.stop_icon = get_icon("stop.png")

        self.connection_file = connection_file
        self.kernel_widget_id = kernel_widget_id
        self.hostname = hostname
        self.sshkey = sshkey
        self.password = password
        self.name = name
        self.get_option = plugin.get_option
        self.shellwidget = IPythonShellWidget(config=self.shellwidget_config(),
                                              local_kernel=False)
        self.shellwidget.hide()
        self.infowidget = WebView(self)
        self.menu_actions = menu_actions
        self.history_filename = get_conf_path(history_filename)
        self.history = []
        self.namespacebrowser = None

        self.set_infowidget_font()
        self.loading_page = self._create_loading_page()
        self.infowidget.setHtml(self.loading_page)

        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        hlayout = QHBoxLayout()
        for button in toolbar_buttons:
            hlayout.addWidget(button)
        vlayout.addLayout(hlayout)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.shellwidget)
        vlayout.addWidget(self.infowidget)
        self.setLayout(vlayout)

        self.exit_callback = lambda: plugin.close_client(client=self)

    #------ Public API --------------------------------------------------------
    def show_shellwidget(self, give_focus=True):
        """Show shellwidget and configure it"""
        self.infowidget.hide()
        self.shellwidget.show()
        self.infowidget.setHtml(BLANK)
        if give_focus:
            self.get_control().setFocus()

        # Connect shellwidget to the client
        self.shellwidget.set_ipyclient(self)

        # To save history
        self.shellwidget.executing.connect(self.add_to_history)

        # For Mayavi to run correctly
        self.shellwidget.executing.connect(self.set_backend_for_mayavi)

        # To update history after execution
        self.shellwidget.executed.connect(self.update_history)

        # To update the Variable Explorer after execution
        self.shellwidget.executed.connect(self.auto_refresh_namespacebrowser)

        # To show a stop button, when executing a process
        self.shellwidget.executing.connect(self.enable_stop_button)

        # To hide a stop button after execution stopped
        self.shellwidget.executed.connect(self.disable_stop_button)

    def enable_stop_button(self):
        self.stop_button.setEnabled(True)

    def disable_stop_button(self):
        self.stop_button.setDisabled(True)

    @Slot()
    def stop_button_click_handler(self):
        self.stop_button.setDisabled(True)
        self.interrupt_kernel()

    def show_kernel_error(self, error):
        """Show kernel initialization errors in infowidget"""
        # Remove explanation about how to kill the kernel (doesn't apply to us)
        error = error.split('issues/2049')[-1]
        # Remove unneeded blank lines at the beginning
        eol = sourcecode.get_eol_chars(error)
        if eol:
            error = error.replace(eol, '<br>')
        while error.startswith('<br>'):
            error = error[4:]
        # Remove connection message
        if error.startswith('To connect another client') or \
          error.startswith('[IPKernelApp] To connect another client'):
            error = error.split('<br>')
            error = '<br>'.join(error[2:])
        # Don't break lines in hyphens
        # From http://stackoverflow.com/q/7691569/438386
        error = error.replace('-', '&#8209')

        message = _("An error ocurred while starting the kernel")
        kernel_error_template = Template(KERNEL_ERROR)
        page = kernel_error_template.substitute(css_path=CSS_PATH,
                                                message=message,
                                                error=error)
        self.infowidget.setHtml(page)

    def show_restart_animation(self):
        self.shellwidget.hide()
        self.infowidget.setHtml(self.loading_page)
        self.infowidget.show()

    def get_name(self):
        """Return client name"""
        return ((_("Console") if self.hostname is None else self.hostname) +
                " " + self.name)

    def get_control(self):
        """Return the text widget (or similar) to give focus to"""
        # page_control is the widget used for paging
        page_control = self.shellwidget._page_control
        if page_control and page_control.isVisible():
            return page_control
        else:
            return self.shellwidget._control

    def get_options_menu(self):
        """Return options menu"""
        restart_action = create_action(self,
                                       _("Restart kernel"),
                                       shortcut=QKeySequence("Ctrl+."),
                                       icon=get_icon('restart.png'),
                                       triggered=self.restart_kernel)
        restart_action.setShortcutContext(Qt.WidgetWithChildrenShortcut)

        # Main menu
        if self.menu_actions is not None:
            actions = [restart_action, None] + self.menu_actions
        else:
            actions = [restart_action]
        return actions

    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

    def add_actions_to_context_menu(self, menu):
        """Add actions to IPython widget context menu"""
        # See spyderlib/widgets/ipython.py for more details on this method
        inspect_action = create_action(
            self,
            _("Inspect current object"),
            QKeySequence(get_shortcut('console', 'inspect current object')),
            icon=get_std_icon('MessageBoxInformation'),
            triggered=self.inspect_object)
        clear_line_action = create_action(self,
                                          _("Clear line or block"),
                                          QKeySequence("Shift+Escape"),
                                          icon=get_icon('eraser.png'),
                                          triggered=self.clear_line)
        clear_console_action = create_action(
            self,
            _("Clear console"),
            QKeySequence(get_shortcut('console', 'clear shell')),
            icon=get_icon('clear.png'),
            triggered=self.clear_console)
        quit_action = create_action(self,
                                    _("&Quit"),
                                    icon='exit.png',
                                    triggered=self.exit_callback)
        add_actions(menu, (None, inspect_action, clear_line_action,
                           clear_console_action, None, quit_action))
        return menu

    def set_font(self, font):
        """Set IPython widget's font"""
        self.shellwidget._control.setFont(font)
        self.shellwidget.font = font

    def set_infowidget_font(self):
        font = get_font('inspector', 'rich_text')
        self.infowidget.set_font(font)

    def interrupt_kernel(self):
        """Interrupt the associanted Spyder kernel if it's running"""
        self.shellwidget.request_interrupt_kernel()

    @Slot()
    def restart_kernel(self):
        """Restart the associanted Spyder kernel"""
        self.shellwidget.request_restart_kernel()

    @Slot()
    def inspect_object(self):
        """Show how to inspect an object with our object inspector"""
        self.shellwidget._control.inspect_current_object()

    @Slot()
    def clear_line(self):
        """Clear a console line"""
        self.shellwidget._keyboard_quit()

    @Slot()
    def clear_console(self):
        """Clear the whole console"""
        self.shellwidget.execute("%clear")

    def if_kernel_dies(self, t):
        """
        Show a message in the console if the kernel dies.
        t is the time in seconds between the death and showing the message.
        """
        message = _("It seems the kernel died unexpectedly. Use "
                    "'Restart kernel' to continue using this console.")
        self.shellwidget._append_plain_text(message + '\n')

    def update_history(self):
        self.history = self.shellwidget._history

    def set_backend_for_mayavi(self, command):
        calling_mayavi = False
        lines = command.splitlines()
        for l in lines:
            if not l.startswith('#'):
                if 'import mayavi' in l or 'from mayavi' in l:
                    calling_mayavi = True
                    break
        if calling_mayavi:
            message = _("Changing backend to Qt for Mayavi")
            self.shellwidget._append_plain_text(message + '\n')
            self.shellwidget.execute("%gui inline\n%gui qt")

    def interrupt_message(self):
        """
        Print an interrupt message when the client is connected to an external
        kernel
        """
        message = _("Kernel process is either remote or unspecified. "
                    "Cannot interrupt")
        QMessageBox.information(self, "IPython", message)

    def restart_message(self):
        """
        Print a restart message when the client is connected to an external
        kernel
        """
        message = _("Kernel process is either remote or unspecified. "
                    "Cannot restart.")
        QMessageBox.information(self, "IPython", message)

    def set_namespacebrowser(self, namespacebrowser):
        """Set namespace browser widget"""
        self.namespacebrowser = namespacebrowser

    def auto_refresh_namespacebrowser(self):
        """Refresh namespace browser"""
        if self.namespacebrowser:
            self.namespacebrowser.refresh_table()

    def shellwidget_config(self):
        """Generate a Config instance for shell widgets using our config
        system
        
        This lets us create each widget with its own config (as opposed to
        IPythonQtConsoleApp, where all widgets have the same config)
        """
        # ---- IPython config ----
        try:
            profile_path = osp.join(get_ipython_dir(), 'profile_default')
            full_ip_cfg = load_pyconfig_files(['ipython_qtconsole_config.py'],
                                              profile_path)

            # From the full config we only select the IPythonWidget section
            # because the others have no effect here.
            ip_cfg = Config({'IPythonWidget': full_ip_cfg.IPythonWidget})
        except:
            ip_cfg = Config()

        # ---- Spyder config ----
        spy_cfg = Config()

        # Make the pager widget a rich one (i.e a QTextEdit)
        spy_cfg.IPythonWidget.kind = 'rich'

        # Gui completion widget
        gui_comp_o = self.get_option('use_gui_completion')
        completions = {True: 'droplist', False: 'ncurses'}
        spy_cfg.IPythonWidget.gui_completion = completions[gui_comp_o]

        # Pager
        pager_o = self.get_option('use_pager')
        if pager_o:
            spy_cfg.IPythonWidget.paging = 'inside'
        else:
            spy_cfg.IPythonWidget.paging = 'none'

        # Calltips
        calltips_o = self.get_option('show_calltips')
        spy_cfg.IPythonWidget.enable_calltips = calltips_o

        # Buffer size
        buffer_size_o = self.get_option('buffer_size')
        spy_cfg.IPythonWidget.buffer_size = buffer_size_o

        # Prompts
        in_prompt_o = self.get_option('in_prompt')
        out_prompt_o = self.get_option('out_prompt')
        if in_prompt_o:
            spy_cfg.IPythonWidget.in_prompt = in_prompt_o
        if out_prompt_o:
            spy_cfg.IPythonWidget.out_prompt = out_prompt_o

        # Merge IPython and Spyder configs. Spyder prefs will have prevalence
        # over IPython ones
        ip_cfg._merge(spy_cfg)
        return ip_cfg

    #------ Private API -------------------------------------------------------
    def _create_loading_page(self):
        loading_template = Template(LOADING)
        loading_img = get_image_path('loading_sprites.png')
        if os.name == 'nt':
            loading_img = loading_img.replace('\\', '/')
        message = _("Connecting to kernel...")
        page = loading_template.substitute(css_path=CSS_PATH,
                                           loading_img=loading_img,
                                           message=message)
        return page

    #---- Qt methods ----------------------------------------------------------
    def closeEvent(self, event):
        """
        Reimplement Qt method to stop sending the custom_restart_kernel_died
        signal
        """
        kc = self.shellwidget.kernel_client
        if kc is not None:
            kc.hb_channel.pause()
Exemple #23
0
class IPythonShellWidget(RichIPythonWidget):
    """
    Spyder's IPython shell widget

    This class has custom control and page_control widgets, additional methods
    to provide missing functionality and a couple more keyboard shortcuts.
    """
    focus_changed = Signal()
    new_client = Signal()

    def __init__(self, *args, **kw):
        # To override the Qt widget used by RichIPythonWidget
        self.custom_control = IPythonControlWidget
        self.custom_page_control = IPythonPageControlWidget
        super(IPythonShellWidget, self).__init__(*args, **kw)
        self.set_background_color()

        # --- Spyder variables ---
        self.ipyclient = None

        # --- Keyboard shortcuts ---
        self.shortcuts = self.create_shortcuts()

        # --- IPython variables ---
        # To send an interrupt signal to the Spyder kernel
        self.custom_interrupt = True

        # To restart the Spyder kernel in case it dies
        self.custom_restart = True

    #---- Public API ----------------------------------------------------------
    def set_ipyclient(self, ipyclient):
        """Bind this shell widget to an IPython client one"""
        self.ipyclient = ipyclient
        self.exit_requested.connect(ipyclient.exit_callback)

    def long_banner(self):
        """Banner for IPython widgets with pylab message"""
        from IPython.core.usage import default_gui_banner
        banner = default_gui_banner

        pylab_o = CONF.get('ipython_console', 'pylab', True)
        autoload_pylab_o = CONF.get('ipython_console', 'pylab/autoload', True)
        mpl_installed = programs.is_module_installed('matplotlib')
        if mpl_installed and (pylab_o and autoload_pylab_o):
            pylab_message = ("\nPopulating the interactive namespace from "
                             "numpy and matplotlib")
            banner = banner + pylab_message

        sympy_o = CONF.get('ipython_console', 'symbolic_math', True)
        if sympy_o:
            lines = """
These commands were executed:
>>> from __future__ import division
>>> from sympy import *
>>> x, y, z, t = symbols('x y z t')
>>> k, m, n = symbols('k m n', integer=True)
>>> f, g, h = symbols('f g h', cls=Function)
"""
            banner = banner + lines
        return banner

    def short_banner(self):
        """Short banner with Python and IPython versions"""
        from IPython.core.release import version
        py_ver = '%d.%d.%d' % (sys.version_info[0], sys.version_info[1],
                               sys.version_info[2])
        banner = 'Python %s on %s -- IPython %s' % (py_ver, sys.platform,
                                                    version)
        return banner

    def clear_console(self):
        self.execute("%clear")

    def write_to_stdin(self, line):
        """
        Send raw characters to the IPython kernel through stdin
        but only if the kernel is currently looking for raw input.
        """
        if self._reading:
            try:
                self.kernel_client.stdin_channel.input(line)
            except AttributeError:
                self.kernel_client.input(line)

    def set_background_color(self):
        lightbg_o = CONF.get('ipython_console', 'light_color')
        if not lightbg_o:
            self.set_default_style(colors='linux')

    def create_shortcuts(self):
        inspect = create_shortcut(self._control.inspect_current_object,
                                  context='Console',
                                  name='Inspect current object',
                                  parent=self)
        clear_console = create_shortcut(self.clear_console,
                                        context='Console',
                                        name='Clear shell',
                                        parent=self)

        # Fixed shortcuts
        new_shortcut("Ctrl+T", self, lambda: self.new_client.emit())
        new_shortcut(SHORTCUT_INLINE, self,
                     lambda: self._control.enter_array_inline())
        new_shortcut(SHORTCUT_TABLE, self,
                     lambda: self._control.enter_array_table())

        return [inspect, clear_console]

    def get_signature(self, content):
        """Get signature from inspect reply content"""
        data = content.get('data', {})
        text = data.get('text/plain', '')
        if text:
            text = ANSI_OR_SPECIAL_PATTERN.sub('', text)
            line = self._control.get_current_line_to_cursor()
            name = line[:-1].split('.')[-1]
            argspec = getargspecfromtext(text)
            if argspec:
                # This covers cases like np.abs, whose docstring is
                # the same as np.absolute and because of that a proper
                # signature can't be obtained correctly
                signature = name + argspec
            else:
                signature = getsignaturefromtext(text, name)
            return signature
        else:
            return ''

    #---- IPython private methods ---------------------------------------------
    def _context_menu_make(self, pos):
        """Reimplement the IPython context menu"""
        menu = super(IPythonShellWidget, self)._context_menu_make(pos)
        return self.ipyclient.add_actions_to_context_menu(menu)

    def _banner_default(self):
        """
        Reimplement banner creation to let the user decide if he wants a
        banner or not
        """
        banner_o = CONF.get('ipython_console', 'show_banner', True)
        if banner_o:
            return self.long_banner()
        else:
            return self.short_banner()

    def _handle_object_info_reply(self, rep):
        """
        Reimplement call tips to only show signatures, using the same style
        from our Editor and External Console too
        Note: For IPython 2-
        """
        self.log.debug("oinfo: %s", rep.get('content', ''))
        cursor = self._get_cursor()
        info = self._request_info.get('call_tip')
        if info and info.id == rep['parent_header']['msg_id'] and \
          info.pos == cursor.position():
            content = rep['content']
            if content.get('ismagic', False):
                call_info, doc = None, None
            else:
                call_info, doc = call_tip(content, format_call=True)
                if call_info is None and doc is not None:
                    name = content['name'].split('.')[-1]
                    argspec = getargspecfromtext(doc)
                    if argspec:
                        # This covers cases like np.abs, whose docstring is
                        # the same as np.absolute and because of that a proper
                        # signature can't be obtained correctly
                        call_info = name + argspec
                    else:
                        call_info = getsignaturefromtext(doc, name)
            if call_info:
                self._control.show_calltip(_("Arguments"),
                                           call_info,
                                           signature=True,
                                           color='#2D62FF')

    def _handle_inspect_reply(self, rep):
        """
        Reimplement call tips to only show signatures, using the same style
        from our Editor and External Console too
        Note: For IPython 3+
        """
        cursor = self._get_cursor()
        info = self._request_info.get('call_tip')
        if info and info.id == rep['parent_header']['msg_id'] and \
          info.pos == cursor.position():
            content = rep['content']
            if content.get('status') == 'ok' and content.get('found', False):
                signature = self.get_signature(content)
                if signature:
                    self._control.show_calltip(_("Arguments"),
                                               signature,
                                               signature=True,
                                               color='#2D62FF')

    #---- Qt methods ----------------------------------------------------------
    def focusInEvent(self, event):
        """Reimplement Qt method to send focus change notification"""
        self.focus_changed.emit()
        return super(IPythonShellWidget, self).focusInEvent(event)

    def focusOutEvent(self, event):
        """Reimplement Qt method to send focus change notification"""
        self.focus_changed.emit()
        return super(IPythonShellWidget, self).focusOutEvent(event)
Exemple #24
0
class NamespaceBrowser(QWidget):
    """Namespace browser (global variables explorer widget)"""
    sig_option_changed = Signal(str, object)
    sig_collapse = Signal()
    
    def __init__(self, parent):
        QWidget.__init__(self, parent)
        
        self.shellwidget = None
        self.is_internal_shell = None
        self.ipyclient = None
        self.is_ipykernel = None
        
        self.is_visible = True # Do not modify: light mode won't work!
        
        self.setup_in_progress = None
        
        # Remote dict editor settings
        self.check_all = None
        self.exclude_private = None
        self.exclude_uppercase = None
        self.exclude_capitalized = None
        self.exclude_unsupported = None
        self.excluded_names = None
        self.truncate = None
        self.minmax = None
        self.remote_editing = None
        self.autorefresh = None
        
        self.editor = None
        self.exclude_private_action = None
        self.exclude_uppercase_action = None
        self.exclude_capitalized_action = None
        self.exclude_unsupported_action = None
        
        self.filename = None
            
    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 = DictEditorTableView(self, None, truncate=truncate,
                                              minmax=minmax)
        else:
            self.editor = RemoteDictEditorTableView(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_time_series_func=self.is_time_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
        hlayout = QHBoxLayout()
        vlayout = QVBoxLayout()
        toolbar = self.setup_toolbar(exclude_private, exclude_uppercase,
                                     exclude_capitalized, exclude_unsupported,
                                     autorefresh)
        vlayout.setAlignment(Qt.AlignTop)
        for widget in toolbar:
            vlayout.addWidget(widget)
        hlayout.addWidget(self.editor)
        hlayout.addLayout(vlayout)
        self.setLayout(hlayout)
        hlayout.setContentsMargins(0, 0, 0, 0)

        self.sig_option_changed.connect(self.option_changed)
        
    def set_shellwidget(self, shellwidget):
        """Bind shellwidget instance to namespace browser"""
        self.shellwidget = shellwidget
        from spyderlib.widgets import internalshell
        self.is_internal_shell = isinstance(self.shellwidget,
                                            internalshell.InternalShell)
        self.is_ipykernel = self.shellwidget.is_ipykernel
        if not self.is_internal_shell:
            shellwidget.set_namespacebrowser(self)
    
    def set_ipyclient(self, ipyclient):
        """Bind ipyclient instance to namespace browser"""
        self.ipyclient = ipyclient
        
    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

    def option_changed(self, option, value):
        """Option has changed"""
        setattr(self, to_text_string(option), value)
        if not self.is_internal_shell:
            settings = self.get_view_settings()
            communicate(self._get_sock(),
                        'set_remote_view_settings()', settings=[settings])
        
    def visibility_changed(self, enable):
        """Notify the widget whether its container (the namespace browser 
        plugin is visible or not"""
        self.is_visible = enable
        if enable:
            self.refresh_table()

    @Slot(bool)
    def toggle_auto_refresh(self, state):
        """Toggle auto refresh state"""
        self.autorefresh = state
        if not self.setup_in_progress and not self.is_internal_shell:
            communicate(self._get_sock(),
                        "set_monitor_auto_refresh(%r)" % state)
            
    def _get_sock(self):
        """Return socket connection"""
        return self.shellwidget.introspection_socket
    
    def get_internal_shell_filter(self, mode, check_all=None):
        """
        Return internal shell data types filter:
            * check_all: check all elements data types for sequences
              (dict, list, tuple)
            * mode (string): 'editable' or 'picklable'
        """
        assert mode in list(SUPPORTED_TYPES.keys())
        if check_all is None:
            check_all = self.check_all
        def wsfilter(input_dict, check_all=check_all,
                     filters=tuple(SUPPORTED_TYPES[mode])):
            """Keep only objects that can be pickled"""
            return globalsfilter(
                         input_dict, check_all=check_all, filters=filters,
                         exclude_private=self.exclude_private,
                         exclude_uppercase=self.exclude_uppercase,
                         exclude_capitalized=self.exclude_capitalized,
                         exclude_unsupported=self.exclude_unsupported,
                         excluded_names=self.excluded_names)
        return wsfilter

    def get_view_settings(self):
        """Return dict editor view settings"""
        settings = {}
        for name in REMOTE_SETTINGS:
            settings[name] = getattr(self, name)
        return settings

    @Slot()
    def refresh_table(self):
        """Refresh variable table"""
        if self.is_visible and self.isVisible():
            if self.is_internal_shell:
                # Internal shell
                wsfilter = self.get_internal_shell_filter('editable')
                self.editor.set_filter(wsfilter)
                interpreter = self.shellwidget.interpreter
                if interpreter is not None:
                    self.editor.set_data(interpreter.namespace)
                    self.editor.adjust_columns()
            elif self.shellwidget.is_running():
    #            import time; print >>STDOUT, time.ctime(time.time()), "Refreshing namespace browser"
                sock = self._get_sock()
                if sock is None:
                    return
                try:
                    communicate(sock, "refresh()")
                except socket.error:
                    # Process was terminated before calling this method
                    pass                
                
    def process_remote_view(self, remote_view):
        """Process remote view"""
        if remote_view is not None:
            self.set_data(remote_view)
        
    #------ Remote Python process commands ------------------------------------
    def get_value(self, name):
        value = monitor_get_global(self._get_sock(), name)
        if value is None:
            if communicate(self._get_sock(), '%s is not None' % name):
                import pickle
                msg = to_text_string(_("Object <b>%s</b> is not picklable")
                                     % name)
                raise pickle.PicklingError(msg)
        return value
        
    def set_value(self, name, value):
        monitor_set_global(self._get_sock(), name, value)
        self.refresh_table()
        
    def remove_values(self, names):
        for name in names:
            monitor_del_global(self._get_sock(), name)
        self.refresh_table()
        
    def copy_value(self, orig_name, new_name):
        monitor_copy_global(self._get_sock(), orig_name, new_name)
        self.refresh_table()
        
    def is_list(self, name):
        """Return True if variable is a list or a tuple"""
        return communicate(self._get_sock(),
                           'isinstance(%s, (tuple, list))' % name)
        
    def is_dict(self, name):
        """Return True if variable is a dictionary"""
        return communicate(self._get_sock(), 'isinstance(%s, dict)' % name)
        
    def get_len(self, name):
        """Return sequence length"""
        return communicate(self._get_sock(), "len(%s)" % name)
        
    def is_array(self, name):
        """Return True if variable is a NumPy array"""
        return communicate(self._get_sock(), 'is_array("%s")' % name)
        
    def is_image(self, name):
        """Return True if variable is a PIL.Image image"""
        return communicate(self._get_sock(), 'is_image("%s")' % name)
    
    def is_data_frame(self, name):
        """Return True if variable is a data_frame"""
        return communicate(self._get_sock(),
             "isinstance(globals()['%s'], DataFrame)" % name)
    
    def is_time_series(self, name):
        """Return True if variable is a data_frame"""
        return communicate(self._get_sock(),
             "isinstance(globals()['%s'], TimeSeries)" % name)   
        
    def get_array_shape(self, name):
        """Return array's shape"""
        return communicate(self._get_sock(), "%s.shape" % name)
        
    def get_array_ndim(self, name):
        """Return array's ndim"""
        return communicate(self._get_sock(), "%s.ndim" % name)
        
    def plot(self, name, funcname):
        command = "import spyderlib.pyplot; "\
                  "__fig__ = spyderlib.pyplot.figure(); "\
                  "__items__ = getattr(spyderlib.pyplot, '%s')(%s); "\
                  "spyderlib.pyplot.show(); "\
                  "del __fig__, __items__;" % (funcname, name)
        if self.is_ipykernel:
            self.ipyclient.shellwidget.execute("%%varexp --%s %s" % (funcname,
                                                                   name))
        else:
            self.shellwidget.send_to_process(command)
        
    def imshow(self, name):
        command = "import spyderlib.pyplot; " \
                  "__fig__ = spyderlib.pyplot.figure(); " \
                  "__items__ = spyderlib.pyplot.imshow(%s); " \
                  "spyderlib.pyplot.show(); del __fig__, __items__;" % name
        if self.is_ipykernel:
            self.ipyclient.shellwidget.execute("%%varexp --imshow %s" % name)
        else:
            self.shellwidget.send_to_process(command)
        
    def show_image(self, name):
        command = "%s.show()" % name
        if self.is_ipykernel:
            self.ipyclient.shellwidget.execute(command)
        else:
            self.shellwidget.send_to_process(command)
        
    def oedit(self, name):
        command = "from spyderlib.widgets.objecteditor import oedit; " \
                  "oedit('%s', modal=False, namespace=locals());" % name
        self.shellwidget.send_to_process(command)
        
    #------ Set, load and save data -------------------------------------------
    def set_data(self, data):
        """Set data"""
        if data != self.editor.model.get_data():
            self.editor.set_data(data)
            self.editor.adjust_columns()
        
    def collapse(self):
        """Collapse"""
        self.sig_collapse.emit()

    @Slot(list)
    def import_data(self, filenames=None):
        """Import data from text file"""
        title = _("Import data")
        if filenames is None:
            if self.filename is None:
                basedir = getcwd()
            else:
                basedir = osp.dirname(self.filename)
            filenames, _selfilter = getopenfilenames(self, title, basedir,
                                                     iofunctions.load_filters)
            if not filenames:
                return
        elif is_text_string(filenames):
            filenames = [filenames]

        for filename in filenames:
            self.filename = to_text_string(filename)
            ext = osp.splitext(self.filename)[1].lower()

            if ext not in iofunctions.load_funcs:
                buttons = QMessageBox.Yes | QMessageBox.Cancel
                answer = QMessageBox.question(self, title,
                            _("<b>Unsupported file extension '%s'</b><br><br>"
                              "Would you like to import it anyway "
                              "(by selecting a known file format)?"
                              ) % ext, buttons)
                if answer == QMessageBox.Cancel:
                    return
                formats = list(iofunctions.load_extensions.keys())
                item, ok = QInputDialog.getItem(self, title,
                                                _('Open file as:'),
                                                formats, 0, False)
                if ok:
                    ext = iofunctions.load_extensions[to_text_string(item)]
                else:
                    return

            load_func = iofunctions.load_funcs[ext]
                
            # 'import_wizard' (self.setup_io)
            if is_text_string(load_func):
                # Import data with import wizard
                error_message = None
                try:
                    text, _encoding = encoding.read(self.filename)
                    if self.is_internal_shell:
                        self.editor.import_from_string(text)
                    else:
                        base_name = osp.basename(self.filename)
                        editor = ImportWizard(self, text, title=base_name,
                                      varname=fix_reference_name(base_name))
                        if editor.exec_():
                            var_name, clip_data = editor.get_data()
                            monitor_set_global(self._get_sock(),
                                               var_name, clip_data)
                except Exception as error:
                    error_message = str(error)
            else:
                QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
                QApplication.processEvents()
                if self.is_internal_shell:
                    namespace, error_message = load_func(self.filename)
                    interpreter = self.shellwidget.interpreter
                    for key in list(namespace.keys()):
                        new_key = fix_reference_name(key,
                                     blacklist=list(interpreter.namespace.keys()))
                        if new_key != key:
                            namespace[new_key] = namespace.pop(key)
                    if error_message is None:
                        interpreter.namespace.update(namespace)
                else:
                    error_message = monitor_load_globals(self._get_sock(),
                                                         self.filename, ext)
                QApplication.restoreOverrideCursor()
                QApplication.processEvents()
    
            if error_message is not None:
                QMessageBox.critical(self, title,
                                     _("<b>Unable to load '%s'</b>"
                                       "<br><br>Error message:<br>%s"
                                       ) % (self.filename, error_message))
            self.refresh_table()
            
    @Slot()
    def save_data(self, filename=None):
        """Save data"""
        if filename is None:
            filename = self.filename
            if filename is None:
                filename = getcwd()
            filename, _selfilter = getsavefilename(self, _("Save data"),
                                                   filename,
                                                   iofunctions.save_filters)
            if filename:
                self.filename = filename
            else:
                return False
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        QApplication.processEvents()
        if self.is_internal_shell:
            wsfilter = self.get_internal_shell_filter('picklable',
                                                      check_all=True)
            namespace = wsfilter(self.shellwidget.interpreter.namespace).copy()
            error_message = iofunctions.save(namespace, filename)
        else:
            settings = self.get_view_settings()
            error_message = monitor_save_globals(self._get_sock(),
                                                 settings, filename)
        QApplication.restoreOverrideCursor()
        QApplication.processEvents()
        if error_message is not None:
            QMessageBox.critical(self, _("Save data"),
                            _("<b>Unable to save current workspace</b>"
                              "<br><br>Error message:<br>%s") % error_message)
        self.save_button.setEnabled(self.filename is not None)
Exemple #25
0
class Profiler(ProfilerWidget, SpyderPluginMixin):
    """Profiler (after python's profile and pstats)"""
    CONF_SECTION = 'profiler'
    CONFIGWIDGET_CLASS = ProfilerConfigPage
    edit_goto = Signal(str, int, str)

    def __init__(self, parent=None):
        ProfilerWidget.__init__(self,
                                parent=parent,
                                max_entries=self.get_option('max_entries', 50))
        SpyderPluginMixin.__init__(self, parent)

        # Initialize plugin
        self.initialize_plugin()

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _("Profiler")

    def get_plugin_icon(self):
        """Return widget icon"""
        return get_icon('profiler.png')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.datatree

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []

    def on_first_registration(self):
        """Action to be performed on first plugin registration"""
        self.main.tabify_plugins(self.main.inspector, self)
        self.dockwidget.hide()

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.edit_goto.connect(self.main.editor.load)
        self.redirect_stdio.connect(self.main.redirect_internalshell_stdio)
        self.main.add_dockwidget(self)

        profiler_act = create_action(self,
                                     _("Profile"),
                                     icon=get_icon('profiler.png'),
                                     triggered=self.run_profiler)
        profiler_act.setEnabled(is_profiler_installed())
        self.register_shortcut(profiler_act,
                               context="Profiler",
                               name="Run profiler")

        self.main.run_menu_actions += [profiler_act]
        self.main.editor.pythonfile_dependent_actions += [profiler_act]

    def refresh_plugin(self):
        """Refresh profiler widget"""
        #self.remove_obsolete_items()  # FIXME: not implemented yet

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        # The history depth option will be applied at
        # next Spyder startup, which is soon enough
        pass

    #------ Public API ---------------------------------------------------------
    def run_profiler(self):
        """Run profiler"""
        self.analyze(self.main.editor.get_current_filename())

    def analyze(self, filename):
        """Reimplement analyze method"""
        if self.dockwidget and not self.ismaximized:
            self.dockwidget.setVisible(True)
            self.dockwidget.setFocus()
            self.dockwidget.raise_()
        pythonpath = self.main.get_spyder_pythonpath()
        runconf = runconfig.get_run_configuration(filename)
        wdir, args = None, None
        if runconf is not None:
            if runconf.wdir_enabled:
                wdir = runconf.wdir
            if runconf.args_enabled:
                args = runconf.args
        ProfilerWidget.analyze(self,
                               filename,
                               wdir=wdir,
                               args=args,
                               pythonpath=pythonpath)
Exemple #26
0
class ConfigPage(QWidget):
    """Base class for configuration page in Preferences"""

    # Signals
    apply_button_enabled = Signal(bool)
    show_this_page = Signal()

    def __init__(self, parent, apply_callback=None):
        QWidget.__init__(self, parent)
        self.apply_callback = apply_callback
        self.is_modified = False

    def initialize(self):
        """
        Initialize configuration page:
            * setup GUI widgets
            * load settings and change widgets accordingly
        """
        self.setup_page()
        self.load_from_conf()

    def get_name(self):
        """Return configuration page name"""
        raise NotImplementedError

    def get_icon(self):
        """Return configuration page icon (24x24)"""
        raise NotImplementedError

    def setup_page(self):
        """Setup configuration page widget"""
        raise NotImplementedError

    def set_modified(self, state):
        self.is_modified = state
        self.apply_button_enabled.emit(state)

    def is_valid(self):
        """Return True if all widget contents are valid"""
        raise NotImplementedError

    def apply_changes(self):
        """Apply changes callback"""
        if self.is_modified:
            self.save_to_conf()
            if self.apply_callback is not None:
                self.apply_callback()

            # Since the language cannot be retrieved by CONF and the language
            # is needed before loading CONF, this is an extra method needed to
            # ensure that when changes are applied, they are copied to a
            # specific file storing the language value. This only applies to
            # the main section config.
            if self.CONF_SECTION == u'main':
                self._save_lang()

            for restart_option in self.restart_options:
                if restart_option in self.changed_options:
                    self.prompt_restart_required()
                    break  # Ensure a single popup is displayed
            self.set_modified(False)

    def load_from_conf(self):
        """Load settings from configuration file"""
        raise NotImplementedError

    def save_to_conf(self):
        """Save settings to configuration file"""
        raise NotImplementedError
Exemple #27
0
class ConfigDialog(QDialog):
    """Spyder configuration ('Preferences') dialog box"""

    # Signals
    check_settings = Signal()
    size_change = Signal(QSize)

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self.main = parent

        # Widgets
        self.pages_widget = QStackedWidget()
        self.contents_widget = QListWidget()
        self.button_reset = QPushButton(_('Reset to defaults'))

        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply
                                | QDialogButtonBox.Cancel)
        self.apply_btn = bbox.button(QDialogButtonBox.Apply)

        # Widgets setup
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowTitle(_('Preferences'))
        self.setWindowIcon(ima.icon('configure'))
        self.contents_widget.setMovement(QListView.Static)
        self.contents_widget.setSpacing(1)
        self.contents_widget.setCurrentRow(0)

        # Layout
        hsplitter = QSplitter()
        hsplitter.addWidget(self.contents_widget)
        hsplitter.addWidget(self.pages_widget)

        btnlayout = QHBoxLayout()
        btnlayout.addWidget(self.button_reset)
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)

        vlayout = QVBoxLayout()
        vlayout.addWidget(hsplitter)
        vlayout.addLayout(btnlayout)

        self.setLayout(vlayout)

        # Signals and slots
        self.button_reset.clicked.connect(self.main.reset_spyder)
        self.pages_widget.currentChanged.connect(self.current_page_changed)
        self.contents_widget.currentRowChanged.connect(
            self.pages_widget.setCurrentIndex)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        bbox.clicked.connect(self.button_clicked)

        # Ensures that the config is present on spyder first run
        CONF.set('main', 'interface_language', load_lang_conf())

    def get_current_index(self):
        """Return current page index"""
        return self.contents_widget.currentRow()

    def set_current_index(self, index):
        """Set current page index"""
        self.contents_widget.setCurrentRow(index)

    def get_page(self, index=None):
        """Return page widget"""
        if index is None:
            widget = self.pages_widget.currentWidget()
        else:
            widget = self.pages_widget.widget(index)
        return widget.widget()

    @Slot()
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.pages_widget.count()):
            configpage = self.get_page(index)
            if not configpage.is_valid():
                return
            configpage.apply_changes()
        QDialog.accept(self)

    def button_clicked(self, button):
        if button is self.apply_btn:
            # Apply button was clicked
            configpage = self.get_page()
            if not configpage.is_valid():
                return
            configpage.apply_changes()

    def current_page_changed(self, index):
        widget = self.get_page(index)
        self.apply_btn.setVisible(widget.apply_callback is not None)
        self.apply_btn.setEnabled(widget.is_modified)

    def add_page(self, widget):
        self.check_settings.connect(widget.check_settings)
        widget.show_this_page.connect(lambda row=self.contents_widget.count():
                                      self.contents_widget.setCurrentRow(row))
        widget.apply_button_enabled.connect(self.apply_btn.setEnabled)
        scrollarea = QScrollArea(self)
        scrollarea.setWidgetResizable(True)
        scrollarea.setWidget(widget)
        self.pages_widget.addWidget(scrollarea)
        item = QListWidgetItem(self.contents_widget)
        item.setIcon(widget.get_icon())
        item.setText(widget.get_name())
        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        item.setSizeHint(QSize(0, 25))

    def check_all_settings(self):
        """This method is called to check all configuration page settings
        after configuration dialog has been shown"""
        self.check_settings.emit()

    def resizeEvent(self, event):
        """
        Reimplement Qt method to be able to save the widget's size from the
        main application
        """
        QDialog.resizeEvent(self, event)
        self.size_change.emit(self.size())
Exemple #28
0
class HistoryLog(SpyderPluginWidget):
    """
    History log widget
    """
    CONF_SECTION = 'historylog'
    CONFIGWIDGET_CLASS = HistoryConfigPage
    focus_changed = Signal()

    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()

        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)

        # 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)

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('History log')

    def get_plugin_icon(self):
        """Return widget icon"""
        return ima.icon('history')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.tabwidget.currentWidget()

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def refresh_plugin(self):
        """Refresh tabwidget"""
        if self.tabwidget.count():
            editor = self.tabwidget.currentWidget()
        else:
            editor = None
        self.find_widget.set_editor(editor)

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        history_action = create_action(self,
                                       _("History..."),
                                       None,
                                       ima.icon('history'),
                                       _("Set history maximum entries"),
                                       triggered=self.change_history_depth)
        self.wrap_action = create_action(self,
                                         _("Wrap lines"),
                                         toggled=self.toggle_wrap_mode)
        self.wrap_action.setChecked(self.get_option('wrap'))
        self.menu_actions = [history_action, self.wrap_action]
        return self.menu_actions

    def on_first_registration(self):
        """Action to be performed on first plugin registration"""
        self.main.tabify_plugins(self.main.extconsole, self)

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.focus_changed.connect(self.main.plugin_focus_changed)
        self.main.add_dockwidget(self)
        #        self.main.console.set_historylog(self)
        self.main.console.shell.refresh.connect(self.refresh_plugin)

    def update_font(self):
        """Update font from Preferences"""
        color_scheme = self.get_color_scheme()
        font = self.get_plugin_font()
        for editor in self.editors:
            editor.set_font(font, color_scheme)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        color_scheme_n = 'color_scheme_name'
        color_scheme_o = self.get_color_scheme()
        font_n = 'plugin_font'
        font_o = self.get_plugin_font()
        wrap_n = 'wrap'
        wrap_o = self.get_option(wrap_n)
        self.wrap_action.setChecked(wrap_o)
        for editor in self.editors:
            if font_n in options:
                scs = color_scheme_o if color_scheme_n in options else None
                editor.set_font(font_o, scs)
            elif color_scheme_n in options:
                editor.set_color_scheme(color_scheme_o)
            if wrap_n in options:
                editor.toggle_wrap_mode(wrap_o)

    #------ Private API --------------------------------------------------------
    def move_tab(self, index_from, index_to):
        """
        Move tab (tabs themselves have already been moved by the tabwidget)
        """
        filename = self.filenames.pop(index_from)
        editor = self.editors.pop(index_from)
        icon = self.icons.pop(index_from)

        self.filenames.insert(index_to, filename)
        self.editors.insert(index_to, editor)
        self.icons.insert(index_to, icon)

    #------ Public API ---------------------------------------------------------
    def add_history(self, filename):
        """
        Add new history tab
        Slot for add_history signal emitted by shell instance
        """
        filename = encoding.to_unicode_from_fs(filename)
        if filename in self.filenames:
            return
        editor = codeeditor.CodeEditor(self)
        if osp.splitext(filename)[1] == '.py':
            language = 'py'
            icon = ima.icon('python')
        else:
            language = 'bat'
            icon = ima.icon('cmdprompt')
        editor.setup_editor(linenumbers=False,
                            language=language,
                            scrollflagarea=False)
        editor.focus_changed.connect(lambda: self.focus_changed.emit())
        editor.setReadOnly(True)
        color_scheme = self.get_color_scheme()
        editor.set_font(self.get_plugin_font(), color_scheme)
        editor.toggle_wrap_mode(self.get_option('wrap'))

        text, _ = encoding.read(filename)
        editor.set_text(text)
        editor.set_cursor_position('eof')

        self.editors.append(editor)
        self.filenames.append(filename)
        self.icons.append(icon)
        index = self.tabwidget.addTab(editor, osp.basename(filename))
        self.find_widget.set_editor(editor)
        self.tabwidget.setTabToolTip(index, filename)
        self.tabwidget.setTabIcon(index, icon)
        self.tabwidget.setCurrentIndex(index)

    def append_to_history(self, filename, command):
        """
        Append an entry to history filename
        Slot for append_to_history signal emitted by shell instance
        """
        if not is_text_string(filename):  # filename is a QString
            filename = to_text_string(filename.toUtf8(), 'utf-8')
        command = to_text_string(command)
        index = self.filenames.index(filename)
        self.editors[index].append(command)
        if self.get_option('go_to_eof'):
            self.editors[index].set_cursor_position('eof')
        self.tabwidget.setCurrentIndex(index)

    @Slot()
    def change_history_depth(self):
        "Change history max entries" ""
        depth, valid = QInputDialog.getInteger(self, _('History'),
                                               _('Maximum entries'),
                                               self.get_option('max_entries'),
                                               10, 10000)
        if valid:
            self.set_option('max_entries', depth)

    @Slot(bool)
    def toggle_wrap_mode(self, checked):
        """Toggle wrap mode"""
        if self.tabwidget is None:
            return
        for editor in self.editors:
            editor.toggle_wrap_mode(checked)
        self.set_option('wrap', checked)
Exemple #29
0
class VariableExplorer(QWidget, SpyderPluginMixin):
    """
    Variable Explorer Plugin
    """
    CONF_SECTION = 'variable_explorer'
    CONFIGWIDGET_CLASS = VariableExplorerConfigPage
    sig_option_changed = Signal(str, object)

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        SpyderPluginMixin.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.shellwidgets = {}

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.stack)
        self.setLayout(layout)

        # Initialize plugin
        self.initialize_plugin()

    @staticmethod
    def get_settings():
        """
        Return Variable Explorer settings dictionary
        (i.e. namespace browser settings according to Spyder's configuration file)
        """
        settings = {}
        #        CONF.load_from_ini() # necessary only when called from another process
        for name in REMOTE_SETTINGS:
            settings[name] = CONF.get(VariableExplorer.CONF_SECTION, name)
        return settings

    # ----- Stack accesors ----------------------------------------------------
    def set_current_widget(self, nsb):
        self.stack.setCurrentWidget(nsb)

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, nsb):
        self.stack.removeWidget(nsb)

    def add_widget(self, nsb):
        self.stack.addWidget(nsb)

    # ----- Public API --------------------------------------------------------
    def add_shellwidget(self, shellwidget):
        shellwidget_id = id(shellwidget)
        # Add shell only once: this method may be called two times in a row
        # by the External console plugin (dev. convenience)
        from spyderlib.widgets.externalshell import systemshell
        if isinstance(shellwidget, systemshell.ExternalSystemShell):
            return
        if shellwidget_id not in self.shellwidgets:
            nsb = NamespaceBrowser(self)
            nsb.set_shellwidget(shellwidget)
            nsb.setup(**VariableExplorer.get_settings())
            nsb.sig_option_changed.connect(self.sig_option_changed.emit)
            self.add_widget(nsb)
            self.shellwidgets[shellwidget_id] = nsb
            self.set_shellwidget_from_id(shellwidget_id)
            return nsb

    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(nsb)
            nsb.close()

    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets[shellwidget_id]
            self.set_current_widget(nsb)
            if self.isvisible:
                nsb.visibility_changed(True)

    def import_data(self, fname):
        """Import data in current namespace"""
        if self.count():
            nsb = self.current_widget()
            nsb.refresh_table()
            nsb.import_data(filename=fname)
            if self.dockwidget and not self.ismaximized:
                self.dockwidget.setVisible(True)
                self.dockwidget.raise_()

    #------ SpyderPluginMixin API ---------------------------------------------
    def visibility_changed(self, enable):
        """DockWidget visibility has changed"""
        SpyderPluginMixin.visibility_changed(self, enable)
        for nsb in list(self.shellwidgets.values()):
            nsb.visibility_changed(enable and nsb is self.current_widget())

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Variable explorer')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('dictedit')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def refresh_plugin(self):
        """Refresh widget"""
        pass

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.extconsole.set_variableexplorer(self)
        self.main.add_dockwidget(self)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for nsb in list(self.shellwidgets.values()):
            nsb.setup(**VariableExplorer.get_settings())
        ar_timeout = self.get_option('autorefresh/timeout')
        for shellwidget in self.main.extconsole.shellwidgets:
            shellwidget.set_autorefresh_timeout(ar_timeout)
Exemple #30
0
class ProjectExplorer(ProjectExplorerWidget, SpyderPluginMixin):
    """Project explorer plugin"""
    CONF_SECTION = 'project_explorer'

    open_terminal = Signal(str)
    open_interpreter = Signal(str)
    pythonpath_changed = Signal()
    sig_projects_were_closed = Signal()
    create_module = Signal(str)
    edit = Signal(str)
    removed = Signal(str)
    removed_tree = Signal(str)
    renamed = Signal(str, str)
    redirect_stdio = Signal(bool)

    def __init__(self, parent=None):
        ProjectExplorerWidget.__init__(
            self,
            parent=parent,
            name_filters=self.get_option('name_filters'),
            show_all=self.get_option('show_all', False),
            show_hscrollbar=self.get_option('show_hscrollbar'))
        SpyderPluginMixin.__init__(self, parent)

        # Initialize plugin
        self.initialize_plugin()

        self.treewidget.header().hide()
        self.set_font(self.get_plugin_font())
        self.load_config()

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _("Project explorer")

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.treewidget

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        new_project_act = create_action(self,
                                        text=_('New project...'),
                                        icon=ima.icon('project_expanded'),
                                        triggered=self.create_new_project)

        font_action = create_action(self,
                                    _("&Font..."),
                                    None,
                                    ima.icon('font'),
                                    _("Set font style"),
                                    triggered=self.change_font)
        self.treewidget.common_actions += (None, font_action)

        self.main.file_menu_actions.insert(1, new_project_act)

        return []

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.pythonpath_changed()
        self.main.restore_scrollbar_position.connect(
            self.restore_scrollbar_position)
        self.pythonpath_changed.connect(self.main.pythonpath_changed)
        self.sig_projects_were_closed.connect(self.projects_were_closed)
        self.create_module.connect(self.main.editor.new)
        self.edit.connect(self.main.editor.load)
        self.removed.connect(self.main.editor.removed)
        self.removed_tree.connect(self.main.editor.removed_tree)
        self.renamed.connect(self.main.editor.renamed)
        self.main.editor.set_projectexplorer(self)
        self.main.add_dockwidget(self)

        self.sig_open_file.connect(self.main.open_file)

    def refresh_plugin(self):
        """Refresh project explorer widget"""
        pass

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        self.save_config()
        self.closing_widget()
        return True

    #------ Public API ---------------------------------------------------------
    @Slot()
    def create_new_project(self):
        """Create new project"""
        if self.dockwidget.isHidden():
            self.dockwidget.show()
        self.dockwidget.raise_()
        if not self.treewidget.new_project():
            # Notify dockwidget to schedule a repaint
            self.dockwidget.update()

    def projects_were_closed(self):
        """Project were just closed: checking if related files are opened in 
        the editor and closing them"""
        for fname in self.main.editor.get_filenames():
            if self.treewidget.workspace.is_file_in_closed_project(fname):
                self.main.editor.close_file_from_name(fname)

    @Slot()
    def change_font(self):
        """Change font"""
        font, valid = QFontDialog.getFont(self.get_plugin_font(), self,
                                          _("Select a new font"))
        if valid:
            self.set_font(font)
            self.set_plugin_font(font)

    def set_font(self, font):
        """Set project explorer widget font"""
        self.treewidget.setFont(font)

    def save_config(self):
        """Save configuration: opened projects & tree widget state"""
        self.set_option('workspace', self.get_workspace())
        self.set_option('expanded_state', self.treewidget.get_expanded_state())
        self.set_option('scrollbar_position',
                        self.treewidget.get_scrollbar_position())

    def load_config(self):
        """Load configuration: opened projects & tree widget state"""
        self.set_workspace(self.get_option('workspace', None))
        expanded_state = self.get_option('expanded_state', None)
        # Sometimes the expanded state option may be truncated in .ini file
        # (for an unknown reason), in this case it would be converted to a
        # string by 'userconfig':
        if is_text_string(expanded_state):
            expanded_state = None
        if expanded_state is not None:
            self.treewidget.set_expanded_state(expanded_state)

    def restore_scrollbar_position(self):
        """Restoring scrollbar position after main window is visible"""
        scrollbar_pos = self.get_option('scrollbar_position', None)
        if scrollbar_pos is not None:
            self.treewidget.set_scrollbar_position(scrollbar_pos)