Valid
	XHTML 1.1! Valid CSS!
Created 2006-01-29 Modified 2009-04-12
Chelton Evans

Qt home

Intro
Hello World
Signals and Slots
Layouts
Installing Qwt in Linux
GUI and Data Model Independence

Intro

Qt is a graphics library in a similar sense with Java. It is designed for a graphical gui and is a general purpose library. To use Qt you will need their documentation, I spend most of my time in their "All Classes" page.

Qt is a C++ library that can be compiled on many different operating systems. It has both open source and commercial liciences. Qt does many more thing that I could not give it justice so I won't. Qwt depends on Qt.

When it is not obvious on how to do something I trall through the forum's. qwt-interest can often solve my Qwt question without having to directly email anyone, though there should be a better way to do this I do not have a better solution.

How to Learn Qt   qmake Manual   Qt Reference Documentation


// Animated plot, derive from QwtPlot
  virtual void timerEvent(QTimerEvent *e)
    // update data
    replot();
  // initialize timer
  (void)startTimer(50);

// Derive from QFrame
  virtual void paintEvent(QPaintEvent *)
    QPainter painter(this);
    painter.setClipRect(contentsRect());
    // r is QRect paint region
    curve.draw(&painter, xMap, yMap, r)
// Initilize the curve
  QwtSymbol sym;
  sym.setStyle(QwtSymbol::Cross);
  curve.setSymbol(sym);
  curve.setPen(QCollor(Qt::darkGreen));
  curve.setRawData(xval,val,Size);



Hello World

#include <QApplication>
#include <QPushButton>

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

  QPushButton hello("Hello Cruel World");
  hello.resize(120,50);

  hello.show();
  return app.exec();
}

Save as main.cpp in its own directory hello.

To compile and run,
qmake -project
qmake
make
./hello

For the OpenGL Hello World the OpenGL library needs to be added. I did this by editing the generated project file with QT += opengl

TEMPLATE = app
TARGET +=
DEPENDPATH += .
INCLUDEPATH += .

# Input
HEADERS += glwidget.h window.h
SOURCES += glwidget.cpp main.cpp window.cpp
QT      += opengl

Help Tips

    const char *text2 =
        "Number of random points that will be generated.";

    const char *text3 =
        "Delay between the generation of two random points.";

#if QT_VERSION < 0x040000
    QWhatsThis::add(d_randomCount, text2);
    QWhatsThis::add(d_timerCount, text3);
#else
    d_randomCount->setWhatsThis(text2);
    d_timerCount->setWhatsThis(text3);
#endif

The new way directly binds the gui component to the text. The user gets the tip by clicking on help then the component.

The help icon was put on the menu bar.

// inside derived QMainWindow class
    MyToolBar *toolBar = new MyToolBar(this);  

    ...
    QAction *whatsThisAction = QWhatsThis::createAction(toolBar);
    whatsThisAction->setText("Help");
    toolBar->addAction(whatsThisAction);

Signals and Slots

Callbacks are implemented as signals and slots in Qt. The connect mechanism maps two events to eachother. Each event is allociated with an object. For example notice in the following example how the signal has the same signature as the slot.

The magic of the signal-slot is that this event trigger(the signal) is independent of the target(the slot). There is no equivalent language feature in C++.

  // Implementing Callback on a button.
  // Inside the class declaration
private slots:
  void f1();
private:
  QPushButton *f1Button;
  // In the gui constructor's scope
  f1Button = new QPushButton(tr("f1"));
  connect
  (
    f1Button,
    SIGNAL(clicked()), 
    this, 
    SLOT(f1())
  );
  buttonLayout->addWidget(flButton);
  // in .cpp file do some action when button pressed.
void Dialog::f1()
{
  smallEditor->setPlainText(tr("Message printed"));
}

Animate a drawing in Qt
void CircleWidget::nextAnimationFrame()
Let cirleWidgets[][] be a matrix of CircleWidget's.
QTimer *timer = new QTimer(this);
connect
(
    timer,
    SIGNAL(timeout()),
    circleWidget[i][j],
    SLOT(nextAnimationFrame())
);
timer->start(100); //Fire every 100ms

A quit button
QPushButton *button = new QPushButton("Quit");
QObject::connect
(
    button,
    SIGNAL(clicked()),
    &app,
    SLOT(quit())
);

Doubley connected Widgets

When two widgets display the same parameter if one changes so should the other. Here is the connecting code hooking up a spin box and slider.

// If the spin box changes, change the slider.
QObject::connect
(
    spinBox,
    SIGNAL(valueChanged(int)),
    slider,
    SLOT(setValue(int))
);

// If the slider changes, change the spin box.
QObject::connect
(
    slider,
    SIGNAL(valueChanged(int)),
    spinBox,
    SLOT(setValue(int))
);

Signal to Signal is also possible so when one signal is fired so is the other.
connect
(
    lcd,
    SIGNAL(textChaned(const QString &)),
    this,
    SIGNAL(updateRecord(const QString &))
)

Layouts

A layout is an arrangement of graphical objects which Qt calls widgets. Examples of layouts are QGridLayout, QHBoxLayout, QVBoxLayout. When a widget is created it has a layout associated with it.

  ... vbox is a QVBoxLayout object
  QWidget * qw = new QWidget();
  qw->setLayout(vbox);
  qw->show();

Designing a Gui interface requires dividing the space up with nested widgets. This is refered to as the gui's geometry. For example

  <label>
  <spin box> <slider>
  <button>

This is composed of a vertical box layout where three components were added. The middle component is itself a horizontal box layout with two widgets added to it.

For a horizontal layout which has a title and thin black line border.

  horizontalGroupBox = new QGroupBox(tr("Horizontal layout"));
  // Add buttons to the layout.
  QHBoxLayout *layout = new QHBoxLayout();
  layout->addWidget(button1); ...
  // Add a layout to the QGroupBox
  horozontalGroupBox->setLayout(layout);
  ...
  mainLayout->addWidget(horizontalGroupBox);

Installing Qwt in Linux

First install Qt.

Download the Qwt source. I used anonymous cvs, the commands on Qwt page.

For some reason after building Qwt there was no make install option that worked. So I copied the library libqwt.so.5.0.0.0 to /lib and created the sym links.

[root@localhost lib]# ln -s libqwt.so.5.0.0 libqwt.so.5.0

The error message I got when I tried to run the programs was
curvdemo1/curvdemo1: error while loading shared libraries: libqwt.so.5: cannot open shared object file: No such file or directory

qmake is needed by Qwt. Since this is local in scope to the current user simply put the directory that it resides in in your path. On my system the directory was /usr/local/Trolltech/Qt-4.1.2/bin/. Inside a script I redefine the PATH variable
declare PATH=$PATH:/lib/:/usr/local/Trolltech/Qt-4.1.2/lib

Installing the documentation is a matter of running Doxygen in QWT's home directory. For the diagrams you will need the dot program but if you have not got dot installed its ok, else get it from The DOT Language.

GUI and Data Model Independence

The idea of separating the data from the model is not new, indeed the idea of OO is to separate the two. Despite over 20 years of experience it is still common practise to mix the two and often assume and not discuss this.

Qt has the same approach. Because the signal/slot mechanism demands that an object be derived from QObject there is a real temptation to mix your data and display together. Java provides a means for a direct callback so it is possible to do the separation in another language. Perhaps I have misunderstood Qt or this will be made possible at a later date.

Here is an example where I split the data model from its display by deriving from QObject a class that contains a pointer to the data model. This derived class which I called DataModelPtr is responsible for managing the widgets and the relationship between the data model and display. If a widget changes the data then the data model has to be changed too. If the data model changes a call to update() will change the display.

The linking between the data model and the two gui widgets is a little too complicated for my liking. Simply we should just set the new values and directly tell the widgets to update when the model changes. However this model appears to be robust.

// DataModel.h
class DataModel
{
    int age;
public:

    void setAge(int _age);
    int getAge() const;
};

// DataModelPtr.h
class DataModelPtr : public QObject
{
    Q_OBJECT
public slots:

    void setAge(int);
    int getAge() const;

public:

    // This class stores a pointer to the true data model.
    DataModelPtr(DataModel * _ptr);

    // If the data model changes call this function.
    void update();

private:
    DataModel *ptr;

    QSpinBox *spinBox;
    QSlider *slider;
    QHBoxLayout *hbox;
    QWidget *qw;
};

// DataModelPtr.cpp file
void DataModelPtr::update()
{
    int age=getAge();
    slider->setValue(age);
    spinBox->setValue(age);
}

DataModelPtr::DataModelPtr(DataModel * _ptr)
  : ptr(_ptr)
{
    assert(ptr!=0);

    spinBox = new QSpinBox();
    spinBox->setRange(0,130);

    slider = new QSlider(Qt::Horizontal);
    slider->setRange(0,130);

    ptr->setAge(40);
    update();

    hbox = new QHBoxLayout();
    hbox->addWidget(spinBox);
    hbox->addWidget(slider);


    // If the spin box changes, change the slider.
    QObject::connect
    (
        spinBox,
        SIGNAL(valueChanged(int)),
        slider,
        SLOT(setValue(int))
    );

    // If the slider changes, change the spin box.
    QObject::connect
    (
        slider,
        SIGNAL(valueChanged(int)),
        spinBox,
        SLOT(setValue(int))
    );

    //QObject::connect
    QObject::connect
    (
        spinBox,
        SIGNAL(valueChanged(int)),
        this,
        SLOT(setAge(int))
    );

    QObject::connect
    (
        slider,
        SIGNAL(valueChanged(int)),
        this,
        SLOT(setAge(int))
    );

    qw = new QWidget();

    qw->setWindowTitle("Enter your age");

    qw->setLayout(hbox);
    qw->show();

}

void DataModelPtr::setAge(int age)
{
    ptr->setAge(age);
}

int DataModelPtr::getAge() const
{
    return ptr->getAge(); 
}