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 __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')
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())
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_()