class RichText(QWidget): """ WebView widget with find dialog """ def __init__(self, parent): QWidget.__init__(self, parent) self.webview = WebView(self) self.find_widget = FindReplace(self) self.find_widget.set_editor(self.webview) self.find_widget.hide() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.webview) layout.addWidget(self.find_widget) self.setLayout(layout) def set_font(self, font, fixed_font=None): """Set font""" self.webview.set_font(font, fixed_font=fixed_font) def set_html(self, html_text, base_url): """Set html text""" self.webview.setHtml(html_text, base_url) def clear(self): self.set_html('', self.webview.url())
class IPythonClient(QWidget, SaveHistoryMixin): """ IPython client or frontend for Spyder This is a widget composed of a shell widget (i.e. RichIPythonWidget + our additions = IPythonShellWidget) and an WebView info widget to print kernel error and other messages. """ SEPARATOR = '%s##---(%s)---' % (os.linesep * 2, time.ctime()) append_to_history = Signal(str, str) def __init__(self, plugin, name, history_filename, connection_file=None, hostname=None, sshkey=None, password=None, kernel_widget_id=None, menu_actions=None): super(IPythonClient, self).__init__(plugin) SaveHistoryMixin.__init__(self) self.options_button = None # stop button and icon self.stop_button = None self.stop_icon = get_icon("stop.png") self.connection_file = connection_file self.kernel_widget_id = kernel_widget_id self.hostname = hostname self.sshkey = sshkey self.password = password self.name = name self.get_option = plugin.get_option self.shellwidget = IPythonShellWidget(config=self.shellwidget_config(), local_kernel=False) self.shellwidget.hide() self.infowidget = WebView(self) self.menu_actions = menu_actions self.history_filename = get_conf_path(history_filename) self.history = [] self.namespacebrowser = None self.set_infowidget_font() self.loading_page = self._create_loading_page() self.infowidget.setHtml(self.loading_page) vlayout = QVBoxLayout() toolbar_buttons = self.get_toolbar_buttons() hlayout = QHBoxLayout() for button in toolbar_buttons: hlayout.addWidget(button) vlayout.addLayout(hlayout) vlayout.setContentsMargins(0, 0, 0, 0) vlayout.addWidget(self.shellwidget) vlayout.addWidget(self.infowidget) self.setLayout(vlayout) self.exit_callback = lambda: plugin.close_client(client=self) #------ Public API -------------------------------------------------------- def show_shellwidget(self, give_focus=True): """Show shellwidget and configure it""" self.infowidget.hide() self.shellwidget.show() self.infowidget.setHtml(BLANK) if give_focus: self.get_control().setFocus() # Connect shellwidget to the client self.shellwidget.set_ipyclient(self) # To save history self.shellwidget.executing.connect(self.add_to_history) # For Mayavi to run correctly self.shellwidget.executing.connect(self.set_backend_for_mayavi) # To update history after execution self.shellwidget.executed.connect(self.update_history) # To update the Variable Explorer after execution self.shellwidget.executed.connect(self.auto_refresh_namespacebrowser) # To show a stop button, when executing a process self.shellwidget.executing.connect(self.enable_stop_button) # To hide a stop button after execution stopped self.shellwidget.executed.connect(self.disable_stop_button) def enable_stop_button(self): self.stop_button.setEnabled(True) def disable_stop_button(self): self.stop_button.setDisabled(True) @Slot() def stop_button_click_handler(self): self.stop_button.setDisabled(True) self.interrupt_kernel() def show_kernel_error(self, error): """Show kernel initialization errors in infowidget""" # Remove explanation about how to kill the kernel (doesn't apply to us) error = error.split('issues/2049')[-1] # Remove unneeded blank lines at the beginning eol = sourcecode.get_eol_chars(error) if eol: error = error.replace(eol, '<br>') while error.startswith('<br>'): error = error[4:] # Remove connection message if error.startswith('To connect another client') or \ error.startswith('[IPKernelApp] To connect another client'): error = error.split('<br>') error = '<br>'.join(error[2:]) # Don't break lines in hyphens # From http://stackoverflow.com/q/7691569/438386 error = error.replace('-', '‑') message = _("An error ocurred while starting the kernel") kernel_error_template = Template(KERNEL_ERROR) page = kernel_error_template.substitute(css_path=CSS_PATH, message=message, error=error) self.infowidget.setHtml(page) def show_restart_animation(self): self.shellwidget.hide() self.infowidget.setHtml(self.loading_page) self.infowidget.show() def get_name(self): """Return client name""" return ((_("Console") if self.hostname is None else self.hostname) + " " + self.name) def get_control(self): """Return the text widget (or similar) to give focus to""" # page_control is the widget used for paging page_control = self.shellwidget._page_control if page_control and page_control.isVisible(): return page_control else: return self.shellwidget._control def get_options_menu(self): """Return options menu""" restart_action = create_action(self, _("Restart kernel"), shortcut=QKeySequence("Ctrl+."), icon=get_icon('restart.png'), triggered=self.restart_kernel) restart_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) # Main menu if self.menu_actions is not None: actions = [restart_action, None] + self.menu_actions else: actions = [restart_action] return actions def get_toolbar_buttons(self): """Return toolbar buttons list""" #TODO: Eventually add some buttons (Empty for now) # (see for example: spyderlib/widgets/externalshell/baseshell.py) buttons = [] # Code to add the stop button if self.stop_button is None: self.stop_button = create_toolbutton( self, text=_("Stop"), icon=self.stop_icon, tip=_("Stop the current command")) self.disable_stop_button() # set click event handler self.stop_button.clicked.connect(self.stop_button_click_handler) if self.stop_button is not None: buttons.append(self.stop_button) if self.options_button is None: options = self.get_options_menu() if options: self.options_button = create_toolbutton( self, text=_("Options"), icon=get_icon('tooloptions.png')) self.options_button.setPopupMode(QToolButton.InstantPopup) menu = QMenu(self) add_actions(menu, options) self.options_button.setMenu(menu) if self.options_button is not None: buttons.append(self.options_button) return buttons def add_actions_to_context_menu(self, menu): """Add actions to IPython widget context menu""" # See spyderlib/widgets/ipython.py for more details on this method inspect_action = create_action( self, _("Inspect current object"), QKeySequence(get_shortcut('console', 'inspect current object')), icon=get_std_icon('MessageBoxInformation'), triggered=self.inspect_object) clear_line_action = create_action(self, _("Clear line or block"), QKeySequence("Shift+Escape"), icon=get_icon('eraser.png'), triggered=self.clear_line) clear_console_action = create_action( self, _("Clear console"), QKeySequence(get_shortcut('console', 'clear shell')), icon=get_icon('clear.png'), triggered=self.clear_console) quit_action = create_action(self, _("&Quit"), icon='exit.png', triggered=self.exit_callback) add_actions(menu, (None, inspect_action, clear_line_action, clear_console_action, None, quit_action)) return menu def set_font(self, font): """Set IPython widget's font""" self.shellwidget._control.setFont(font) self.shellwidget.font = font def set_infowidget_font(self): font = get_font('inspector', 'rich_text') self.infowidget.set_font(font) def interrupt_kernel(self): """Interrupt the associanted Spyder kernel if it's running""" self.shellwidget.request_interrupt_kernel() @Slot() def restart_kernel(self): """Restart the associanted Spyder kernel""" self.shellwidget.request_restart_kernel() @Slot() def inspect_object(self): """Show how to inspect an object with our object inspector""" self.shellwidget._control.inspect_current_object() @Slot() def clear_line(self): """Clear a console line""" self.shellwidget._keyboard_quit() @Slot() def clear_console(self): """Clear the whole console""" self.shellwidget.execute("%clear") def if_kernel_dies(self, t): """ Show a message in the console if the kernel dies. t is the time in seconds between the death and showing the message. """ message = _("It seems the kernel died unexpectedly. Use " "'Restart kernel' to continue using this console.") self.shellwidget._append_plain_text(message + '\n') def update_history(self): self.history = self.shellwidget._history def set_backend_for_mayavi(self, command): calling_mayavi = False lines = command.splitlines() for l in lines: if not l.startswith('#'): if 'import mayavi' in l or 'from mayavi' in l: calling_mayavi = True break if calling_mayavi: message = _("Changing backend to Qt for Mayavi") self.shellwidget._append_plain_text(message + '\n') self.shellwidget.execute("%gui inline\n%gui qt") def interrupt_message(self): """ Print an interrupt message when the client is connected to an external kernel """ message = _("Kernel process is either remote or unspecified. " "Cannot interrupt") QMessageBox.information(self, "IPython", message) def restart_message(self): """ Print a restart message when the client is connected to an external kernel """ message = _("Kernel process is either remote or unspecified. " "Cannot restart.") QMessageBox.information(self, "IPython", message) def set_namespacebrowser(self, namespacebrowser): """Set namespace browser widget""" self.namespacebrowser = namespacebrowser def auto_refresh_namespacebrowser(self): """Refresh namespace browser""" if self.namespacebrowser: self.namespacebrowser.refresh_table() def shellwidget_config(self): """Generate a Config instance for shell widgets using our config system This lets us create each widget with its own config (as opposed to IPythonQtConsoleApp, where all widgets have the same config) """ # ---- IPython config ---- try: profile_path = osp.join(get_ipython_dir(), 'profile_default') full_ip_cfg = load_pyconfig_files(['ipython_qtconsole_config.py'], profile_path) # From the full config we only select the IPythonWidget section # because the others have no effect here. ip_cfg = Config({'IPythonWidget': full_ip_cfg.IPythonWidget}) except: ip_cfg = Config() # ---- Spyder config ---- spy_cfg = Config() # Make the pager widget a rich one (i.e a QTextEdit) spy_cfg.IPythonWidget.kind = 'rich' # Gui completion widget gui_comp_o = self.get_option('use_gui_completion') completions = {True: 'droplist', False: 'ncurses'} spy_cfg.IPythonWidget.gui_completion = completions[gui_comp_o] # Pager pager_o = self.get_option('use_pager') if pager_o: spy_cfg.IPythonWidget.paging = 'inside' else: spy_cfg.IPythonWidget.paging = 'none' # Calltips calltips_o = self.get_option('show_calltips') spy_cfg.IPythonWidget.enable_calltips = calltips_o # Buffer size buffer_size_o = self.get_option('buffer_size') spy_cfg.IPythonWidget.buffer_size = buffer_size_o # Prompts in_prompt_o = self.get_option('in_prompt') out_prompt_o = self.get_option('out_prompt') if in_prompt_o: spy_cfg.IPythonWidget.in_prompt = in_prompt_o if out_prompt_o: spy_cfg.IPythonWidget.out_prompt = out_prompt_o # Merge IPython and Spyder configs. Spyder prefs will have prevalence # over IPython ones ip_cfg._merge(spy_cfg) return ip_cfg #------ Private API ------------------------------------------------------- def _create_loading_page(self): loading_template = Template(LOADING) loading_img = get_image_path('loading_sprites.png') if os.name == 'nt': loading_img = loading_img.replace('\\', '/') message = _("Connecting to kernel...") page = loading_template.substitute(css_path=CSS_PATH, loading_img=loading_img, message=message) return page #---- Qt methods ---------------------------------------------------------- def closeEvent(self, event): """ Reimplement Qt method to stop sending the custom_restart_kernel_died signal """ kc = self.shellwidget.kernel_client if kc is not None: kc.hb_channel.pause()
class IPythonClient(QWidget, SaveHistoryMixin): """ IPython client or frontend for Spyder This is a widget composed of a shell widget (i.e. RichIPythonWidget + our additions = IPythonShellWidget) and a WebView info widget to print kernel error and other messages. """ SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime()) append_to_history = Signal(str, str) def __init__(self, plugin, name, history_filename, connection_file=None, hostname=None, sshkey=None, password=None, kernel_widget_id=None, menu_actions=None): super(IPythonClient, self).__init__(plugin) SaveHistoryMixin.__init__(self) self.options_button = None # stop button and icon self.stop_button = None self.stop_icon = ima.icon('stop') self.connection_file = connection_file self.kernel_widget_id = kernel_widget_id self.hostname = hostname self.sshkey = sshkey self.password = password self.name = name self.get_option = plugin.get_option self.shellwidget = IPythonShellWidget(config=self.shellwidget_config(), local_kernel=False) self.shellwidget.hide() self.infowidget = WebView(self) self.menu_actions = menu_actions self.history_filename = get_conf_path(history_filename) self.history = [] self.namespacebrowser = None self.set_infowidget_font() self.loading_page = self._create_loading_page() self.infowidget.setHtml(self.loading_page) vlayout = QVBoxLayout() toolbar_buttons = self.get_toolbar_buttons() hlayout = QHBoxLayout() for button in toolbar_buttons: hlayout.addWidget(button) vlayout.addLayout(hlayout) vlayout.setContentsMargins(0, 0, 0, 0) vlayout.addWidget(self.shellwidget) vlayout.addWidget(self.infowidget) self.setLayout(vlayout) self.exit_callback = lambda: plugin.close_client(client=self) #------ Public API -------------------------------------------------------- def show_shellwidget(self, give_focus=True): """Show shellwidget and configure it""" self.infowidget.hide() self.shellwidget.show() self.infowidget.setHtml(BLANK) if give_focus: self.get_control().setFocus() # Connect shellwidget to the client self.shellwidget.set_ipyclient(self) # To save history self.shellwidget.executing.connect(self.add_to_history) # For Mayavi to run correctly self.shellwidget.executing.connect(self.set_backend_for_mayavi) # To update history after execution self.shellwidget.executed.connect(self.update_history) # To update the Variable Explorer after execution self.shellwidget.executed.connect(self.auto_refresh_namespacebrowser) # To show a stop button, when executing a process self.shellwidget.executing.connect(self.enable_stop_button) # To hide a stop button after execution stopped self.shellwidget.executed.connect(self.disable_stop_button) def enable_stop_button(self): self.stop_button.setEnabled(True) def disable_stop_button(self): self.stop_button.setDisabled(True) @Slot() def stop_button_click_handler(self): """Method to handle what to do when the stop button is pressed""" self.stop_button.setDisabled(True) # Interrupt computations or stop debugging if not self.shellwidget._reading: self.interrupt_kernel() else: self.shellwidget.write_to_stdin('exit') def show_kernel_error(self, error): """Show kernel initialization errors in infowidget""" # Remove explanation about how to kill the kernel (doesn't apply to us) error = error.split('issues/2049')[-1] # Remove unneeded blank lines at the beginning eol = sourcecode.get_eol_chars(error) if eol: error = error.replace(eol, '<br>') while error.startswith('<br>'): error = error[4:] # Remove connection message if error.startswith('To connect another client') or \ error.startswith('[IPKernelApp] To connect another client'): error = error.split('<br>') error = '<br>'.join(error[2:]) # Don't break lines in hyphens # From http://stackoverflow.com/q/7691569/438386 error = error.replace('-', '‑') message = _("An error ocurred while starting the kernel") kernel_error_template = Template(KERNEL_ERROR) page = kernel_error_template.substitute(css_path=CSS_PATH, message=message, error=error) self.infowidget.setHtml(page) def show_restart_animation(self): """Show loading_page while restarting the kernel""" self.shellwidget.hide() self.infowidget.setHtml(self.loading_page) self.infowidget.show() def get_name(self): """Return client name""" return ((_("Console") if self.hostname is None else self.hostname) + " " + self.name) def get_control(self): """Return the text widget (or similar) to give focus to""" # page_control is the widget used for paging page_control = self.shellwidget._page_control if page_control and page_control.isVisible(): return page_control else: return self.shellwidget._control def get_options_menu(self): """Return options menu""" restart_action = create_action(self, _("Restart kernel"), shortcut=QKeySequence("Ctrl+."), icon=ima.icon('restart'), triggered=self.restart_kernel) restart_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) # Main menu if self.menu_actions is not None: actions = [restart_action, None] + self.menu_actions else: actions = [restart_action] return actions def get_toolbar_buttons(self): """Return toolbar buttons list""" #TODO: Eventually add some buttons (Empty for now) # (see for example: spyderlib/widgets/externalshell/baseshell.py) buttons = [] # Code to add the stop button if self.stop_button is None: self.stop_button = create_toolbutton(self, text=_("Stop"), icon=self.stop_icon, tip=_("Stop the current command")) self.disable_stop_button() # set click event handler self.stop_button.clicked.connect(self.stop_button_click_handler) if self.stop_button is not None: buttons.append(self.stop_button) if self.options_button is None: options = self.get_options_menu() if options: self.options_button = create_toolbutton(self, text=_('Options'), icon=ima.icon('tooloptions')) self.options_button.setPopupMode(QToolButton.InstantPopup) menu = QMenu(self) add_actions(menu, options) self.options_button.setMenu(menu) if self.options_button is not None: buttons.append(self.options_button) return buttons def add_actions_to_context_menu(self, menu): """Add actions to IPython widget context menu""" # See spyderlib/widgets/ipython.py for more details on this method inspect_action = create_action(self, _("Inspect current object"), QKeySequence(get_shortcut('console', 'inspect current object')), icon=ima.icon('MessageBoxInformation'), triggered=self.inspect_object) clear_line_action = create_action(self, _("Clear line or block"), QKeySequence("Shift+Escape"), icon=ima.icon('editdelete'), triggered=self.clear_line) clear_console_action = create_action(self, _("Clear console"), QKeySequence(get_shortcut('console', 'clear shell')), icon=ima.icon('editclear'), triggered=self.clear_console) quit_action = create_action(self, _("&Quit"), icon=ima.icon('exit'), triggered=self.exit_callback) add_actions(menu, (None, inspect_action, clear_line_action, clear_console_action, None, quit_action)) return menu def set_font(self, font): """Set IPython widget's font""" self.shellwidget._control.setFont(font) self.shellwidget.font = font def set_infowidget_font(self): """Set font for infowidget""" font = get_font('inspector', 'rich_text') self.infowidget.set_font(font) def interrupt_kernel(self): """Interrupt the associanted Spyder kernel if it's running""" self.shellwidget.request_interrupt_kernel() @Slot() def restart_kernel(self): """Restart the associanted Spyder kernel""" self.shellwidget.request_restart_kernel() @Slot() def inspect_object(self): """Show how to inspect an object with our object inspector""" self.shellwidget._control.inspect_current_object() @Slot() def clear_line(self): """Clear a console line""" self.shellwidget._keyboard_quit() @Slot() def clear_console(self): """Clear the whole console""" self.shellwidget.execute("%clear") def if_kernel_dies(self, t): """ Show a message in the console if the kernel dies. t is the time in seconds between the death and showing the message. """ message = _("It seems the kernel died unexpectedly. Use " "'Restart kernel' to continue using this console.") self.shellwidget._append_plain_text(message + '\n') def update_history(self): self.history = self.shellwidget._history def set_backend_for_mayavi(self, command): """ Mayavi plots require the Qt backend, so we try to detect if one is generated to change backends """ calling_mayavi = False lines = command.splitlines() for l in lines: if not l.startswith('#'): if 'import mayavi' in l or 'from mayavi' in l: calling_mayavi = True break if calling_mayavi: message = _("Changing backend to Qt for Mayavi") self.shellwidget._append_plain_text(message + '\n') self.shellwidget.execute("%gui inline\n%gui qt") def interrupt_message(self): """ Print an interrupt message when the client is connected to an external kernel """ message = _("Kernel process is either remote or unspecified. " "Cannot interrupt") QMessageBox.information(self, "IPython", message) def restart_message(self): """ Print a restart message when the client is connected to an external kernel """ message = _("Kernel process is either remote or unspecified. " "Cannot restart.") QMessageBox.information(self, "IPython", message) def set_namespacebrowser(self, namespacebrowser): """Set namespace browser widget""" self.namespacebrowser = namespacebrowser def auto_refresh_namespacebrowser(self): """Refresh namespace browser""" if self.namespacebrowser: self.namespacebrowser.refresh_table() def shellwidget_config(self): """ Generate a Config instance for shell widgets using our config system This lets us create each widget with its own config (as opposed to IPythonQtConsoleApp, where all widgets have the same config) """ # ---- IPython config ---- try: profile_path = osp.join(get_ipython_dir(), 'profile_default') full_ip_cfg = load_pyconfig_files(['ipython_qtconsole_config.py'], profile_path) # From the full config we only select the IPythonWidget section # because the others have no effect here. ip_cfg = Config({'IPythonWidget': full_ip_cfg.IPythonWidget}) except: ip_cfg = Config() # ---- Spyder config ---- spy_cfg = Config() # Make the pager widget a rich one (i.e a QTextEdit) spy_cfg.IPythonWidget.kind = 'rich' # Gui completion widget completion_type_o = CONF.get('ipython_console', 'completion_type') completions = {0: "droplist", 1: "ncurses", 2: "plain"} spy_cfg.IPythonWidget.gui_completion = completions[completion_type_o] # Pager pager_o = self.get_option('use_pager') if pager_o: spy_cfg.IPythonWidget.paging = 'inside' else: spy_cfg.IPythonWidget.paging = 'none' # Calltips calltips_o = self.get_option('show_calltips') spy_cfg.IPythonWidget.enable_calltips = calltips_o # Buffer size buffer_size_o = self.get_option('buffer_size') spy_cfg.IPythonWidget.buffer_size = buffer_size_o # Prompts in_prompt_o = self.get_option('in_prompt') out_prompt_o = self.get_option('out_prompt') if in_prompt_o: spy_cfg.IPythonWidget.in_prompt = in_prompt_o if out_prompt_o: spy_cfg.IPythonWidget.out_prompt = out_prompt_o # Merge IPython and Spyder configs. Spyder prefs will have prevalence # over IPython ones ip_cfg._merge(spy_cfg) return ip_cfg #------ Private API ------------------------------------------------------- def _create_loading_page(self): """Create html page to show while the kernel is created""" loading_template = Template(LOADING) loading_img = get_image_path('loading_sprites.png') if os.name == 'nt': loading_img = loading_img.replace('\\', '/') message = _("Connecting to kernel...") page = loading_template.substitute(css_path=CSS_PATH, loading_img=loading_img, message=message) return page #---- Qt methods ---------------------------------------------------------- def closeEvent(self, event): """ Reimplement Qt method to stop sending the custom_restart_kernel_died signal """ kc = self.shellwidget.kernel_client if kc is not None: kc.hb_channel.pause()
class IPythonClient(QWidget, SaveHistoryMixin): """ IPython client or frontend for Spyder This is a widget composed of a shell widget (i.e. RichIPythonWidget + our additions = IPythonShellWidget) and an WebView info widget to print kernel error and other messages. """ SEPARATOR = '%s##---(%s)---' % (os.linesep*2, time.ctime()) def __init__(self, plugin, history_filename, connection_file=None, kernel_widget_id=None, menu_actions=None): super(IPythonClient, self).__init__(plugin) SaveHistoryMixin.__init__(self) self.options_button = None self.connection_file = connection_file self.kernel_widget_id = kernel_widget_id self.name = '' self.shellwidget = IPythonShellWidget(config=plugin.ipywidget_config(), local_kernel=False) self.shellwidget.hide() self.infowidget = WebView(self) self.menu_actions = menu_actions self.history_filename = get_conf_path(history_filename) self.history = [] self.namespacebrowser = None self.set_infowidget_font() self.loading_page = self._create_loading_page() self.infowidget.setHtml(self.loading_page) vlayout = QVBoxLayout() toolbar_buttons = self.get_toolbar_buttons() hlayout = QHBoxLayout() for button in toolbar_buttons: hlayout.addWidget(button) vlayout.addLayout(hlayout) vlayout.setContentsMargins(0, 0, 0, 0) vlayout.addWidget(self.shellwidget) vlayout.addWidget(self.infowidget) self.setLayout(vlayout) self.exit_callback = lambda: plugin.close_console(client=self) #------ Public API -------------------------------------------------------- def show_shellwidget(self): """Show shellwidget and configure it""" self.infowidget.hide() self.shellwidget.show() self.infowidget.setHtml(BLANK) self.get_control().setFocus() # Connect shellwidget to the client self.shellwidget.set_ipyclient(self) # To save history self.shellwidget.executing.connect( lambda c: self.add_to_history(command=c)) # To update history after execution self.shellwidget.executed.connect(self.update_history) # To update the Variable Explorer after execution self.shellwidget.executed.connect(self.auto_refresh_namespacebrowser) def show_kernel_error(self, error): """Show kernel initialization errors in the client""" # Remove explanation about how to kill the kernel # (doesn't apply to us) error = error.split('issues/2049')[-1] error = error.replace('\n', '<br>') # Remove unneeded blank lines at the beginning while error.startswith('<br>'): error = error[4:] # Remove connection message if error.startswith('To connect another client') or \ error.startswith('[IPKernelApp] To connect another client'): error = error.split('<br>') error = '<br>'.join(error[2:]) # Don't break lines in hyphens # From http://stackoverflow.com/q/7691569/438386 error = error.replace('-', '‑') message = _("An error ocurred while starting the kernel!") kernel_error_template = Template(KERNEL_ERROR) page = kernel_error_template.substitute(css_path=CSS_PATH, message=message, error=error) self.infowidget.setHtml(page) def show_restart_animation(self): self.shellwidget.hide() self.infowidget.setHtml(self.loading_page) self.infowidget.show() def get_name(self): """Return client name""" return _("Console") + " " + self.name def get_control(self): """Return the text widget (or similar) to give focus to""" # page_control is the widget used for paging page_control = self.shellwidget._page_control if page_control and page_control.isVisible(): return page_control else: return self.shellwidget._control def get_options_menu(self): """Return options menu""" # Kernel self.interrupt_action = create_action(self, _("Interrupt kernel"), icon=get_icon('terminate.png'), triggered=self.interrupt_kernel) self.restart_action = create_action(self, _("Restart kernel"), icon=get_icon('restart.png'), triggered=self.restart_kernel) # Main menu if self.menu_actions is not None: actions = [self.interrupt_action, self.restart_action, None] +\ self.menu_actions else: actions = [self.interrupt_action, self.restart_action] return actions def get_toolbar_buttons(self): """Return toolbar buttons list""" #TODO: Eventually add some buttons (Empty for now) # (see for example: spyderlib/widgets/externalshell/baseshell.py) buttons = [] if self.options_button is None: options = self.get_options_menu() if options: self.options_button = create_toolbutton(self, text=_("Options"), icon=get_icon('tooloptions.png')) self.options_button.setPopupMode(QToolButton.InstantPopup) menu = QMenu(self) add_actions(menu, options) self.options_button.setMenu(menu) if self.options_button is not None: buttons.append(self.options_button) return buttons def add_actions_to_context_menu(self, menu): """Add actions to IPython widget context menu""" # See spyderlib/widgets/ipython.py for more details on this method inspect_action = create_action(self, _("Inspect current object"), QKeySequence("Ctrl+I"), icon=get_std_icon('MessageBoxInformation'), triggered=self.inspect_object) clear_line_action = create_action(self, _("Clear line or block"), QKeySequence("Shift+Escape"), icon=get_icon('eraser.png'), triggered=self.clear_line) clear_console_action = create_action(self, _("Clear console"), QKeySequence("Ctrl+L"), icon=get_icon('clear.png'), triggered=self.clear_console) quit_action = create_action(self, _("&Quit"), icon='exit.png', triggered=self.exit_callback) add_actions(menu, (None, inspect_action, clear_line_action, clear_console_action, None, quit_action)) return menu def set_font(self, font): """Set IPython widget's font""" self.shellwidget._control.setFont(font) self.shellwidget.font = font def set_infowidget_font(self): font = get_font('inspector', 'rich_text') self.infowidget.set_font(font) def interrupt_kernel(self): """Interrupt the associanted Spyder kernel if it's running""" self.shellwidget.request_interrupt_kernel() def restart_kernel(self): """Restart the associanted Spyder kernel""" self.shellwidget.request_restart_kernel() def inspect_object(self): """Show how to inspect an object with our object inspector""" self.shellwidget._control.inspect_current_object() def clear_line(self): """Clear a console line""" self.shellwidget._keyboard_quit() def clear_console(self): """Clear the whole console""" self.shellwidget.execute("%clear") def if_kernel_dies(self, t): """ Show a message in the console if the kernel dies. t is the time in seconds between the death and showing the message. """ message = _("It seems the kernel died unexpectedly. Use " "'Restart kernel' to continue using this console.") self.shellwidget._append_plain_text(message + '\n') def update_history(self): self.history = self.shellwidget._history def interrupt_message(self): """ Print an interrupt message when the client is connected to an external kernel """ message = _("Kernel process is either remote or unspecified. " "Cannot interrupt") QMessageBox.information(self, "IPython", message) def restart_message(self): """ Print a restart message when the client is connected to an external kernel """ message = _("Kernel process is either remote or unspecified. " "Cannot restart.") QMessageBox.information(self, "IPython", message) def set_namespacebrowser(self, namespacebrowser): """Set namespace browser widget""" self.namespacebrowser = namespacebrowser def auto_refresh_namespacebrowser(self): """Refresh namespace browser""" if self.namespacebrowser: self.namespacebrowser.refresh_table() #------ Private API ------------------------------------------------------- def _create_loading_page(self): loading_template = Template(LOADING) loading_img = get_image_path('loading.gif') message = _("Connecting to kernel...") page = loading_template.substitute(css_path=CSS_PATH, loading_img=loading_img, message=message) return page #---- Qt methods ---------------------------------------------------------- def closeEvent(self, event): """ Reimplement Qt method to stop sending the custom_restart_kernel_died signal """ if programs.is_module_installed('IPython', '>=1.0'): kc = self.shellwidget.kernel_client if kc is not None: kc.hb_channel.pause() else: self.shellwidget.custom_restart = False