Получить объект результата из встроенной консоли Qt iPython / Jupyter
У меня есть встроенная консоль Jupyter в приложении PyQt5, реализованная с использованием RichJupyterWidget
виджет доступен здесь.
Сама консольная интеграция работает нормально, консоль Jupyter встроена в Widget, код Python может быть выполнен, а результат отображается внутри самой консоли.
Что мне нужно сделать, однако, всякий раз, когда я запускаю некоторый код, который возвращает Pandas DataFrame
отобразить его в TableView
вне консоли.
Вот рабочий пример моей реализации:
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_())
До сих пор я пытался переопределить следующий метод из 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:
[...]
Но к сожалению при возврате DataFrame
, msg содержит строку с содержимым DataFrame
в формате HTML.
редактировать
Я смог получить DataFrame
Объект из оболочки ядра выглядит следующим образом:
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')
К сожалению, это будет работать только тогда, когда я сам запускаю код, а не когда пользователь вводит что-то в консоль, а также я должен знать имя переменной, содержащей фрейм данных.