Get result object from iPython / Jupyter embedded Qt console

Refresh

December 2018

Views

227 time

6

I have an embedded Jupyter console in a PyQt5 application implemented using the RichJupyterWidget widget available here.

The console integration itself works fine, the Jupyter console is embedded in a Widget, Python code can be executed and the result is displayed within the console itself.

What I need to do, however, is whenever I run some code that returns a Pandas DataFrame display it in a TableView outside the console.

Here is a working example of my implementation:

import sys

from PyQt5.QtWidgets import QMainWindow, QApplication
from qtconsole.inprocess import QtInProcessKernelManager
from qtconsole.rich_jupyter_widget import RichJupyterWidget


class IPythonConsole:

    def __init__(self):
        self._kernel_manager = None
        self._kernel = None
        self._kernel_client = None

        self.start()

    @property
    def kernel_manager(self) -> QtInProcessKernelManager:
        return self._kernel_manager

    @property
    def kernel(self):
        return self._kernel

    @property
    def kernel_client(self):
        return self._kernel_client

    def start(self):
        self._kernel_manager = QtInProcessKernelManager()
        self._kernel_manager.start_kernel()
        self._kernel = self._kernel_manager.kernel
        self._kernel.gui = 'qt'

        self._kernel_client = self._kernel_manager.client()
        self._kernel_client.start_channels()

    def stop(self):
        self._kernel_client.stop_channels()
        self._kernel_manager.shutdown_kernel()


class IPythonWidget(RichJupyterWidget):
    """RichIPythonWidget implementation to integrate the IPython console inside a Qt Widget"""

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

        self._console = IPythonConsole()
        self.kernel_manager = self._console.kernel_manager
        self.kernel_client = self._console.kernel_client

    @property
    def console(self):
        return self._console

    def run_command(self, command_str):
        self.execute(command_str, False, True)

    def close(self):
        self._console.stop()


if __name__ == '__main__':
    app = QApplication(sys.argv)

    main_window = QMainWindow()
    ipython_widget = IPythonWidget()
    main_window.layout().addWidget(ipython_widget)
    main_window.setFixedHeight(768)
    main_window.setFixedWidth(1024)

    ipython_widget.run_command("import pandas as pd\n"
                               "df = pd.DataFrame({'Test': ['a', 'b', 'c']})\n"
                               "df")

    main_window.show()

    sys.exit(app.exec_())

So far I tried to override the following method from RichJupyterWidget:

 def _handle_execute_result(self, msg):
        """Overridden to handle rich data types, like SVG."""
        self.log.debug("execute_result: %s", msg.get('content', ''))
        if self.include_output(msg):
            self.flush_clearoutput()
            content = msg['content']
            prompt_number = content.get('execution_count', 0)
            data = content['data']
            metadata = msg['content']['metadata']
            if 'image/svg+xml' in data:
                self._pre_image_append(msg, prompt_number)
                self._append_svg(data['image/svg+xml'], True)
                self._append_html(self.output_sep2, True)
            elif 'image/png' in data:
            [...]

But unfortunately when returning a DataFrame, msg contains a string with the contents of the DataFrame in HTML format.

Edit

I was able to get the DataFrame object from the kernel shell as follows:

if self.ipython_widget.execute("df = pd.DataFrame({'Test': ['a', 'b', 'c']})\n", False, False):
    df = self.ipython_widget.console.kernel.shell.user_ns.get('df')

Unfortunately, this will only work when I run the code myself, not when the user inputs something in the console and also I must know the name of the variable containing the dataframe.

0 answers