Building custom applications with C++

One can build custom applications with HippoDraw's C++ class library along with Qt.

Custom application with canvas

To build an application with the CanvasWindow and Inspector, it can be as simple as the one shown below...

#include "QtApp.h"

int main ( int argc, char** argv)
{
  QtApp app ( argc, argv );
  app.setFirstWindow();
  
  return app.exec();
}

Your custom code, say to generate the data source data should be inserted before the call to app.exec() as that function starts the Qt event loop and doesn't return until the application is terminated.

If you already have a Qt application, then you can add the HippoDraw CanvasWindow and Inspector to your application by adding code to your main with something like the implementation of QtApp::setFirstWindow does. The result might look like this ...

#include "qt/CanvasWindow.h"
#include "qapplication.h"


int main ( int argc, char** argv)
{
  MyApp app ( argc, argv );
  
  CanvasWindow * window = new CanvasWindow ();
  window->show();

  return app.exec();
}

One doesn't need to create the CanvasWindow before starting the Qt event loop. It can be created at any time.

Custom application with widget in window

This section describes how to put a HippoDraw plot in a single window.

First the QApplication object is created and the NTupleController is used to create an NTuple by reading a file as shown here.

int main ( int argc, char **argv )
{
    QApplication app ( argc, argv );

    const string filename ( "../../../hippodraw/examples/aptuple.tnt" );
    NTupleController * nt_controller = NTupleController::instance ();
    NTuple * nt 
      = nt_controller->createNTuple ( filename  );

In a custom application, you will probably have other ways of creating the NTuple. If you want the NTuple visible to the Inspector, then you must register it with the NTupleController like this ...

    NTuple * nt = // create your NTuple somehow 
    NTupleController * nt_controller = NTupleController::instance ();

    nt_controller->registerNTuple ( nt );

Next a display is created bound to the NTuple and one of its columns.

    const string histo ( "Histogram" );
    vector < string > bindings;
    bindings.push_back ( "Cost" );

    DisplayController * dc_controller = DisplayController::instance ();
    PlotterBase * plotter 
      = dc_controller->createDisplay ( histo, *nt, bindings );

Note that DisplayController creates the appropriate class derived from PlotterBase for the kind of display you requested.

Plotter objects are used both for canvas items and widgets. In this case, a QtViewWidget is created and the plotter is attached to it.

    QtViewWidget * view = new QtViewWidget ( );
    view->setPlotter ( plotter );

Finally, the view it set into the Qt main window, resized, captioned, and the event loop started.

    view->resize ( 200, 200 );
    app.setMainWidget( view );
    view->setCaption ( "Qt HippoDraw - View widget" );
    view->show();

    int result = a.exec();

Note that the above code used methods that QtViewWidget inherits from Qt's QWidget class.

Don't forget to clean up when you are done.

    delete view;
    delete nt;

    return result;
}

The resulting application window looks like this ...

widget_window.png

QtViewWidget set as application's main window.

The complete code is shown below ...

00001 
00007 #include "qt/QtViewWidget.h"
00008 
00009 #include "controllers/DisplayController.h"
00010 #include "datasrcs/NTupleController.h"
00011 #include "datasrcs/NTuple.h"
00012 #include "plotters/PlotterBase.h"
00013 
00014 #include <qapplication.h>
00015 
00016 #include <string>
00017 #include <vector>
00018 
00019 using std::string;
00020 using std::vector;
00021 
00022 
00030 using namespace hippodraw;
00031 
00032 int main ( int argc, char **argv )
00033 {
00034     QApplication a( argc, argv );
00035 
00036     const string filename ( "../../../hippodraw/examples/aptuple.tnt" );
00037     NTupleController * nt_controller = NTupleController::instance ();
00038     DataSource * nt 
00039       = nt_controller->createNTuple ( filename  );
00040 
00041     const string histo ( "Histogram" );
00042     vector < string > bindings;
00043     bindings.push_back ( "Cost" );
00044 
00045     DisplayController * dc_controller = DisplayController::instance ();
00046     PlotterBase * plotter 
00047       = dc_controller->createDisplay ( histo, *nt, bindings );
00048 
00049     QtViewWidget * view = new QtViewWidget ( );
00050     view->setPlotter ( plotter );
00051 
00052     view->resize ( 200, 200 );
00053     a.setMainWidget( view );
00054     view->setCaption ( "Qt HippoDraw - View widget" );
00055     view->show();
00056 
00057     int result = a.exec();
00058 
00059     delete view;
00060     delete nt;
00061 
00062     return result;
00063 }

Custom application with custom widget in Qt Designer

One can use HippoDraw's QtViewWidget as a custom widget within Qt Designer. When doing so, your main program may look like this

#include <qapplication.h>
#include "QtViewWidgetWindow.h"

int main( int argc, char ** argv )
{
    QApplication app ( argc, argv );
    QtViewWidgetWindow w;
    w.show();
    app.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) );

    return app.exec();
}

In this example, the class QtViewWidgetWindow was built with Qt Designer. It was created by asking for a new main window. QtViewWidget was inserted in Qt Designer as a custom widget. The results look like this ...

custom_widget_designer.png

QtViewWidget as custom widget

In this example, the fileOpen() slot as implemented like the implementation in the Custom application with canvas example. But in addition, the implementation creates an Inspector and signals it to update itself with the created plotter. The code look like this ...

  m_inspector = new Inspector ();
  m_inspector->show();
  QCustomEvent * event = new QCustomEvent ( QEvent::User, plotter );
  QApplication::postEvent ( m_inspector, event );

One might have expected a direct call to Inspector::update following the Observer pattern instead of this implementation. However, it was found that under the Windows operating system, such a direct call caused problems with the OS's threading model. So the QCustomEvent is used to avoid the problem.

Some part of the application may cause a change to the plotter. When that happens, one needs to update the Inspector. Following the Observer pattern, when something in the plotter changes, it sends an update message to its observer which is the QtViewWidget. It in turn, needs to send a message to its parent which is the QtViewWidgetWindow object. It uses a QCustomEvent to do this, so the implementation of the QtViewWidgetWindow must implement it something like this ...

void QtViewWidgetWindow::customEvent ( QCustomEvent * event )
{
  void * data = event->data();
  QCustomEvent * e = new QCustomEvent ( QEvent::User, data );

  QApplication::postEvent ( m_inspector, e );
}

After opening the file, the window looks like this ...

custom_window.png

QtViewWidgetWindow after opening file

The complete source code is show below ...

00001 /* -*- mode: c++ -*- */
00002 /****************************************************************************
00003 ** ui.h extension file, included from the uic-generated form implementation.
00004 **
00005 ** If you wish to add, delete or rename functions or slots use
00006 ** Qt Designer which will update this file, preserving your code. Create an
00007 ** init() function in place of a constructor, and a destroy() function in
00008 ** place of a destructor.
00009 *****************************************************************************/
00010 
00011 using std::string;
00012 using std::vector;
00013 
00014 using namespace hippodraw;
00015 
00018 void QtViewWidgetWindow::init()
00019 {
00020 
00021 }
00022 
00023 void QtViewWidgetWindow::fileOpen()
00024 {
00025 #if QT_VERSION < 0x040000 // 3.1.0
00026    QString file_name 
00027     = QFileDialog::getOpenFileName ( QString::null, // starting directory
00028                                      "ntuple (*.*)", // filter
00029                                      this, // parent
00030                                      "open ntuple file", // internal name
00031                                      "Open ntuple" ); // actual window caption
00032 #else
00033    QString file_name 
00034     = Q3FileDialog::getOpenFileName ( QString::null, // starting directory
00035                                      "ntuple (*.*)", // filter
00036                                      this, // parent
00037                                      "open ntuple file", // internal name
00038                                      "Open ntuple" ); // actual window caption
00039 #endif
00040   if ( file_name.isEmpty () ) return;
00041 
00042 #if QT_VERSION < 0x030100 // 3.1.0
00043   const string filename ( file_name );  
00044 #else
00045   const string filename( file_name.latin1() );
00046 #endif
00047  NTupleController * nt_controller = NTupleController::instance ();
00048  DataSource * nt 
00049     = nt_controller->createNTuple ( filename  );
00050 
00051   const string histo ( "Histogram" );
00052   vector < string > bindings;
00053   bindings.push_back ( "Cost" );
00054 
00055   DisplayController * dc_controller = DisplayController::instance ();
00056   PlotterBase * plotter 
00057       = dc_controller->createDisplay ( histo, *nt, bindings );
00058 
00059   m_view->setPlotter ( plotter );
00060 
00061   m_inspector = new Inspector ();
00062   m_view -> setInspector ( m_inspector );
00063 
00064   m_inspector->show();
00065   m_view->update ( plotter );
00066   PlotterEvent * event = new PlotterEvent ( plotter );
00067   QApplication::postEvent ( m_inspector, event );
00068 }
00069 
00070 void QtViewWidgetWindow::filePrint()
00071 {
00072 
00073 }
00074 
00075 void QtViewWidgetWindow::fileExit()
00076 {
00077 qApp->quit();
00078 }
00079 
00080 void QtViewWidgetWindow::helpAbout()
00081 {
00082 
00083 }

Generated for HippoDraw by doxygen