jeremiahbuddha
jeremiahbuddha

Reputation: 10181

Coopt IPython Qt Console for my PyQt Application

I'm creating a console driven Qt application using Python. Rather than implement my own custom console, I'd like to embed the IPython Qt console, but also make it responsive to my application. For instance, I would like certain keywords input to the console to trigger actions in my main application. So I type "dothis" in the console and in another window of my application a plot is displayed.

I've seen some questions along these lines: this one discusses how to embed an IPython Qt widget into your application and pass through functions, although it looks like these functions execute in the IPython kernel and not the kernel of my main app. There is also this guy, but I can't execute the code in the examples (it's two years old), and it doesn't look like it's doing what I want either.

Is there a way I can pass in functions or methods that will execute in my main kernel, or at least simulate this behavior somehow by communicating with the IPython kernel? Has anyone done this before?

Upvotes: 1

Views: 571

Answers (1)

jeremiahbuddha
jeremiahbuddha

Reputation: 10181

This is what I came up with, and so far it is working pretty well. I subclass the RichIPythonWidget class and overload the _execute method. Whenever the user types something into the console, I check it against a list of registered commands; if it matches a command, then I execute the command code, otherwise I just pass the input along to the default _execute method.

Console Code:

from IPython.qt.console.rich_ipython_widget import RichIPythonWidget

class CommandConsole( RichIPythonWidget ):
    """
    This is a thin wrapper around IPython's RichIPythonWidget. It's
    main purpose is to register console commands and intercept
    them when typed into the console.

    """
    def __init__(self, *args, **kw ):
         kw['kind'] = 'cc'
         super(CommandConsole, self).__init__(*args, **kw)
         self.commands = {}


    def _execute(self, source, hidden):
        """
        Overloaded version of the _execute first checks the console
        input against registered commands. If it finds a command it
        executes it, otherwise it passes the input to the back kernel
        for processing.

        """
        try:
            possible_cmd = source.split()[0].strip()
        except:
            return super(CommandConsole, self)._execute("pass", hidden)

        if possible_cmd in self.commands.keys():
            # Commands return code that is passed to the console for execution.
            s = self.commands[possible_cmd].execute()
            return super(CommandConsole, self)._execute( s, hidden )
        else:
            # Default back to the original _execute
            return super(CommandConsole, self)._execute(source, hidden)


    def register_command( self, name, command ):
        """
        This method links the name of a command (name) to the function
        that should be called when it is typed into the console (command).

        """
        self.commands[name] = command

Example Command:

from PyQt5.QtCore import pyqtSignal, QObject, QFile

class SelectCommand( QObject ):
    """
    The Select command class.

    """
    # This signal is emitted whenever the command is executed.
    executed = pyqtSignal( str, dict, name = "selectExecuted" )

    # This is the command as typed into the console.
    name = "select"

    def execute(self):
        """
        This method is executed whenever the ``name`` command is issued
        in the console.

        """
        name = "data description"
        data = { "data dict" : 0 }
        # The signal is sent to my Qt Models
        self.executed.emit( name, data )
        # This code is executed in the console.
        return 'print("the select command has been executed")'

Upvotes: 3

Related Questions