1. Introduction
It would be nice if we can interact directly with widgets in our GUI interfaces. Let us start with a simple Qt GUI interface that we can interact with. Its screen shot is as follows:
A Qt GUI Interface with 2 OpenGL widgets (top row, left two) and other native Qt widgets
You may download source code for this Qt project from this link: http://www.yousendit.com/download/MFo3TkFsT010Ni92Wmc9PQ
2. A Brief Description of the Qt Project
To begin with, let us take a look at its Qt project file and get an idea of C++ source code files in it:
Demo.pro
QT += opengl
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
HEADERS += circlebar.h \
circlebarwindow.h \
flagwidget.h \
glwidget.h \
helper.h \
renderarea.h \
widget.h \
window.h
SOURCES += circlebar.cpp \
circlebarwindow.cpp \
flagwidget.cpp \
glwidget.cpp \
helper.cpp \
main.cpp \
renderarea.cpp \
widget.cpp \
window.cpp
The US national flag is rendered with OpenGL code in class FlagWidget defined in flagwidget.h and implemented in flagwidget.cpp.
flagwidget.h
#ifndef FLAGWIDGET_H
#define FLAGWIDGET_H
#include <QGLWidget>
class FlagWidget : public QGLWidget
{
Q_OBJECT
public:
explicit FlagWidget(QWidget *parent = 0);
protected:
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
private:
void drawStar(float fX, float fY);
void drawStars();
void drawStripes();
};
#endif // FLAGWIDGET_H
As can be seen in the following implementation file, two private methods, FlagWidget::drawStars() and FlagWidget::drawStripes, are directly called in FlagWidget::paintGL() method. The FlagWidget::drawStars() method then calls the third private method, FlagWidget::drawStar(). These private methods implement our algorithm to draw the flag with OpenGL code. They are private so that we can easily switch from one implementation/algorithm of today to anther in the future without changing on its interface. Our clients can happily continue using new implementation/algorithm without noticing our change, a nice object-oriented feature of C++ encapsulation. Please note that the US flag must have a width/height ratio of 1.9, which is enforced on line 8 of the following cpp file:
flagwidget.cpp
#include "flagwidget.h"
#include <math.h>
FlagWidget::FlagWidget(QWidget *parent) :
QGLWidget(parent)
{
// flag ratio (W/H) must be 1.9
setFixedSize( 418, 220 );
}
void FlagWidget::initializeGL()
{
glClearColor(0.0, 0.0, 102.0/255.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
}
void FlagWidget::resizeGL(int width, int height)
{
glViewport(0, 0, (GLint)width, (GLint)height);
}
void FlagWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
drawStripes();
drawStars();
glFlush();
}
void FlagWidget::drawStar(float fX, float fY)
{
const float kfPi = 3.1415926535897932384626433832795;
const float kfRadius = 0.0616/2.0;
const float kfInnerRadius = kfRadius*(1.0/(sin((2.0*kfPi)/5.0)*2.0*cos(kfPi/10.0) + sin((3.0*kfPi)/10.0)));
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_TRIANGLE_FAN);
glVertex3f(fX, fY, 0.0);
for (int iVertIndex = 0; iVertIndex < 10; ++iVertIndex)
{
float fAngleStart = kfPi/2.0 + (iVertIndex*2.0*kfPi)/10.0;
float fAngleEnd = fAngleStart + kfPi/5.0;
if (iVertIndex % 2 == 0)
{
glVertex3f(fX + kfRadius*cos(fAngleStart)/1.9, fY + kfRadius*sin(fAngleStart), 0.0);
glVertex3f(fX + kfInnerRadius*cos(fAngleEnd)/1.9, fY + kfInnerRadius*sin(fAngleEnd), 0.0);
} else
{
glVertex3f(fX + kfInnerRadius*cos(fAngleStart)/1.9, fY + kfInnerRadius*sin(fAngleStart), 0.0);
glVertex3f(fX + kfRadius*cos(fAngleEnd)/1.9, fY + kfRadius*sin(fAngleEnd), 0.0);
}
}
glEnd();
}
void FlagWidget::drawStars()
{
for (int iStarRow = 0; iStarRow < 9; ++iStarRow)
{
float fY = 6.0/13.0 + (iStarRow + 1)*((7.0/13.0)/10);
if (iStarRow % 2 == 0)
{
for (int iStarCol = 0; iStarCol < 6; ++iStarCol)
drawStar(iStarCol*((0.76/1.9)/6.0) + (0.76/1.9)/12.0, fY);
} else {
for (int iStarCol = 0; iStarCol < 5; ++iStarCol)
drawStar((iStarCol + 1)*((0.76/1.9)/6.0), fY);
}
}
}
void FlagWidget::drawStripes()
{
for (int iStripeIndex = 0; iStripeIndex < 13; ++iStripeIndex)
{
if (iStripeIndex % 2 == 0)
glColor3f(204.0/255.0, 0.0, 0.0);
else
glColor3f(1.0, 1.0, 1.0);
float fStartX = 0.0;
float fEndX = 1.0;
float fStartY = iStripeIndex*(1.0/13.0);
float fEndY = (iStripeIndex + 1)*(1.0/13.0);
if (iStripeIndex > 5)
fStartX = 0.76/1.9;
glBegin(GL_QUADS);
glVertex3f(fStartX, fStartY, 0.0);
glVertex3f(fEndX, fStartY, 0.0);
glVertex3f(fEndX, fEndY, 0.0);
glVertex3f(fStartX, fEndY, 0.0);
glEnd();
}
}
The OpenGL Widget is defined in glwidget.h and implemented in glwidget.cpp. The Native Qt Widget is defined in widget.h and implemented in widget.cpp. They both use the Helper class declared in helper.h and implemented in helper.cpp for painting after setting up the content for the QPainter. The QTimer is used to create the animation effect. I included source code for these three classes from Qt documentation.
There is no visual difference between the two widgets. However, on systems with hardware acceleration, the OpenGL widget should have obvious performance improvement over the native Qt widget.
glwidget.h
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QGLWidget>
class Helper;
class QPaintEvent;
class QWidget;
class GLWidget : public QGLWidget
{
Q_OBJECT
public:
explicit GLWidget(Helper *helper, QWidget *parent = 0);
public slots:
void animate();
protected:
void paintEvent(QPaintEvent *event);
private:
Helper *helper;
int elapsed;
};
#endif // GLWIDGET_H
glwidget.cpp
#include <QtGui>
#include "glwidget.h"
#include "helper.h"
GLWidget::GLWidget(Helper *helper, QWidget *parent)
: QGLWidget(QGLFormat(QGL::SampleBuffers), parent), helper(helper)
{
elapsed = 0;
setFixedSize(220, 220);
setAutoFillBackground(false);
}
void GLWidget::animate()
{
elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
repaint();
}
void GLWidget::paintEvent(QPaintEvent *event) {
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
helper->paint(&painter, event, elapsed);
painter.end();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Helper;
class QPaintEvent;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(Helper* helper, QWidget *parent = 0);
public slots:
void animate();
protected:
void paintEvent(QPaintEvent *event);
private:
Helper *helper;
int elapsed;
};
#endif // WIDGET_H
widget.cpp
#include <QtGui>
#include "helper.h"
#include "widget.h"
Widget::Widget(Helper* helper, QWidget *parent)
:QWidget(parent), helper(helper)
{
elapsed = 0;
setFixedSize(220, 220);
}
void Widget::animate()
{
elapsed = (elapsed + qobject_cast<QTimer*>(sender())->interval()) % 1000;
repaint();
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
helper->paint(&painter, event, elapsed);
painter.end();
}
helper.h
#ifndef HELPER_H
#define HELPER_H
#include <QBrush>
#include <QFont>
#include <QPen>
class QPainter;
class QPaintEvent;
class Helper
{
public:
Helper();
public:
void paint(QPainter *painter, QPaintEvent *event, int elapsed);
private:
QBrush background;
QBrush circleBrush;
QPen circlePen;
QFont textFont;
QPen textPen;
};
#endif // HELPER_H
helper.cpp
#include <QPainter>
#include <QPaintEvent>
#include "helper.h"
Helper::Helper()
{
QLinearGradient gradient(QPointF(50, 20), QPointF(80, 20));
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39));
background = QBrush(QColor(64, 32, 64));
circleBrush = QBrush(gradient);
circlePen = QPen(Qt::black);
circlePen.setWidth(1);;
textPen = QPen(Qt::white);
textFont.setPixelSize(50);
}
void Helper::paint(QPainter *painter, QPaintEvent *event, int elapsed)
{
painter->fillRect(event->rect(), background);
painter->translate(100, 100);
painter->save();
painter->setBrush(circleBrush);
painter->setPen(circlePen);
painter->rotate(elapsed * 0.020);
qreal r = elapsed/1000.0;
int n = 30;
for (int i = 0; i < n; ++i) {
painter->rotate(30);;
qreal radius = 0 + 120*((i+r)/n);
qreal circuleRadius = 1 + ((i+r)/n)*20;
painter->drawEllipse(QRectF(radius, -circuleRadius,
circuleRadius*2, circuleRadius*2));
}
painter->restore();
painter->setPen(textPen);
painter->setFont(textFont);
painter->drawText(QRectF(-50, -50, 100, 100), Qt::AlignCenter, "Qt");
}
The top, right widget, in the form of CircleBar class, is defined in circlebar.h and implemented in circlebar.cpp. In order to put a horizontal slider under it and lay them out in our interface, the class CircleBarWindow is declared in circlebarwindow.h and implemented in circlebarwindow.cpp. The outer, black, circle is drawn when the interface is up. The user can change the diameter of the inner circle (drawn like a red rose) by moving the slider. The diameter of the rose in pixel is automatically updated and shown below the slider. Alternatively, the user can move the mouse into the outer, black, circle and use mouse wheel to change its diameter.
circlebar.h
#ifndef CIRCLEBAR_H
#define CIRCLEBAR_H
#include <QWidget>
class CircleBar : public QWidget
{
Q_OBJECT
public:
CircleBar(int value = 0, QWidget *parent = 0);
int value() const;
int heightForWidth(int) const;
protected slots:
void setValue(int);
signals:
void valueChanged(int);
protected:
void paintEvent(QPaintEvent *);
void wheelEvent(QWheelEvent *);
private:
int m_value;
};
#endif // CIRCLEBAR_H
circlebar.cpp
#include <QPaintEvent>
#include <QWheelEvent>
#include <QPainter>
#include <QRadialGradient>
#include "circlebar.h"
CircleBar::CircleBar(int value, QWidget *parent)
: QWidget(parent)
{
m_value = value;
QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred);
policy.setHeightForWidth( true );
setSizePolicy( policy );
}
int CircleBar::heightForWidth(int width) const
{
return width;
}
int CircleBar::value() const
{
return m_value;
}
void CircleBar::setValue(int value)
{
if (value < 0 )
value = 0;
if (value > 100)
value = 100;
if (m_value == value)
return;
m_value = value;
update();
emit valueChanged(m_value);
}
void CircleBar::paintEvent(QPaintEvent *)
{
int radius = width()/2;
double factor = m_value/100.0;
QPainter painter( this );
painter.setPen(Qt::black );
painter.drawEllipse(0, 0, width()-1, width()-1);
QRadialGradient radGrad( QPointF(radius, radius), 10 );
radGrad.setColorAt( 0, Qt::red );
radGrad.setColorAt( 1, Qt::white );
radGrad.setSpread( QGradient::RepeatSpread );
painter.setPen(Qt::white );
painter.setBrush( radGrad );
painter.drawEllipse( int(radius*(1.0-factor)), int(radius*(1.0-factor)),
int((width()-1)*factor)+1, int((width()-1)*factor)+1 );
}
void CircleBar::wheelEvent(QWheelEvent *event)
{
event->accept();
setValue( value() + event->delta()/20 );
}
circlebarwindow.h
#ifndef CIRCLEBARWINDOW_H
#define CIRCLEBARWINDOW_H
#include <QWidget>
#include <QSlider>
class CircleBarWindow : public QWidget
{
Q_OBJECT
public:
explicit CircleBarWindow(QWidget *parent = 0);
// getter to encapsulate the private QSlider *slider;
const QSlider *getSliderPtr() const;
private:
QSlider *slider;
};
#endif // CIRCLEBARWINDOW_H
circlebarwindow.cpp
#include <QLabel>
#include <QGridLayout>
#include "circlebarwindow.h"
#include "circlebar.h"
CircleBarWindow::CircleBarWindow(QWidget *parent) :
QWidget(parent)
{
CircleBar *bar = new CircleBar(0, this);
slider = new QSlider(Qt::Horizontal, this);
QGridLayout *layout = new QGridLayout(this);
layout->addWidget( bar, 0, 0, 2, 2);
layout->addWidget( slider, 5, 0, 1, 1);
connect( slider, SIGNAL(valueChanged(int)), bar, SLOT(setValue(int)) );
connect( bar, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)) );
}
const QSlider *CircleBarWindow::getSliderPtr() const
{
return slider;
}
Please note that I declared the “const QSlider *getSliderPtr() const" on line 14 of the circlebarwindow.h and implemented it on lines 20 to 23 of the circlebarwindow.cpp file. This is how I strive to prevent users from messing up with the naked pointer (QSlider *slider). It would be dangerous to simply declare the pointer as public and allow clients to mess it up, a trap that we can easily fall into.
The last four widgets in the second row of the screen shot use RenderArea class declared in renderarea.h and implemented in renderarea.cpp. RenderArea class defines the area for painter to paint, specify what operation to be performed on one of the shapes, draw the x and y axes in red lines, translate, scale or rotate
the coordinate system, etc.
renderarea.h
#ifndef RENDERAREA_H
#define RENDERAREA_H
#include <QFont>
#include <QList>
#include <QPainterPath>
#include <QRect>
#include <QWidget>
class QPaintEvent;
enum Operation {NoTransformation, Translate, Rotate, Scale };
class RenderArea : public QWidget
{
Q_OBJECT
public:
RenderArea(QWidget *parent = 0);
void setOperations(const QList<Operation> &operations);
void setShape(const QPainterPath &shape);
QSize minimumSizeHint() const;
QSize sizeHint() const;
protected:
void paintEvent(QPaintEvent *event);
private:
void drawCoordinates(QPainter &painter);
void drawOutline(QPainter &painter);
void drawShape(QPainter &painter);
void transformPainter(QPainter &painter);
QList<Operation> operations;
QPainterPath shape;
QRect xBoundingRect;
QRect yBoundingRect;
};
#endif // RENDERAREA_H
renderarea.cpp
#include <QPainter>
#include <QPaintEvent>
#include "renderarea.h"
RenderArea::RenderArea(QWidget *parent)
: QWidget(parent)
{
QFont newFont = font();
newFont.setPixelSize(12);
setFont(newFont);
QFontMetrics fontMetrics(newFont);
xBoundingRect = fontMetrics.boundingRect(tr("x"));
yBoundingRect = fontMetrics.boundingRect(tr("y"));
}
void RenderArea::setOperations(const QList<Operation> &operations)
{
this->operations = operations;
update();
}
void RenderArea::setShape(const QPainterPath &shape)
{
this->shape = shape;
update();
}
QSize RenderArea::minimumSizeHint() const
{
return QSize(182, 182);
}
QSize RenderArea::sizeHint() const
{
return QSize(232, 232);
}
void RenderArea::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.fillRect(event->rect(), QBrush(Qt::white));
painter.translate(66, 66);
painter.save();
transformPainter(painter);
drawShape(painter);
painter.restore();
drawOutline(painter);
transformPainter(painter);
drawCoordinates(painter);
}
void RenderArea::drawCoordinates(QPainter &painter)
{
painter.setPen(Qt::red);
painter.drawLine(0, 0, 50, 0);
painter.drawLine(48, -2, 50, 0);
painter.drawLine(48, 2, 50, 0);
painter.drawText(60 - xBoundingRect.width() / 2,
0 + xBoundingRect.height() / 2, tr("x"));
painter.drawLine(0, 0, 0, 50);
painter.drawLine(-2, 48, 0, 50);
painter.drawLine(2, 48, 0, 50);
painter.drawText(0 - yBoundingRect.width() / 2,
60 + yBoundingRect.height() / 2, tr("y"));
}
void RenderArea::drawOutline(QPainter &painter)
{
painter.setPen(Qt::darkGreen);
painter.setPen(Qt::DashLine);
painter.setBrush(Qt::NoBrush);
painter.drawRect(0, 0, 100, 100);
}
void RenderArea::drawShape(QPainter &painter)
{
painter.fillPath(shape, Qt::blue);
}
void RenderArea::transformPainter(QPainter &painter)
{
for (int i = 0; i < operations.size(); ++i) {
switch (operations[i]) {
case Translate:
painter.translate(50, 50);
break;
case Scale:
painter.scale(0.75, 0.75);
break;
case Rotate:
painter.rotate(60);
break;
case NoTransformation:
default:
;
}
}
}
The four widgets in the second row of the screen short are actually created and laid out with class Window declared in in window.h and an implemented in window.cpp. So are all other widgets we saw in our interface. From the combo drop down menu, we can select either "Clock", "House", "Text" or "Truck" shape to draw, select either "No transformation", "Rotate by 60 degrees", "Scale to 75%", or "Translate by (50, 50)" operation to perform. We can see how these widgets and other ones are laid out in our interface in the following two files (I adopted code for renderearea.h, rendereare.cpp and window.h from the Book "Foundations of Qt Development" by Johan Thelin and modified its code in window.cpp for this project):
window.h
#ifndef WINDOW_H
#define WINDOW_H
#include <QList>
#include <QPainterPath>
#include <QWidget>
#include <QComboBox>
#include <QLabel>
#include "renderarea.h"
#include "helper.h"
class Window : public QWidget
{
Q_OBJECT
public:
Window();
public slots:
void operationChanged();
void shapeSelected(int index);
private:
void setupShapes();
enum { NumTransformedAreas = 3 };
QComboBox *operationComboBoxes[NumTransformedAreas];
RenderArea *transformedRenderAreas[NumTransformedAreas];
QList<QPainterPath> shapes;
RenderArea *originalRenderArea;
QComboBox *shapeComboBox;
Helper helper;
};
#endif // WINDOW_H
window.cpp
#include <QGridLayout>
#include <QTimer>
#include "window.h"
#include "glwidget.h"
#include "widget.h"
#include "flagwidget.h"
#include "circlebarwindow.h"
Window::Window() : helper(Helper() )
{
FlagWidget *flag = new FlagWidget;
QLabel *flagLabel = new QLabel(tr("US National Flag Rendered with OpenGL"));
flagLabel->setAlignment( Qt::AlignHCenter);
GLWidget *openGL = new GLWidget(&helper);
QLabel *openGLLabel = new QLabel(tr("OpenGL Widgets (Left Two)"));
openGLLabel->setAlignment((Qt::AlignHCenter));
Widget *native = new Widget(&helper);
QLabel *nativeLabel = new QLabel(tr("Native Qt Widgets (Other Six)"));
nativeLabel->setAlignment(Qt::AlignHCenter);
CircleBarWindow *barWindow = new CircleBarWindow;
QLabel *radius = new QLabel(tr("Radius"));
QLabel *value = new QLabel(tr("0"));
QLabel *pixel = new QLabel(tr("pixels"));
originalRenderArea = new RenderArea;
shapeComboBox = new QComboBox;
shapeComboBox->addItem(tr("Clock"));
shapeComboBox->addItem(tr("House"));
shapeComboBox->addItem(tr("Text"));
shapeComboBox->addItem(tr("Truck"));
QGridLayout *layout = new QGridLayout(this);
layout->addWidget(flag, 0, 0);
layout->addWidget(flagLabel, 1, 0 );
layout->addWidget(openGL, 0, 1);
layout->addWidget(openGLLabel, 1, 1);
layout->addWidget(native, 0, 2);
layout->addWidget(nativeLabel, 1, 2);
layout->addWidget(barWindow, 0, 3 );
layout->addWidget(radius, 1, 3, 1, 1, Qt::AlignLeft);
layout->addWidget(value, 1, 3, 1, 1, Qt::AlignHCenter);
layout->addWidget(pixel, 1, 3, 1, 1, Qt::AlignRight);
layout->addWidget(originalRenderArea, 2, 0, 1, 1, Qt::AlignHCenter);
layout->addWidget(shapeComboBox, 3, 0, 1, 1, Qt::AlignHCenter);
for (int i = 0; i < NumTransformedAreas; ++i)
{
transformedRenderAreas[i] = new RenderArea;
operationComboBoxes[i] = new QComboBox;
operationComboBoxes[i]->addItem(tr("No transformation"));
operationComboBoxes[i]->addItem(tr("Rotate by 60\xB0"));
operationComboBoxes[i]->addItem(tr("Scale to 75%"));
operationComboBoxes[i]->addItem(tr("Translate by (50, 50)"));
connect(operationComboBoxes[i], SIGNAL(activated(int)),
this, SLOT(operationChanged()));
layout->addWidget(transformedRenderAreas[i], 2, i + 1);
layout->addWidget(operationComboBoxes[i], 3, i + 1);
}
setLayout(layout);
setupShapes();
shapeSelected(0);
setWindowTitle(tr("Transformations, 2D Painting on Native and OpenGL Widgets"));
setFixedSize(1100, 540);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), native, SLOT(animate()));
connect(timer, SIGNAL(timeout()), openGL, SLOT(animate()));
connect( barWindow->getSliderPtr(), SIGNAL(valueChanged(int)), value,SLOT(setNum(int)));
timer->start(50);
}
void Window::setupShapes()
{
QPainterPath truck;
truck.setFillRule(Qt::WindingFill);
truck.moveTo(0.0, 87.0);
truck.lineTo(0.0, 60.0);
truck.lineTo(10.0, 60.0);
truck.lineTo(35.0, 35.0);
truck.lineTo(100.0, 35.0);
truck.lineTo(100.0, 87.0);
truck.lineTo(0.0, 87.0);
truck.moveTo(17.0, 60.0);
truck.lineTo(55.0, 60.0);
truck.lineTo(55.0, 40.0);
truck.lineTo(37.0, 40.0);
truck.lineTo(17.0, 60.0);
truck.addEllipse(17.0, 75.0, 25.0, 25.0);
truck.addEllipse(63.0, 75.0, 25.0, 25.0);
QPainterPath clock;
clock.addEllipse(-50.0, -50.0, 100.0, 100.0);
clock.addEllipse(-48.0, -48.0, 96.0, 96.0);
clock.moveTo(0.0, 0.0);
clock.lineTo(-2.0, -2.0);
clock.lineTo(0.0, -42.0);
clock.lineTo(2.0, -2.0);
clock.lineTo(0.0, 0.0);
clock.moveTo(0.0, 0.0);
clock.lineTo(2.732, -0.732);
clock.lineTo(24.495, 14.142);
clock.lineTo(0.732, 2.732);
clock.lineTo(0.0, 0.0);
QPainterPath house;
house.moveTo(-45.0, -20.0);
house.lineTo(0.0, -45.0);
house.lineTo(45.0, -20.0);
house.lineTo(45.0, 45.0);
house.lineTo(-45.0, 45.0);
house.lineTo(-45.0, -20.0);
house.addRect(15.0, 5.0, 20.0, 35.0);
house.addRect(-35.0, -15.0, 25.0, 25.0);
QPainterPath text;
QFont font;
font.setPixelSize(50);
QRect fontBoundingRect = QFontMetrics(font).boundingRect(tr("Qt"));
text.addText(-QPointF(fontBoundingRect.center()), font, tr("Qt"));
shapes.append(clock);
shapes.append(house);
shapes.append(text);
shapes.append(truck);
connect(shapeComboBox, SIGNAL(activated(int)),
this, SLOT(shapeSelected(int)));
}
void Window::operationChanged()
{
static const Operation operationTable[] =
{
NoTransformation, Rotate, Scale, Translate
};
QList<Operation> operations;
for (int i = 0; i < NumTransformedAreas; ++i)
{
int index = operationComboBoxes[i]->currentIndex();
operations.append(operationTable[index]);
transformedRenderAreas[i]->setOperations(operations);
}
}
void Window::shapeSelected(int index)
{
QPainterPath shape = shapes[index];
originalRenderArea->setShape(shape);
for (int i = 0; i < NumTransformedAreas; ++i)
transformedRenderAreas[i]->setShape(shape);
}
Now that everything falls into its position, it is trivial to write the main.cpp:
#include <QApplication>
#include "window.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}