[risolto] ipython embedded e output di un plot
« il: Febbraio 13, 2013, 15:01 »
Ciao, come da titolo vorrei embeddare ipython in un'applicazione PyQt (PySide a dire il vero) per gestire l'output grafico dei plot.

[codice]#coding: utf-8

import atexit

from PySide import QtCore, QtGui

from IPython.zmq.ipkernel import IPKernelApp
from IPython.lib.kernel import find_connection_file
from IPython.frontend.qt.kernelmanager import QtKernelManager
from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.config.application import catch_config_error

import matplotlib
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar

DEFAULT_INSTANCE_ARGS = ['qtconsole','--pylab']

class IPythonLocalKernelApp(IPKernelApp):
    @catch_config_error
    def initialize(self, argv=DEFAULT_INSTANCE_ARGS):
        """
        argv: IPython args
        """
        super(IPythonLocalKernelApp, self).initialize(argv)
        self.kernel.eventloop = self.loop_qt4_nonblocking
        self.kernel.start()
        self.start()

    def loop_qt4_nonblocking(self, kernel):
        """Non-blocking version of the ipython qt4 kernel loop"""
        kernel.timer = QtCore.QTimer()
        kernel.timer.timeout.connect(kernel.do_one_iteration)
        kernel.timer.start(1000*kernel._poll_interval)

    def get_connection_file(self):
        """Returne current kernel connection file."""
        return self.connection_file

    def get_user_namespace(self):
        """Returns current kernel userspace dict"""
        return self.kernel.shell.user_ns

class AppForm(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(AppForm, self).__init__(parent)
        self.create_right_frame()
        self.create_left_frame()
        self.create_main_frame()
       
    def create_right_frame(self):
        self.right_frame = QtGui.QWidget()
        self.dpi = 100
        self.fig = Figure((9.0, 5.0), dpi=self.dpi)
        self.canvas = FigureCanvas(self.fig)
        self.mpl_toolbar = NavigationToolbar(self.canvas, self)
        vbox = QtGui.QVBoxLayout()
        vbox.addWidget(self.canvas)
        vbox.addWidget(self.mpl_toolbar)
        self.right_frame.setLayout(vbox)
       
    def create_left_frame(self):
        kernelapp = IPythonLocalKernelApp.instance()
        kernelapp.initialize()
        self.left_frame = QtGui.QWidget()
        vbox = QtGui.QVBoxLayout()
        ipy = IPythonConsoleQtWidget()
        ipy.connect_kernel(connection_file=kernelapp.get_connection_file())
        namespace = kernelapp.get_user_namespace()
        nxxx = 12
        namespace["widget"] = ipy
        namespace["QtGui"]=QtGui
        namespace["nxxx"]=nxxx
        vbox.addWidget(ipy)
        self.left_frame.setLayout(vbox)

    def create_main_frame(self):
        self.main_frame = QtGui.QWidget()
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(self.left_frame)
        hbox.addWidget(self.right_frame)
        self.main_frame.setLayout(hbox)
        self.setCentralWidget(self.main_frame)

class IPythonConsoleQtWidget(RichIPythonWidget):
    def connect_kernel(self, connection_file, heartbeat = False):
        km = QtKernelManager(connection_file=find_connection_file(connection_file), config=self.config)
        km.load_connection_file()
        km.start_channels(hb=heartbeat)
        self.kernel_manager = km
        atexit.register(self.kernel_manager.cleanup_connection_file)

def main():
    app = QtGui.QApplication([])
    widget = AppForm()
    widget.show()
    app.exec_()

if __name__=='__main__':
    main() [/codice]

Ho assemblato questo codice grazie ad un po' di esempi in rete e penso di esserci quasi.
Non riesco pero' a collegare l'output della console nel frame di sinistra al widget canvas sulla destra (cosi' com'e' il codice ora provando a fare un plot si apre una seconda finiestra)

Grazie mille

EDIT:

Carissimi, ho risolto. Per chi fosse interessato si deve aggiungere un metodo ad AppForm tipo

[codice]    def myplot(self, x, y):
        self.axes.plot(x, y)
        self.canvas.draw()[/codice]

ed esporre in namespace il metodo per poterlo usare:

[codice]        namespace["myplot"] = self.myplot[/codice]

 ;)
« Ultima modifica: Febbraio 13, 2013, 16:28 da Aezio »