Esempio n. 1
0
 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)
         self.addWidget(nsb)
         self.shellwidgets[shellwidget_id] = nsb
         shellwidget.set_namespacebrowser(nsb)
Esempio n. 2
0
 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.addWidget(nsb)
         self.shellwidgets[shellwidget_id] = nsb
         self.set_shellwidget(shellwidget)
         return nsb
Esempio n. 3
0
 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
Esempio n. 4
0
 def get_shell_widget(self):
     if self.stand_alone:
         self.namespacebrowser = NamespaceBrowser(self)
         self.namespacebrowser.set_shellwidget(self)
         self.connect(self.namespacebrowser, SIGNAL('collapse()'),
                      lambda: self.toggle_globals_explorer(False))
         # Shell splitter
         self.splitter = splitter = QSplitter(Qt.Vertical, self)
         self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'),
                      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
     else:
         return self.shell
Esempio n. 5
0
 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
Esempio n. 6
0
 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
Esempio n. 7
0
    def _init_python_tab(self):
        # Console
        ns = {'current': self.provider, 'selections': self.selections}
        cmds = """
from __future__ import division
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
import guiqwt
import guiqwt.pyplot as guiplt
import guidata
import spykeutils
import spykeviewer
plt.ion()
""".split('\n')
        self.console = InternalShell(self.consoleDock, namespace=ns,
            multithreaded=False, commands=cmds, max_line_count=1000)
        self.consoleDock.setWidget(self.console)
        self.console.clear_terminal()

        # Variable browser
        self.browser = NamespaceBrowser(self.variableExplorerDock)
        self.browser.set_shellwidget(self.console)
        self.browser.setup(check_all=True, exclude_private=True,
            exclude_uppercase=False, exclude_capitalized=False,
            exclude_unsupported=True, excluded_names=[],
            truncate=False, minmax=False, collvalue=False,
            remote_editing=False, inplace=False, autorefresh=False)
        self.variableExplorerDock.setWidget(self.browser)

        # History
        self.history = CodeEditor(self.historyDock)
        self.history.setup_editor(linenumbers=False, language='py',
            scrollflagarea=False)
        self.history.setReadOnly(True)
        self.history.set_text('\n'.join(self.console.history))
        self.history.set_cursor_position('eof')
        self.historyDock.setWidget(self.history)
        self.console.connect(self.console, SIGNAL("refresh()"),
            self._append_python_history)
Esempio n. 8
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)
Esempio n. 9
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)
Esempio n. 10
0
 def import_data(self, filenames=None):
     NamespaceBrowser._orig_import_data(self, filenames)
     self.save_button.setEnabled(self.filename is not None)
Esempio n. 11
0
 def setup(self, *a, **kw):
     NamespaceBrowser._orig_setup(self, *a, **kw)
     self.editor.is_peakmap = self.is_peakmap
     self.editor.is_table = self.is_table
     self.editor.is_tablelist = self.is_tablelist
Esempio n. 12
0
class ExternalPythonShell(ExternalShellBase):
    """External Shell widget: execute Python script in a separate process"""
    SHELL_CLASS = ExtPythonShellWidget
    def __init__(self, parent=None, fname=None, wdir=None, commands=[],
                 interact=False, debug=False, path=[],
                 ipython=False, arguments=None, stand_alone=True,
                 umd_enabled=True, umd_namelist=[], umd_verbose=True):
        self.namespacebrowser = None # namespace browser widget!
        
        self.fname = startup.__file__ if fname is None else fname
        
        self.stand_alone = stand_alone
        
        self.umd_enabled = umd_enabled
        self.umd_namelist = umd_namelist
        self.umd_verbose = umd_verbose
        
        self.namespacebrowser_button = None
        self.cwd_button = None
        self.terminate_button = None
        
        ExternalShellBase.__init__(self, parent, wdir,
                                   history_filename='.history.py')

        self.nsb_timer = QTimer(self) # Namespace browser auto-refresh timer
        self.nsb_timer.setInterval(3000)
        
        if arguments is not None:
            assert isinstance(arguments, basestring)
            self.arguments = arguments
        
        self.ipython = ipython
        if self.ipython:
            interact = False
        
        self.shell.set_externalshell(self)

        self.toggle_globals_explorer(False)
        self.interact_action.setChecked(interact)
        self.debug_action.setChecked(debug)
        
        self.monitor_socket = None
        self.interpreter = fname is None
        
        if self.interpreter:
            self.terminate_button.hide()
        
        self.commands = ["import sys", "sys.path.insert(0, '')"] + commands
        
        # Additional python path list
        self.path = path
        
    def closeEvent(self, event):
        ExternalShellBase.closeEvent(self, event)
        self.disconnect(self.nsb_timer, SIGNAL("timeout()"),
                        self.namespacebrowser.refresh_table)
        
    def get_toolbar_buttons(self):
        ExternalShellBase.get_toolbar_buttons(self)
        if self.namespacebrowser_button is None and self.stand_alone:
            self.namespacebrowser_button = create_toolbutton(self,
                          get_icon('dictedit.png'), self.tr("Variables"),
                          tip=self.tr("Show/hide global variables explorer"),
                          toggled=self.toggle_globals_explorer)
        if self.cwd_button is None:
            self.cwd_button = create_toolbutton(self,
                          get_std_icon('DirOpenIcon'), self.tr("Working directory"),
                          tip=self.tr("Set current working directory"),
                          triggered=self.set_current_working_directory)
        if self.terminate_button is None:
            self.terminate_button = create_toolbutton(self,
                          get_icon('terminate.png'), self.tr("Terminate"),
                          tip=self.tr("Attempts to terminate the process.\n"
                                      "The process may not exit as a result of "
                                      "clicking this button\n"
                                      "(it is given the chance to prompt "
                                      "the user for any unsaved files, etc)."))        
        buttons = [self.cwd_button]
        if self.namespacebrowser_button is not None:
            buttons.append(self.namespacebrowser_button)
        buttons += [self.run_button, self.options_button,
                    self.terminate_button, self.kill_button]
        return buttons

    def get_options_menu(self):
        self.interact_action = create_action(self, self.tr("Interact"))
        self.interact_action.setCheckable(True)
        self.debug_action = create_action(self, self.tr("Debug"))
        self.debug_action.setCheckable(True)
        self.args_action = create_action(self, self.tr("Arguments..."),
                                         triggered=self.get_arguments)
        return [self.interact_action, self.debug_action, self.args_action]

    def is_interpreter(self):
        """Return True if shellwidget is a Python/IPython interpreter"""
        return self.interpreter
        
    def set_namespacebrowser(self, namespacebrowser):
        """Set namespace browser *widget*"""
        self.namespacebrowser = namespacebrowser
        
    def get_shell_widget(self):
        if self.stand_alone:
            self.namespacebrowser = NamespaceBrowser(self)
            self.namespacebrowser.set_shellwidget(self)
            self.connect(self.namespacebrowser, SIGNAL('collapse()'),
                         lambda: self.toggle_globals_explorer(False))
            # Shell splitter
            self.splitter = splitter = QSplitter(Qt.Vertical, self)
            self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'),
                         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
        else:
            return self.shell
    
    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.interpreter)
        self.debug_action.setEnabled(not state and not self.interpreter)
        self.args_action.setEnabled(not state and
                                    (not self.interpreter or self.ipython))
        if state:
            if self.arguments:
                argstr = self.tr("Arguments: %1").arg(self.arguments)
            else:
                argstr = self.tr("No argument")
        else:
            argstr = self.tr("Arguments...")
        self.args_action.setText(argstr)
        self.terminate_button.setEnabled(state)
        if not state:
            self.toggle_globals_explorer(False)
        self.cwd_button.setEnabled(state)
        if self.namespacebrowser_button is not None:
            self.namespacebrowser_button.setEnabled(state)
    
    def create_process(self):
        self.shell.clear()
            
        self.process = QProcess(self)
        if self.ipython:
            self.process.setProcessChannelMode(QProcess.MergedChannels)
        else:
            self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.connect(self.shell, SIGNAL("wait_for_ready_read()"),
                     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 self.interact_action.isChecked():
            p_args.append('-i')
        if self.debug_action.isChecked():
            p_args.extend(['-m', 'pdb'])
        if os.name == 'nt':
            # When calling pdb on Windows, one has to double the backslashes 
            # to help Python escaping these characters (otherwise, for example,
            # '\t' will be interpreted as a tabulation):
            p_args.append(self.fname.replace(os.sep, os.sep*2))
        else:
            p_args.append(self.fname)
        
        env = self.process.systemEnvironment()
        
        # Monitor
        env.append('SHELL_ID=%s' % id(self))
        from spyderlib.widgets.externalshell.monitor import start_server
        server, port = start_server()
        self.notification_thread = server.register(str(id(self)), self)
        self.connect(self.notification_thread, SIGNAL('refresh()'),
                     self.namespacebrowser.refresh_table)
        env.append('SPYDER_PORT=%d' % port)
        
        # Python init commands (interpreter only)
        if self.commands and self.interpreter:
            env.append('PYTHONINITCOMMANDS=%s' % ';'.join(self.commands))
            self.process.setEnvironment(env)
        
        # User Module Deleter
        if self.interpreter:
            env.append('UMD_ENABLED=%r' % self.umd_enabled)
            env.append('UMD_NAMELIST=%s' % ','.join(self.umd_namelist))
            env.append('UMD_VERBOSE=%r' % self.umd_verbose)
        
        # IPython related configuration
        if self.ipython:
            env.append('IPYTHON=True')
            # Do not call msvcrt.getch in IPython.genutils.page_more:
            env.append('TERM=emacs')
            
        pathlist = []

        # Fix encoding with custom "sitecustomize.py"
        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)
        
        self.process.setEnvironment(env)
        #-------------------------Python specific-------------------------------
            
        if self.arguments:
            p_args.extend( self.arguments.split(' ') )
                        
        self.connect(self.process, SIGNAL("readyReadStandardOutput()"),
                     self.write_output)
        self.connect(self.process, SIGNAL("readyReadStandardError()"),
                     self.write_error)
        self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"),
                     self.finished)

        self.connect(self.terminate_button, SIGNAL("clicked()"),
                     self.process.terminate)
        self.connect(self.kill_button, SIGNAL("clicked()"),
                     self.process.kill)
        
        #-------------------------Python specific-------------------------------
        self.process.start(sys.executable, p_args)
        #-------------------------Python specific-------------------------------
            
        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, self.tr("Error"),
                                 self.tr("Process failed to start"))
        else:
            self.shell.setFocus()
            self.emit(SIGNAL('started()'))
            self.connect(self.nsb_timer, SIGNAL("timeout()"),
                         self.namespacebrowser.auto_refresh)
            self.nsb_timer.start()
            
        return self.process
    
#===============================================================================
#    Input/Output
#===============================================================================
    def write_error(self):
        #---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, qstr):
        if not isinstance(qstr, QString):
            qstr = QString(qstr)
        if not qstr.endsWith('\n'):
            qstr.append('\n')
        self.process.write(qstr.toLocal8Bit())
        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):
        communicate(self.monitor_socket, "thread.interrupt_main()")
            
#===============================================================================
#    Globals explorer
#===============================================================================
    def toggle_globals_explorer(self, state):
        if self.stand_alone:
            self.splitter.setSizes([1, 1 if state else 0])
            self.namespacebrowser_button.setChecked(state)
            if state:
                self.namespacebrowser.refresh_table()
        
    def splitter_moved(self, pos, index):
        self.namespacebrowser_button.setChecked( self.splitter.sizes()[1] )

#===============================================================================
#    Current working directory
#===============================================================================
    def set_current_working_directory(self):
        cwd = self.shell.get_cwd()
        self.emit(SIGNAL('redirect_stdio(bool)'), False)
        directory = QFileDialog.getExistingDirectory(self,
                                             self.tr("Select directory"), cwd)
        if not directory.isEmpty():
            self.shell.set_cwd(unicode(directory))
        self.emit(SIGNAL('redirect_stdio(bool)'), True)
Esempio n. 13
0
class ExternalPythonShell(ExternalShellBase):
    """External Shell widget: execute Python script in a separate process"""
    SHELL_CLASS = ExtPythonShellWidget
    def __init__(self, parent=None, fname=None, wdir=None,
                 interact=False, debug=False, path=[], python_args='',
                 ipython_shell=False, ipython_kernel=False,
                 arguments='', stand_alone=None,
                 umd_enabled=True, umd_namelist=[], umd_verbose=True,
                 pythonstartup=None, pythonexecutable=None,
                 monitor_enabled=True, mpl_patch_enabled=True,
                 mpl_backend=None, ets_backend='qt4', qt_api=None, pyqt_api=0,
                 install_qt_inputhook=True, 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.pythonstartup = pythonstartup
        self.pythonexecutable = pythonexecutable
        self.monitor_enabled = monitor_enabled
        self.mpl_patch_enabled = mpl_patch_enabled
        self.mpl_backend = mpl_backend
        self.ets_backend = ets_backend
        self.qt_api = qt_api
        self.pyqt_api = pyqt_api
        self.install_qt_inputhook = install_qt_inputhook
        self.ignore_sip_setapi_errors = ignore_sip_setapi_errors
        self.merge_output_channels = merge_output_channels
        self.colorize_sys_stderr = colorize_sys_stderr
        self.umd_enabled = umd_enabled
        self.umd_namelist = umd_namelist
        self.umd_verbose = umd_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, 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)
        
        self.python_args = None
        if python_args:
            assert isinstance(python_args, basestring)
            self.python_args = python_args
        
        assert isinstance(arguments, basestring)
        self.arguments = arguments
        
        self.is_ipython_shell = ipython_shell
        self.is_ipython_kernel = ipython_kernel
        if self.is_ipython_shell or self.is_ipython_kernel:
            interact = False
            # Running our custom startup script for IPython sessions:
            # (see spyderlib/widgets/externalshell/startup.py)
            self.fname = get_module_source_path(
                            'spyderlib.widgets.externalshell', 'startup.py')
        else:
            self.fname = fname
        
        self.shell.set_externalshell(self)

        self.toggle_globals_explorer(False)
        self.interact_action.setChecked(interact)
        self.debug_action.setChecked(debug)
        
        self.introspection_socket = None
        self.is_interpreter = fname is None
        
        if self.is_interpreter:
            self.terminate_button.hide()
        
        # Additional python path list
        self.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('terminate.png'),
                  tip=_("""Attempts to terminate the process.
The process may not exit as a result of clicking this button
(it is given the chance to prompt 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.options_button,
                    self.terminate_button, self.kill_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)
        run_settings_menu = QMenu(_("Run settings"), self)
        add_actions(run_settings_menu,
                    (self.interact_action, self.debug_action, self.args_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/IPython 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.connect(self.namespacebrowser, SIGNAL('collapse()'),
                         lambda: self.toggle_globals_explorer(False))
            # Shell splitter
            self.splitter = splitter = QSplitter(Qt.Vertical, self)
            self.connect(self.splitter, SIGNAL('splitterMoved(int, int)'),
                         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 or\
                                     self.is_ipython_shell))
        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.connect(self.notification_thread,
                         SIGNAL('refresh_namespace_browser()'),
                         self.namespacebrowser.refresh_table)
            signal = self.notification_thread.sig_process_remote_view
            signal.connect(self.namespacebrowser.process_remote_view)
    
    def create_process(self):
        self.shell.clear()
            
        self.process = QProcess(self)
        if self.merge_output_channels or self.is_ipython_shell:
            self.process.setProcessChannelMode(QProcess.MergedChannels)
        else:
            self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.connect(self.shell, SIGNAL("wait_for_ready_read()"),
                     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'] + get_python_args(self.fname, self.python_args,
                                          self.interact_action.isChecked(),
                                          self.debug_action.isChecked(),
                                          self.arguments)
        
        env = [unicode(_path) for _path in self.process.systemEnvironment()]
        if self.pythonstartup:
            env.append('PYTHONSTARTUP=%s' % self.pythonstartup)
        
        # 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.connect(self.notification_thread, SIGNAL('pdb(QString,int)'),
                         lambda fname, lineno:
                         self.emit(SIGNAL('pdb(QString,int)'), fname, lineno))
            self.connect(self.notification_thread,
                         SIGNAL('new_ipython_kernel(QString)'),
                         lambda args:
                         self.emit(SIGNAL('create_ipython_frontend(QString)'),
                         args))
            self.connect(self.notification_thread,
                         SIGNAL('open_file(QString,int)'),
                         lambda fname, lineno:
                         self.emit(SIGNAL('open_file(QString,int)'),
                                   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)
        env.append('MATPLOTLIB_PATCH=%r' % self.mpl_patch_enabled)
        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('INSTALL_QT_INPUTHOOK=%s' % self.install_qt_inputhook)
        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('UMD_ENABLED=%r' % self.umd_enabled)
            env.append('UMD_NAMELIST=%s' % ','.join(self.umd_namelist))
            env.append('UMD_VERBOSE=%r' % self.umd_verbose)
        
        # IPython related configuration
        if self.is_ipython_shell:
            env.append('IPYTHON=True')
            # Do not call msvcrt.getch in IPython.genutils.page_more:
            env.append('TERM=emacs')
        elif self.is_ipython_kernel:
            env.append('IPYTHON_KERNEL=True')
            
        pathlist = []

        # Fix encoding with custom "sitecustomize.py"
        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)
        
        self.process.setEnvironment(env)
        #-------------------------Python specific-------------------------------
                        
        self.connect(self.process, SIGNAL("readyReadStandardOutput()"),
                     self.write_output)
        self.connect(self.process, SIGNAL("readyReadStandardError()"),
                     self.write_error)
        self.connect(self.process, SIGNAL("finished(int,QProcess::ExitStatus)"),
                     self.finished)
                     
        self.connect(self, SIGNAL('finished()'), self.dialog_manager.close_all)

        self.connect(self.terminate_button, SIGNAL("clicked()"),
                     self.process.terminate)
        self.connect(self.kill_button, SIGNAL("clicked()"),
                     self.process.kill)
        
        #-------------------------Python specific-------------------------------
        executable = self.pythonexecutable
        if executable is None:
            executable = get_python_executable()
        self.process.start(executable, p_args)
        #-------------------------Python specific-------------------------------
            
        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.emit(SIGNAL('started()'))
            
        return self.process
    
#===============================================================================
#    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 isinstance(text, basestring):
            text = unicode(text)
        if self.install_qt_inputhook and not self.is_ipython_shell:
            # For now, the Spyder's input hook does not work with IPython:
            # with IPython v0.10 or non-Windows platforms, this is not a
            # problem. However, with IPython v0.11 on Windows, this will be
            # fixed by patching IPython to force it to use our inputhook.
            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 not self.is_ipython_shell and text.startswith(('%', '!')):
            text = 'evalsc(r"%s")\n' % text
        if not text.endswith('\n'):
            text += '\n'
        self.process.write(locale_codec.fromUnicode(text))
        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
#===============================================================================
    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.
#===============================================================================
    def set_current_working_directory(self):
        """Set current working directory"""
        cwd = self.shell.get_cwd()
        self.emit(SIGNAL('redirect_stdio(bool)'), False)
        directory = getexistingdirectory(self, _("Select directory"), cwd)
        if directory:
            self.shell.set_cwd(directory)
        self.emit(SIGNAL('redirect_stdio(bool)'), True)

    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))
        
    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)
Esempio n. 14
0
class MainWindow(QMainWindow, Ui_MainWindow):
    """ The main window of SpikeViewer
    """

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

        QCoreApplication.setOrganizationName('SpykeUtils')
        QCoreApplication.setApplicationName('Spyke Viewer')

        self.setupUi(self)
        self.dir = os.getcwd()
        self.console = None
        self.progress = ProgressIndicatorDialog(self)
        self.provider_factory = DataProvider
        self.selections = []
        self.provider = None
        self.plugin_paths = []

        # Drag and Drop for selections menu
        self.menuSelections.setAcceptDrops(True)
        self.menuSelections.paintEvent =\
            self.on_menuSelections_paint
        self.menuSelections.mousePressEvent =\
            self.on_menuSelections_mousePressed
        self.menuSelections.mouseMoveEvent =\
            self.on_menuSelections_mouseMoved
        self.menuSelections.dragEnterEvent =\
            self.on_menuSelections_dragEnter
        self.menuSelections.dragMoveEvent =\
            self.on_menuSelections_dragMoved
        self.menuSelections.dropEvent =\
            self.on_menuSelections_drop

        self.seldrag_start_pos = None
        self.seldrag_selection = None
        self.seldrag_target = None
        self.seldrag_target_upper = False

        # Docks
        self.setCentralWidget(None)

        # A lot of docks are only needed for the internal database mode
        self.navigationDBDock.setVisible(False)
        self.filterDBDock.setVisible(False)
        self.spikeSortingDock.setVisible(False)
        self.waveformsDock.setVisible(False)
        self.spikeTrainDock.setVisible(False)

        if not hasattr(self, 'internal_database_mode'):
            self.removeDockWidget(self.navigationDBDock)
            self.removeDockWidget(self.filterDBDock)
            self.removeDockWidget(self.spikeSortingDock)
            self.removeDockWidget(self.waveformsDock)
            self.removeDockWidget(self.spikeTrainDock)

            del self.navigationDBDock
            del self.filterDBDock
            del self.spikeSortingDock
            del self.waveformsDock
            del self.spikeTrainDock

        self.update_view_menu()

        if not hasattr(self, 'internal_database_mode'):
            self.restore_state()

        self.mode_switch()


    def update_view_menu(self):
        self.menuBar.clear()
        self.menuBar.addMenu(self.menuFile)
        self.menuBar.addMenu(self.menuSelections)
        self.menuView = self.createPopupMenu()
        self.menuView.setTitle('View')
        self.menuBar.addMenu(self.menuView)
        self.menuBar.addMenu(self.menuHelp)


    def restore_state(self):
        settings = QSettings()
        if not settings.contains('windowGeometry') or\
           not settings.contains('windowState'):
            self.set_initial_layout()
        else:
            self.restoreGeometry(settings.value('windowGeometry'))
            self.restoreState(settings.value('windowState'))

        if not settings.contains('pluginPaths'):
            if hasattr(sys, 'frozen'):
                module_path = os.path.dirname(sys.executable)
            else:
                file_path = os.path.abspath(os.path.dirname(__file__))
                module_path = os.path.dirname(os.path.dirname(file_path))
            plugin_path = os.path.join(module_path, 'plugins')
            if os.path.isdir(plugin_path):
                self.plugin_paths.append(plugin_path)
        else:
            paths = settings.value('pluginPaths')
            if paths is None:
                self.plugin_paths = []
            else:
                self.plugin_paths = paths

        if not settings.contains('selectionPath'):
            data_path = QDesktopServices.storageLocation(
                QDesktopServices.DataLocation)
            self.selection_path = os.path.join(data_path, 'selections')
        else:
            self.selection_path = settings.value('selectionPath')

        if not settings.contains('dataPath'):
            data_path = QDesktopServices.storageLocation(
                QDesktopServices.DataLocation)
            AnalysisPlugin.data_dir = os.path.join(data_path, 'data')
        else:
            AnalysisPlugin.data_dir = settings.value('dataPath')

        if not settings.contains('remoteScript'):
            if hasattr(sys, 'frozen'):
                path = os.path.dirname(sys.executable)
            else:
                file_path = os.path.abspath(os.path.dirname(__file__))
                path = os.path.dirname(os.path.dirname(file_path))
                path = os.path.join(path, 'bin')
            self.remote_script = os.path.join(path, 'spykeview_analysis')
        else:
            self.remote_script = settings.value('remoteScript')


    def set_initial_layout(self):
        self.setGeometry(self.x(), self.y(), 800, 750)
        self.navigationNeoDock.setVisible(True)

        self.removeDockWidget(self.filterDock)
        self.removeDockWidget(self.analysisDock)
        self.addDockWidget(Qt.RightDockWidgetArea, self.filterDock)
        self.addDockWidget(Qt.RightDockWidgetArea, self.analysisDock)
        self.tabifyDockWidget(self.filterDock, self.analysisDock)
        self.filterDock.setVisible(True)
        self.analysisDock.setVisible(True)

        self.consoleDock.setVisible(False)
        self.variableExplorerDock.setVisible(False)
        self.historyDock.setVisible(False)
        self.tabifyDockWidget(self.consoleDock, self.variableExplorerDock)
        self.tabifyDockWidget(self.variableExplorerDock, self.historyDock)


    def mode_switch(self):
        if self.is_db_mode():
            self.switch_to_db_mode()
        if self.is_neo_mode():
            self.switch_to_neo_mode()

        if self.console:
            self.console.interpreter.locals['current'] = self.provider


    def switch_to_db_mode(self):
        raise NotImplementedError()


    def switch_to_neo_mode(self):
        raise NotImplementedError()


    def _init_python_tab(self):
        # Console
        ns = {'current': self.provider, 'selections': self.selections}
        cmds = """
from __future__ import division
import scipy as sp
import numpy as np
import matplotlib.pyplot as plt
import guiqwt
import guiqwt.pyplot as guiplt
import guidata
import spykeutils
import spykeviewer
plt.ion()
""".split('\n')
        self.console = InternalShell(self.consoleDock, namespace=ns,
            multithreaded=False, commands=cmds, max_line_count=1000)
        self.consoleDock.setWidget(self.console)
        self.console.clear_terminal()

        # Variable browser
        self.browser = NamespaceBrowser(self.variableExplorerDock)
        self.browser.set_shellwidget(self.console)
        self.browser.setup(check_all=True, exclude_private=True,
            exclude_uppercase=False, exclude_capitalized=False,
            exclude_unsupported=True, excluded_names=[],
            truncate=False, minmax=False, collvalue=False,
            remote_editing=False, inplace=False, autorefresh=False)
        self.variableExplorerDock.setWidget(self.browser)

        # History
        self.history = CodeEditor(self.historyDock)
        self.history.setup_editor(linenumbers=False, language='py',
            scrollflagarea=False)
        self.history.setReadOnly(True)
        self.history.set_text('\n'.join(self.console.history))
        self.history.set_cursor_position('eof')
        self.historyDock.setWidget(self.history)
        self.console.connect(self.console, SIGNAL("refresh()"),
            self._append_python_history)


    def _append_python_history(self):
        self.browser.refresh_table()
        self.history.append('\n' + self.console.history[-1])
        self.history.set_cursor_position('eof')


    def is_neo_mode(self):
        return True


    def is_db_mode(self):
        return False


    @pyqtSignature("")
    def on_actionExit_triggered(self):
        self.close()


    def on_menuHelp_triggered(self):
        QMessageBox.about(self, 'How to navigate plots',
            'Zoom:\tHold right mouse button' +
            '\nTranslate:\tHold middle mouse button' +
            '\nHome:\tClick middle mouse button' +
            '\n\nAxis synchronization does not work with some actions. ' +
            'Zoom or translate changed plot to synchronize.')


    def on_menuSelections_mousePressed(self, event):
        if event.button() == Qt.LeftButton:
            action = self.menuSelections.actionAt(event.pos())
            if action:
                selection = action.data()
                if selection:
                    self.seldrag_start_pos = event.pos()
                    self.seldrag_selection = selection
        else:
            self.seldrag_start_pos = None
            self.seldrag_selection = None
            self.seldrag_target = None
        QMenu.mousePressEvent(self.menuSelections, event)


    def on_menuSelections_mouseMoved(self, event):
        if event.buttons() & Qt.LeftButton and self.seldrag_start_pos:
            if ((event.pos() - self.seldrag_start_pos).manhattanLength() >=
                QApplication.startDragDistance()):
                drag = QDrag(self.menuSelections)
                data = QMimeData()
                data.setText(self.seldrag_selection.name)
                drag.setMimeData(data)
                drag.exec_()
                self.seldrag_start_pos = None
                self.seldrag_selection = None
                self.seldrag_target = None
        QMenu.mouseMoveEvent(self.menuSelections, event)


    def on_menuSelections_paint(self, event):
        QMenu.paintEvent(self.menuSelections, event)
        if self.seldrag_target:
            # Paint line where selection will be dropped
            p = QPainter()
            color = QPalette().color(self.menuSelections.foregroundRole())
            pen = QPen(color, 2, Qt.SolidLine)
            p.begin(self.menuSelections)
            p.setPen(pen)
            rect = self.menuSelections.actionGeometry(self.seldrag_target)
            if self.seldrag_target_upper:
                p.drawLine(rect.topLeft(), rect.topRight())
            else:
                p.drawLine(rect.bottomLeft(), rect.bottomRight())
            p.end()


    def _menuSelections_pos_is_drop_target(self, pos):
        """ Return if selection can be dropped at this position and
            prepare information needed for drawing and dropping
        """
        action = self.menuSelections.actionAt(pos)
        if not action or not action.data():
            self.seldrag_target = None
            return False

        self.seldrag_target = action
        rect = self.menuSelections.actionGeometry(action)
        if pos.y() < rect.top() + rect.height() / 2:
            self.seldrag_target_upper = True
        else:
            self.seldrag_target_upper = False
        return True


    def on_menuSelections_dragEnter(self, event):
        event.setDropAction(Qt.MoveAction)
        if self._menuSelections_pos_is_drop_target(event.pos()):
            event.accept()
        else:
            event.ignore()

        QMenu.dragEnterEvent(self.menuSelections, event)


    def on_menuSelections_dragMoved(self, event):
        event.setDropAction(Qt.MoveAction)
        if self._menuSelections_pos_is_drop_target(event.pos()):
            event.accept()
            self.menuSelections.update()
        else:
            event.ignore()

        QMenu.dragMoveEvent(self.menuSelections, event)


    def on_menuSelections_drop(self, event):
        source = self.seldrag_selection
        target = self.seldrag_target.data()
        if source != target:
            self.selections.remove(source)
            target_index = self.selections.index(target)
            if not self.seldrag_target_upper:
                target_index += 1
            self.selections.insert(target_index, source)
            self.populate_selection_menu()

        QMenu.dropEvent(self.menuSelections, event)


    def populate_selection_menu(self):
        self.menuSelections.clear()
        a = self.menuSelections.addAction('New')
        a.triggered.connect(self.on_selection_new)
        a = self.menuSelections.addAction('Clear')
        a.triggered.connect(self.on_selection_clear)
        self.menuSelections.addSeparator()

        for i, s in enumerate(self.selections):
            m = self.menuSelections.addMenu(s.name)
            m.menuAction().setData(s)

            a = m.addAction('Load')
            self.connect(a, SIGNAL('triggered()'),
                lambda sel=s:self.on_selection_load(sel))

            a = m.addAction('Save')
            self.connect(a, SIGNAL('triggered()'),
                lambda sel=s:self.on_selection_save(sel))

            a = m.addAction('Rename')
            self.connect(a, SIGNAL('triggered()'),
                lambda sel=s:self.on_selection_rename(sel))

            a = m.addAction('Remove')
            self.connect(a, SIGNAL('triggered()'),
                lambda sel=s:self.on_selection_remove(sel))


    def on_selection_load(self, selection):
        self.set_current_selection(selection.data_dict())


    def on_selection_save(self, selection):
        i = self.selections.index(selection)
        self.selections[i] = self.provider_factory(
            self.selections[i].name, self)
        self.populate_selection_menu()

    def on_selection_clear(self):
        if QMessageBox.question(self, 'Confirmation',
            'Do you really want to remove all selections?',
            QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
            return

        del self.selections[:]
        self.populate_selection_menu()

    def on_selection_rename(self, selection):
        (name, ok) = QInputDialog.getText(self, 'Edit selection name',
            'New name:', QLineEdit.Normal, selection.name)
        if ok and name:
            selection.name = name
            self.populate_selection_menu()


    def on_selection_remove(self, selection):
        if QMessageBox.question(self, 'Confirmation',
            'Do you really want to remove the selection "%s"?' %
            selection.name,
            QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
            return

        self.selections.remove(selection)
        self.populate_selection_menu()


    def on_selection_new(self):
        self.selections.append(self.provider_factory('Selection %d' %
                                                     (len(self.selections) + 1), self))
        self.populate_selection_menu()


    def serialize_selections(self):
        sl = list() # Selection list, current selection as first item
        sl.append(self.provider_factory('current', self).data_dict())
        for s in self.selections:
            sl.append(s.data_dict())
        return json.dumps(sl, sort_keys=True, indent=2)


    def save_selections_to_file(self, filename):
        f = open(filename, 'w')
        f.write(self.serialize_selections())
        f.close()


    def load_selections_from_file(self, filename):
        try:
            f = open(filename, 'r')
            p = json.load(f)
            # First selection in file is current selection
            self.set_current_selection(p[0])
            for s in p[1:]:
                self.add_selection(s)
            f.close()
            self.populate_selection_menu()
        except Exception, e:
            self.progress.done()
            QMessageBox.critical(self, 'Error loading selection',
                str(e).decode('utf8'))