/** @page pythonoverview_root Python extension modules The @b HippoDraw library can be used with the Python programming language. To do that, a Python extension module is built so that the C++ classes appear as Python classes. Two different extension modules can be built and they have a different interface. The first is @ref python_interact designed to for interactive use or from a Python script This is the @em hippo Python extension module. With it, @b HippoDraw can be manipulated interactively or via scripts written in Python. The second, is @ref python_appli designed for use with PyQt. It is the @em sihippo Python extension module. The two uses are quite different and thus the interface is different. For interactive use of @b HippoDraw, the Boost.Python package is used to build the extension module. Version 2 of Boost.Python is required. It is distributed as part of the Boost distribution since version 1.29.0. This version is simpler and has less problems with common C++ compilers then version 1. The source code for building the Python extension module with Boost.Python is in the @c python subdirectory. To write applications using Qt with Python, the PyQt package can be used. PyQt uses the SIP package to build the Python extension module. Before SIP version 4, SIP and Boost.Python appeared to be incompatible (see the discussion in PyKDE and Python C++-sig mailing list archives). This maybe fixed with version 4, but it has not been tested. Thus, @b HippoDraw has an Python extension module built with SIP as well. All the sip interface sources are in the @c sip subdirectory. Note that the Python extension modules built with Boost.Python seem to be compatible with ones build with SWIG . As there are many more Python extension modules built with SWIG or Boost.Python than with SIP, the Boost.Python built module for @b HippoDraw is always built, while the SIP one is optional. */ /** @page python_interact Python extension module tutorial The @b hippo Python extension module is designed to be used interactively or via Python scripts. Thus the interface is somewhat different from the C++ interface to the @b HippoDraw library. Using @b %HippoDraw interactively can be as simple as two lines of Python code. Below is an example session. @verbatim > python Python 2.4 (#2, Apr 15 2005, 17:09:59) [GCC 3.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import hippo >>> app = hippo.HDApp() >>> @endverbatim Obviously, even typing these two lines for every session can get boring. Instead one can put the commands in a file and use that as initialization step for the Python session. For example, the file, @em canvas.py, in the testsuite directory contains @verbatim import hippo app = hippo.HDApp() canvas = app.canvas() @endverbatim where we also show how to get a handle on the current canvas window. One can run this script from a UNIX shell or Windows command prompt like this @verbatim > python -i canvas.py >>> @endverbatim This launches the complete @b %HippoDraw application in a separate thread with the ability to interact with it from the Python shell. @section python_interact_help Getting help and documentation Python has interactive help system. The get all help for the @em hippo module, just do the following... @verbatim > python Python 2.4 (#2, Apr 15 2005, 17:09:59) [GCC 3.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import hippo >>> help ( hippo ) @endverbatim This gives you all the built-in documentation in a pager like the UNIX @em more or @em less command which is not always convienent. However, one can get the documentation on one class by ... @verbatim > python Python 2.4 (#2, Apr 15 2005, 17:09:59) [GCC 3.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import hippo >>> help ( hippo.HDApp ) @endverbatim Or even one member function like this ... @verbatim > python Python 2.4 (#2, Apr 15 2005, 17:09:59) [GCC 3.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import hippo >>> help ( hippo.HDApp.canvas ) @endverbatim Another way to access the same information is to use the @em pydoc program that came with your Python installation (except under Windows). @verbatim > pydoc hippo @endverbatim This also gives you all the built-in help in a pager. Probably the most convienent method is to generate html version of the documentation. You do this by typing ... @verbatim > pydoc -w hippo @endverbatim and a hippo.html file is created in your working directory. Here is what it looks like. Not very pretty, but it is the standard ouput from @em pydoc. You can use this link as your documentation for @b HippoDraw. However, it will get updated for each release and you may be using an older version. The following sections shows and explains some example script. See also @ref examples_page for more examples @section python_interact_generate Creating the NTuple in Python One might generate some data in Python that you want to display with @b %HippoDraw. For example, you could generate a Python list with random Gaussian distribution like this @verbatim >>> import random >>> x = [] >>> for i in range ( 10000 ) : ... x.append ( random.gauss ( 45, 10 ) ) ... >>> @endverbatim To display the data as a histogram, one can then type @verbatim >>> from hippo import Display >>> hist = Display ( 'Histogram', ( x, ), ('Gaussian', ) ) >>> canvas.addDisplay ( hist ) >>> @endverbatim The first argument to the Display function specifies the type of display to create. The second is a Python tuple of Python list objects that will be used by the display. The third argument is a Python tuple of string labels for the lists. You can now modify the plot, for example, changing the width of the bins in two ways. From the Python shell, one can invoke a member function of the histogram object like this... @verbatim >>> hist.setBinWidth ( 'x', 2 ) >>> @endverbatim But it is much easier to use @ref inspector_axis and change it with the slider or text field. The function created a DataSource called a ListTuple. It holds references to the Python list objects as columns. The list is not copied, just referenced. It also holds the labels of each column. Displays don't redraw themselves unless they know there's been a change, like changing the bin width. But should the contents of your Python list change, the Display wouldn't know about it. But you can force the display to redraw like this... @verbatim >>> hist.update() >>> @endverbatim The Python tuple of strings provide the data source labels, but they also giving the bindings of the displays to the data source. Some displays have binding that are optional. For the example, an "XY Plot" display had binding for the X and Y axis, and optionally, for an error on X or Y. To say which optional bindings not to use the "nil" column label is used. The we can do the following @verbinclude examples/simple_xyplot.py The "nil" string can also be use by the @ref inspector_data as well. Note in this example, we used list of lists instead of tuple of lists. Either can be used. Speaking of the @b Data @b %Inspector, sometimes it is more convenient to give @b HippoDraw all the data you might want to use for displays, and use the @b Data @b %Inspector to create them. To do this, one creates a DataSource manually. There are three kinds supported: ListTuple, NTuple, and NumArrayTuple. They share a common interface and differ on how they store the column data. As we've seen, the ListTuple stores references to Python list objects. The NTuple makes copies of Python list objects and stores it internally as a C++ vector of doubles. The NumArrayTuple stores references to rank 1 numarray objects. The NTuple has the feature that you can add and replace rows or columns. Creating displays with the %DataInspector doesn't preclude one from also creating them with Python. The interface is similar to what we've already seen. For example @verbatim >>> energy = [90.74, 91.06, 91.43, 91.5, 92.16, 92.22, 92.96, 89.24, 89.98 ] >>> sigma = [ 29.0, 30.0, 28.40, 28.8, 21.95, 22.9, 13.5, 4.5, 10.8 ] >>> errors = [ 5.9, 3.15, 3.0, 5.8, 7.9, 3.1, 4.6, 3.5, 4.6,] >>> ntuple = NTuple () # an empty NTuple >>> ntc = NTupleController.instance () >>> ntc.registerNTuple ( ntuple ) >>> ntuple.addColumn ( 'Energy', energy ) >>> ntuple.addColumn ( 'Sigma', sigma ) >>> ntuple.addColumn ( 'error', errors ) >>> xy = Display ( "XY Plot", ntuple, ('Energy', 'Sigma', 'nil', 'error' ) ) >>> canvas.addDisplay ( xy ) >>> @endverbatim Registering the ntuple with the NTupleController is necessary in order for the @b Data @b %Inspector to know of their existence. @section python_interact_file Getting data from a file An %NTuple data source can also be created by reading a plain text file. See @ref file_ascii for the details. The example file, @em histogram.py, in the testsuite directory shows how to read a file and create displays from Python. It contains ... @includelineno histogram.py After reading a @b %HippoDraw compatible @ref ntuple_root file, this Python script creates two displays. It sets the range on the first and the bin width on the second. The results of running this script are shown below. @image html hist_2.png Result of using @em histogram.py @image latex hist_2.eps Result of using @em histogram.py The @c Display class is actually a small wrapper around the internal @b %HippoDraw C++ library class. It is needed because Qt is running in a separate thread from Python. Since creating a display and perhaps modifying it requires interaction with Qt's event loop, the application must be locked before calling a member function of the actual @b %HippoDraw class and then unlocked when returning. @section python_interact_hippoplotter Using the hippoplotter interface Making use of Python's default parameter value feature in calling functions, Jim Chiang has extended the %HippoDraw interface with his @ref hippoplotter. The file, @em pl_exp_test.py, in the testsuite directory shows an example of using this module. @verbinclude pl_exp_test.py The above script leads to the canvas shown below @image html hist_exp.png Results of @em pl_exp_test.py script @image latex hist_exp.eps Results of @em pl_exp_test.py script @section python_interact_extract Extracting data from a display The interaction with @b %HippoDraw from Python is not just one direction. Once can extract data from the displays and use them in Python. The file @em function_ntuple.py illustrates this... @includelineno function_ntuple.py Like the previous script, it fits two functions to a histogram. It also shows how to extract the function parameter names and their values. Near the end of the script, one extracts the contents of the histogram bins in the form of an %NTuple. In the @c for loop at the end, one uses the %NTuple to calculate the residuals between the function and the bin contents and put them in a Python list. The the list is added as a column to the %NTuple. Finally, one creates an XYPlot to display them and adds it to the canvas. The result looks like this... @image html hist_resid.png Results of @em function_ntuple.py @image latex hist_resid.eps Results of @em function_ntuple.py However, one didn't have to write this script to plot the residuals, as the is a control in the @ref inspector_functions that does it for you. @section python_interact_fits Using a FITS file A FITS file can be used as input to @b HippoDraw. Here's how one can it to view an image of the EGRET All-Sky survey from such a file @includelineno egret.py The resulting canvas is shown below @image html canvas_egret.png The EGRET All-Sky survey. @image latex canvas_egret.eps The EGRET All-Sky survey. The FITS data format is a standard astronomical data and mandated by NASA for some projects. It supports images as well as binary or ASCII tables. A FITS table is essentially a %NTuple with added information in the form of keyword-value pairs. James Chiang also wrote the following Python function to convert a FITS table to a @b %HippoDraw %NTuple. @includelineno python/FitsNTuple.py @section python_interact_root Using ROOT files Another example is reading a ROOT file that has the form of an ntuple as define in @ref ntuple_roottuple. The Python code might look like this... @includelineno svac.py This script not only uses ROOT, but it also uses numarray. It converts a ROOT brach into a numarray array so it can do vector calculations. The ROOT C++ macro to do the equivalent of the above Python script would be considerable more complex. @section python_interact_limitations Limitations. With this release, not all of @b %HippoDraw's C++ library is exposed to Python. Although this could be done, it is thought to be not necessary. Rather, selected higher level components are exposed in one of two ways. Some classes are exposed directly with a one to one relationship between the C++ member functions and Python member functions. An example is the NTuple class. One can view the reference documentation for the hippo extension module with Python's online help command, One can also use the pydoc program to view it or generated HTML file with the command "pydoc -w hippo". In order to be able to have an interactive Python session that interacts with the @b %HippoDraw canvas items and at the same time have interaction with the same items from the %Inspector, it was necessary to run the @b %HippoDraw application object in a separate thread. Threading conflicts could then occur. Thus some of @b %HippoDraw's C++ classes are exposed to Python via a thin wrapper class which locks the Qt application object before invoking an action and unlocks it when done. One good thing about Python is that what ever you do, Python never crashes. Thus, what ever you do with the @b %HippoDraw extension module should not crash Python. An interactive user, however, can easily mis-type an argument to a function. For example, he could try to create a display with "ContourPlot" instead of "Contour Plot". For such errors, the C++ library throws a C++ exception. The @b %HippoDraw extension module catches them and translates them to a Python exception. Thus, when the Python user makes an error, he will receive a message instead of crashing his session. Another reason the wrapper classes exist is to try to present a more Python "interactive friendly" interface to the user than the raw C++ interface which was designed for the application writer. With this release, it is not clear what a more "friendly" interface should look like. Maybe the Python extension module should be closer to the C++ interface and provide Python classes to wrap them in a more friendly way like James Chiang has done. Feed back on this topic would be very welcome. */ /** @page python_appli Python extension module: sihippo Applications can be written using @b HippoDraw library in either C++ or Python. The @b HippoDraw's @em sihippo Python extension module is used with PyQt. Thus it uses the SIP tool to built the Python extension module. Modules built with SIP are not compatible with those built with SWIG or Boost.Python. A complete @b HippoDraw application written in Python can be as simple as this... @verbatim > python Python 2.2.3 (#1, Jun 21 2003, 08:34:26) [GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> from qt import * >>> app = QApplication ( sys.argv ) >>> from sihippo import * >>> window = CanvasWindow() >>> window.show() >>> app.exec_loop() @endverbatim This produces the equivalent of the @b HippoDraw stand-a-lone application. With this release, Not all of @b HippoDraw's C++ library has been exposed to Python. Although this could be done, as was done with PyQt, it is not believed to be necessary. Rather, selected high level components have been exposed. More can be exposed in future releases as needed. It is perhaps more interesting to add the @b HippoDraw to an existing application. This can be done by using a @b HippoDraw display as a Qt widget, or by adding a canvas window and %Inspector to an existing application. The @em testwidget.py file in the @em sip subdirectory demonstrates using @b HippoDraw library as a Qt widget. Its contents is... @code import sys from qt import * app = QApplication ( sys.argv ) from sihippo import * ntc = NTupleController.instance() nt = ntc.createNTuple ( '../../hippodraw/examples/aptuple.tnt ) dc = DisplayController.instance(); plotter = dc.createDisplay ( "Histogram", nt, [ 'Cost' ] ) view = QtViewWidget() view.setPlotter ( plotter ) view.resize ( 400, 400 ) app.setMainWidget ( view ) view.setCaption ( "Qt Hippodraw - View widget" ) view.show() app.exec_loop() @endcode Note that the @b HippoDraw library interface with SIP mirrors the C++ interface. Also since calls to these member functions are being done from the same thread as the one that Qt %QApplication object created, no locking or unlocking of the application is needed. The results looks like this... @image html widget_window.png Window with HippoDraw Qt widget. @image latex widget_window.eps Window with HippoDraw Qt widget. At SLAC, a data acquisition system was built entirely with Python and PyQt. @b HippoDraw was added to it to display the collected data. The run control panel is initialized in its @c startup member function like this... @code import sys from qt import * from sihippo import * import RunControl as rc def startup(): # Start up run control app = QApplication(sys.argv) QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()")) canvas = CanvasWindow() canvas.show() runCtl = rc.RunControl() app.setMainWidget(runCtl) runCtl.show() app.exec_loop() @endcode The result is a run control panel, a @b HippoDraw @ref canvas_root and the @b HippoDraw @ref inspector_root. Elsewhere, this example application creates two empty one column NTuples to monitor the run. The code looks like this @code label1 = ['Strip Hits'] label2 = ['Event Size'] ntc = NTupleController.instance() self.__nt = ntc.createNTuple(1) self.__nt2 = ntc.createNTuple ( label2 ) self.__nt.setTitle('Event Readout #1') self.__nt2.setTitle('Event Readout #2') self.__nt.setLabels(label1) self.__nt.reserve ( 512 ) self.__nt2.reserve ( 512 ) @endcode Later in this application's code, two displays are created like this... @code wc = WindowController.instance() canvas = wc.currentCanvas() dc = DisplayController.instance() hist = dc.createDisplay ( 'Histogram', self.__nt, label1 ) hist2 = dc.createDisplay ( 'Histogram', self.__nt2, label2 ) canvas.addDisplay(hist) canvas.addDisplay(hist2) self.__nt.setIntervalCount ( 500 ) self.__nt2.setIntervalCount ( 100 ) @endcode Again, the SIP built interface mirrors the C++ interface. At this point we have displays attached to an empty ntuples. As the ntuples get filled. they send an Observer::update message to their observers. This will cause the displays to be refreshed. The NTuple::setIntervalCount slows down the refresh rate so that application can spend more time collecting data. The first ntuple sends the update message only for every 500 updates of the ntuple, while the second only 100 updates. The @c startRun member function looks like this... @code self.__nt.clear() self.__nt2.clear() wc = WindowController.instance() canvas = wc.currentCanvas() canvas.setIntervalEnabled ( 1 ) cnt = self.__arg.getValue("Enter number of triggers to take") @endcode The ntuples are cleared at the beginning of a run. The interval counting is enabled by calling CanvasWindow::setIntervalEnabled member function. It forward the enabling message to all the ntuples that have displays in the window. At the end of the run, interval counting will be turned off so that the displays will refresh to the current state of the ntuple. The event processing is not very interesting but looks like this... @code def process(self, buffer): "Method called back for each data event taken" self.evtCli.evDumpTKR() for stripData in self.evtCli.evt.TKRstrips: self.__nt.addRow([stripData.ui]) self.__nt2.addRow([self.evtCli.evt_size]) @endcode Thus, the ntuples are built row by row. This applications collects about 80 events per second. With the interval counting enabled, the histograms still refresh about once a second. Without interval counting, the event rate would be cut in half, because the application would be spending so much time drawing new histograms. */