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.