I’ve been working on learning how to use the QT4 framework in python with the PySide bindings. I’m trying to move into more app-centered development as opposed to just doing all of my work from a command line. I work in an environment with very few actual programmers, and if I can develop real apps to do analysis, then it’s much easier for me to share my tools than if I develop command-line programs. GUIs are just much more accessible to non-programmers, and I like being useful to my coworkers.
About a year ago I picked up the Enthought Tool Suite and gave it a try. Traits itself is extremely useful, and I’ve switched to using chaco and Mayavi for 2d and 3d plotting in applications. My feelings are mixed on TraitsUI, however. The goal was to make a graphical interface to Traits-using classes, and it does that quite well. It’s a good extension to command line work. But it falls short of a full UI. It’s not something that I can easily code and wrap up as a self-contained app to hand off to one of my coworkers.
So I’ve been looking for alternatives. In the last edition of the Enthought Python Distribution, they included QT4 and the PySide python bindings, so I’ve been learning how to use PySide to create my interfaces. I’ve been having a blast. It’s much easier to lay out elements; QTDesigner is free, and you can export layouts to python code for easy use. The major problem I’ve been having is with information passing between QT code and my workhorse code. In particular, getting chaco image plots to work.
So here’s a short sample of how to pass data back and forth.
import sys from numpy import arange, sin, array from PySide.QtCore import * from PySide.QtGui import * app = QApplication(sys.argv) from enthought.etsconfig.etsconfig import ETSConfig ETSConfig.toolkit = "qt4" from enthought.enable.api import Window from enthought.chaco.api import ArrayPlotData, Plot class Viewer(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.plotview = Plotter(self) self.setCentralWidget(self.plotview.widget) x = arange(10) y = sin(x) self.plotview.update_data(x, y) class Plotter(): def __init__(self, parent): self.plotdata = ArrayPlotData(x=array(), y=array()) self.window = self.create_plot(parent) self.widget = self.window.control def update_data(self, x, y): self.plotdata.set_data("x", x) self.plotdata.set_data("y", y) def create_plot(self, parent): plot = Plot(self.plotdata, padding=50, border_visible=True) plot.plot(("x", "y"), name="data plot", color="green") return Window(parent, -1, component=plot) if __name__ == "__main__": plot = Viewer() plot.resize(600, 400) plot.show() sys.exit(app.exec_())
There’s a couple of things to keep an eye on. Note that I create the QApplication up in the header region before my Enthought imports. That’s because the Enthought imports will try to create one themselves, and you can’t have two active at once. It’s also important to set the Enthought toolkit to qt4 or it will use the wxpython bindings and you won’t be able to pass data around properly.
The central part of the link is the Enable Window object that gets created by Plotter.create_plot(). The Window.control attribute subclasses from QWidget, so we can use it like any other widget in our QT framework. Here I’m just setting it as the central widget of the main window, but you can put it in layouts just like anything else. Data actually moves back and forth by updating the ArrayPlotData instance that the chaco plots take their data from. Any time we change the data there, the chaco plot will automatically update.