Beispiel #1
0
    def initialize(self, argv=None):
        self.init_qt_app()
        super(ChatConsoleApp, self).initialize(argv)
        if self._dispatching:
            return
        # handle renames related to JupyterQtConsole, accepting  as outdated config
        for old_name, new_name in [
            ('IPythonQtConsoleApp', 'ChatConsole'),
            ('IPythonWidget', 'PlainTabMain'),
            ('RichIPythonWidget', 'RichTabMain'),
            ('JupyterQtConsole', 'ChatConsole'),
            ('RichJupyterWidget', 'RichTabMain'),
            ('JupyterWidget', 'PlainTabMain')
        ]:
            cfg = self._deprecate_config(self.config, old_name, new_name)
            if cfg:
                self.update_config(cfg)

        if not self.existing:
            if not self.local_kernel:
                self.storage = JSONStorage(chconsole_data_dir(), self.default_file)
                self.chooser = FileChooser(self.storage, self.storage_key, get_home_dir(), self.default_file,
                                           parent=None, caption='Choose Existing Connection File', file_filter='*.json',
                                           default_ext='json')
                if self.chooser.choose_file() and os.path.exists(self.chooser.file):
                    connection_data = JSONStorage(self.chooser.dir, self.chooser.name)
                    self.existing = self.chooser.file

        JupyterConsoleApp.initialize(self, argv)

        self.init_qt_elements()
        self.init_signal()
Beispiel #2
0
    def __init__(self):
        super(AppMain, self).__init__()

        self.storage = JSONStorage(chconsole_data_dir(), self.default_file)
        self.chooser = FileChooser(self.storage, self.storage_key, get_home_dir(), self.default_file,
                                   parent=None, caption='Choose Connection File or Enter New File Name',
                                   file_filter='*.json',
                                   default_ext='json')

        self.text_area = QtGui.QPlainTextEdit()
        self.text_area.setReadOnly(True)
        self.setCentralWidget(self.text_area)

        self.setGeometry(300, 300, 700, 200)
        self.setWindowTitle('IPython Kernel Launched')
Beispiel #3
0
class AppMain(QtGui.QMainWindow, DefaultNames):
    max_ipython_start_time = 5
    # maximum number of seconds allowed for ipython to create a connection file,
    # if it does not exist
    sleep_time = 0.1  # time to repeatedly wait until connection file has been created by the kernel.
    storage = None  # JSONStorage
    chooser = None  # FileChooser
    text_area = None  # QPlainTextEdit, output text area

    default_user_name = ''  # default user name to be used for the kernel

    def __init__(self):
        super(AppMain, self).__init__()

        self.storage = JSONStorage(chconsole_data_dir(), self.default_file)
        self.chooser = FileChooser(self.storage, self.storage_key, get_home_dir(), self.default_file,
                                   parent=None, caption='Choose Connection File or Enter New File Name',
                                   file_filter='*.json',
                                   default_ext='json')

        self.text_area = QtGui.QPlainTextEdit()
        self.text_area.setReadOnly(True)
        self.setCentralWidget(self.text_area)

        self.setGeometry(300, 300, 700, 200)
        self.setWindowTitle('IPython Kernel Launched')

    @property
    def _user_name(self):
        """
        Get the user name to the application. Either self.user_name if it is not '' or None,
        or kernel-USER environment variable if it is assigned, or kernel-getpass.getuser(), or 'kernel-user'
        if getpass.getuser() fails.
        :return: user name
        """
        prefix = 'kernel_'
        user_name = self.default_user_name
        if not user_name:
            user_name = os.getenv('USER', default=None)
            if user_name is None:
                try:
                    user_name = getpass.getuser()
                except Exception:
                    user_name = 'user'
            user_name = prefix + user_name
        return user_name

    def launch(self, app):
        if self.chooser.choose_file():
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.connect(('8.8.8.8', 1))  # connecting to a UDP address doesn't send packets
                local_ip_address = s.getsockname()[0]
            except (socket.timeout, InterruptedError):
                local_ip_address = socket.gethostbyname(socket.getfqdn())

            cmd = ['ipython', 'kernel', '-f', self.chooser.file,
                   '--ip', local_ip_address, '--user', self._user_name]

            Popen(cmd)

            # Ensure the connection file has been created
            wait_time = 0
            while wait_time < self.max_ipython_start_time and not os.path.exists(self.chooser.file):
                time.sleep(self.sleep_time)
                wait_time += self.sleep_time
            if not os.path.exists(self.chooser.file):
                print('Error: Kernel did not create the chosen connection file:')
                print(self.chooser.file)
                sys.exit(app.exit(1))

            self.text_area.insertPlainText('Connection file for Chat Console:\n')
            self.text_area.insertPlainText(self.chooser.file + '\n')

            self.text_area.insertPlainText('\nCommand used to start IPython:\n')
            self.text_area.insertPlainText(' '.join(cmd) + '\n')

            self.text_area.insertPlainText('\nThis window may be closed. The kernel will keep running!\n')
            self.text_area.insertPlainText('The kernel can be stopped by connecting a console '
                                           'and entering the quit command.')
            self.show()
        else:
            sys.exit(app.quit())
Beispiel #4
0
class ChatConsoleApp(JupyterApp, JupyterConsoleApp, DefaultNames):
    name = 'jupyter-chconsole'
    version = __version__
    description = """
        The Chat Console.

        This launches a Console-style application using Qt.  It is not a full
        console, in that launched terminal subprocesses will not be able to accept
        input.

    """
    examples = _examples

    classes = [RichTabMain] + JupyterConsoleApp.classes
    flags = Dict(flags)
    aliases = Dict(aliases)
    frontend_flags = Any(qt_flags)
    frontend_aliases = Any(qt_aliases)
    kernel_client_class = QtKernelClient
    kernel_manager_class = QtKernelManager

    stylesheet = Unicode('', config=True,
        help="path to a custom CSS stylesheet")

    hide_menubar = CBool(False, config=True,
        help="Start the console window with the menu bar hidden.")

    maximize = CBool(False, config=True,
        help="Start the console window maximized.")

    plain = CBool(False, config=True,
        help="Use a plaintext widget instead of rich text (plain can't print/save).")

    display_banner = CBool(True, config=True,
        help="Whether to display a banner upon starting the console."
    )

    local_kernel = CBool(False, config=True, help='Whether to launch a local kernel automatically on start.')


    storage = None  # JSONStorage
    chooser = None  # FileChooser  # traitlets handler

    def _plain_changed(self, name, old, new):
        """
        Change type of text edit used.
        :param self: ChatConsoleApp
        :param name: dummy
        :param old: dummy
        :param new: True if new type of text edit is plain, and false if it is rich.
        :return:
        """

        if new:  # plain
            self.widget_factory = PlainTabMain
        else:  # rich
            self.widget_factory = RichTabMain

    # the factory for creating a widget
    widget_factory = Any(RichTabMain)

    def parse_command_line(self, argv=None):
        super(ChatConsoleApp, self).parse_command_line(argv)
        self.build_kernel_argv(self.extra_args)

    def new_frontend_master(self):
        """ Create and return new frontend attached to new kernel, launched on localhost.
        """
        kernel_manager = self.kernel_manager_class(
                                connection_file=self._new_connection_file(),
                                parent=self,
                                autorestart=True,
        )
        # start the kernel
        kwargs = {}
        # FIXME: remove special treatment of IPython kernels
        if self.kernel_manager.ipykernel:
            kwargs['extra_arguments'] = self.kernel_argv
        kernel_manager.start_kernel(**kwargs)
        kernel_manager.client_factory = self.kernel_client_class
        kernel_client = kernel_manager.client()
        kernel_client.start_channels(shell=True, iopub=True)
        widget = self.widget_factory(config=self.config,
                                   local_kernel=True)
        self.init_colors(widget)
        widget.kernel_manager = kernel_manager
        widget.kernel_client = kernel_client
        widget._existing = False
        widget._may_close = True
        widget._confirm_exit = self.confirm_exit
        widget.display_banner = self.display_banner
        return widget

    def new_frontend_slave(self, current_widget):
        """Create and return a new frontend attached to an existing kernel.

        Parameters
        ----------
        current_widget : RichTabMain, PlainTabMain
            The widget whose kernel this frontend is to share
        """
        kernel_client = self.kernel_client_class(
                                connection_file=current_widget.kernel_client.connection_file,
                                config = self.config,
        )
        kernel_client.load_connection_file()
        kernel_client.start_channels()
        widget = self.widget_factory(config=self.config, local_kernel=False)
        self.init_colors(widget)
        widget._existing = True
        widget._may_close = False
        widget._confirm_exit = False
        widget.display_banner = self.display_banner
        widget.kernel_client = kernel_client
        widget.kernel_manager = current_widget.kernel_manager
        return widget

    def init_qt_app(self):
        # separate from qt_elements, because it must run first
        self.app = QtGui.QApplication(['jupyter-chconsole'])
        self.app.setApplicationName('jupyter-chconsole')

    def init_qt_elements(self):
        # Create the widget.

        base_path = os.path.abspath(os.path.dirname(__file__))
        icon_path = os.path.join(base_path, 'resources', 'icon', 'JupyterConsole.svg')
        self.app.icon = QtGui.QIcon(icon_path)
        QtGui.QApplication.setWindowIcon(self.app.icon)

        ip = self.ip
        local_kernel = (not self.existing) or is_local_ip(ip)
        self.widget = self.widget_factory(config=self.config,
                                        local_kernel=local_kernel)
        self.init_colors(self.widget)
        self.widget._existing = self.existing
        self.widget._may_close = not self.existing
        self.widget._confirm_exit = self.confirm_exit
        self.widget.display_banner = self.display_banner

        self.widget.kernel_manager = self.kernel_manager
        self.widget.kernel_client = self.kernel_client
        self.window = ExpandedMainWindow(self.app,
                                confirm_exit=self.confirm_exit,
                                new_frontend_factory=self.new_frontend_master,
                                slave_frontend_factory=self.new_frontend_slave,
                                )
        self.window.log = self.log
        self.window.add_tab_with_frontend(self.widget)
        self.window.init_menu_bar()

        # Ignore on OSX, where there is always a menu bar
        if sys.platform != 'darwin' and self.hide_menubar:
            self.window.menuBar().setVisible(False)

        self.window.setWindowTitle('Chat Console')
        # MM: set focus on the active entry
        self.window.active_frontend.main_content.entry.set_focus()

    def init_colors(self, widget):
        """Configure the coloring of the widget"""
        # Note: This will be dramatically simplified when colors
        # are removed from the backend.

        # parse the colors arg down to current known labels
        cfg = self.config
        colors = cfg.ZMQInteractiveShell.colors if 'ZMQInteractiveShell.colors' in cfg else None
        style = cfg.TabMain.syntax_style if 'TabMain.syntax_style' in cfg else None
        sheet = cfg.TabMain.style_sheet if 'TabMain.style_sheet' in cfg else None

        # find the value for colors:
        if colors:
            colors=colors.lower()
            if colors in ('lightbg', 'light'):
                colors='lightbg'
            elif colors in ('dark', 'linux'):
                colors='linux'
            else:
                colors='nocolor'
        elif style:
            if style=='bw':
                colors='nocolor'
            elif styles.dark_style(style):
                colors='linux'
            else:
                colors='lightbg'
        else:
            colors=None

        # Configure the style
        if style:
            widget.style_sheet = styles.sheet_from_template(style, colors)
            widget.syntax_style = style
            widget._syntax_style_changed()
            widget._style_sheet_changed()
        elif colors:
            # use a default dark/light/bw style
            widget.set_default_style(colors=colors)

        if self.stylesheet:
            # we got an explicit stylesheet
            if os.path.isfile(self.stylesheet):
                with open(self.stylesheet) as f:
                    sheet = f.read()
            else:
                raise IOError("Stylesheet %r not found." % self.stylesheet)
        if sheet:
            widget.style_sheet = sheet
            widget._style_sheet_changed()

    def init_signal(self):
        """allow clean shutdown on sigint"""
        signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
        # need a timer, so that QApplication doesn't block until a real
        # Qt event fires (can require mouse movement)
        # timer trick from http://stackoverflow.com/q/4938723/938949
        timer = QtCore.QTimer()
         # Let the interpreter run each 200 ms:
        timer.timeout.connect(lambda: None)
        timer.start(200)
        # hold onto ref, so the timer doesn't get cleaned up
        self._sigint_timer = timer

    def _deprecate_config(self, cfg, old_name, new_name):
        """Warn about deprecated config"""
        if old_name in cfg:
            self.log.warn("Use %s in config, not %s. Outdated config:\n    %s",
                new_name, old_name,
                '\n    '.join('{name}.{key} = {value!r}'.format(key=key, value=value, name=old_name)
                    for key, value in self.config[old_name].items()
                )
            )
            cfg = cfg.copy()
            cfg[new_name].merge(cfg[old_name])
            return cfg

    @catch_config_error
    def initialize(self, argv=None):
        self.init_qt_app()
        super(ChatConsoleApp, self).initialize(argv)
        if self._dispatching:
            return
        # handle renames related to JupyterQtConsole, accepting  as outdated config
        for old_name, new_name in [
            ('IPythonQtConsoleApp', 'ChatConsole'),
            ('IPythonWidget', 'PlainTabMain'),
            ('RichIPythonWidget', 'RichTabMain'),
            ('JupyterQtConsole', 'ChatConsole'),
            ('RichJupyterWidget', 'RichTabMain'),
            ('JupyterWidget', 'PlainTabMain')
        ]:
            cfg = self._deprecate_config(self.config, old_name, new_name)
            if cfg:
                self.update_config(cfg)

        if not self.existing:
            if not self.local_kernel:
                self.storage = JSONStorage(chconsole_data_dir(), self.default_file)
                self.chooser = FileChooser(self.storage, self.storage_key, get_home_dir(), self.default_file,
                                           parent=None, caption='Choose Existing Connection File', file_filter='*.json',
                                           default_ext='json')
                if self.chooser.choose_file() and os.path.exists(self.chooser.file):
                    connection_data = JSONStorage(self.chooser.dir, self.chooser.name)
                    self.existing = self.chooser.file

        JupyterConsoleApp.initialize(self, argv)

        self.init_qt_elements()
        self.init_signal()

    def start(self):
        super(ChatConsoleApp, self).start()

        # draw the window
        if self.maximize:
            self.window.showMaximized()
        else:
            self.window.show()
        self.window.raise_()

        # Start the application main loop.
        self.app.exec_()