Instructions for simple OpenGL application with QT 4.
Introduction
The QT libraries are a set of cross platform Graphical User Interface (GUI) libraries. They are available to create programs on UNIX/Linux, Windows, Mac, and for some imbedded systems such as for some phones and PDAs. Developed by Trolltech which is a company from Norway, they are available to use under several licensing agreements which can be generalized as: The libraries are free to use for free software and need to be licensed for commercial software. The version installed in the labs for use under the free software licensing. The good and bad thing (depending on your perspective) is that the free version is not integrated into the Visual Studio environment. You can still use Visual Studio for editing source code but the tools to compile and link the program are command line based. This is not a major problem as the tools do all the nasty bits for you.
Getting QT and about the code here
To get QT, go over to Trolltech and download it. If you are going to be using Windows as the development platform, you want to start with the Free Software licensed version for Windows + GCC. This is available to learn with and create free software but not for commercial use. If you are going to be making commercial software, you need to purchase a license from Trolltech. The licensing is quite reasonable for this type of library and getting the commercial version allows you to do other things such as use it within Visual Studio.The code below is a downsized version of the Hello OpengGL program. I cut out the call list and extrusion stuff to make it simpler to understand. As written here, it will not easily compile if you copy and paste it into an editor. That's ok, just get the code from the examples which are installed when you download QT. This is just for an explanation. There are several OpenGL and many other examples included in the downloaded package. There are also some tutorials included.
Programming Philosophy
The libraries use a superset of C++. This means that to program these libraries, the basic stuff of C++ is used but the authors of the libraries created a bit of extra capability which is converted to C++ by a pre-processor. You really do not have to do that much with the process but you will see a few extra files and output so don’t be alarmed. This extra bit of code magic is what makes the libraries nice to use. The whole concept of correlating the different events that can happen (moving a mouse, clicking a button, moving a scroll bar, and so forth) to event handlers which reflect these events gets simplified from the more traditional method of processing the event loop. Also, the creation of container widgets simplifies the layout of your graphical application.
Getting started with an application
The first thing you do to create a project for a graphical application is to create the main.cpp. All this does is to create an instance of the application and pass the command line parameters for your program along to the graphical application. The code is generic to any program you might create this way. (The code here is from the HelloGL example from the distribution of QT but stripped down to be a little more clear.)
// The basic application include file
#include <QApplication>
// The include file for your implementation of a window
#include "window.h"
int main(int argc, char *argv[])
{
// Constructor for the application and initialize it with
//the argument vector
QApplication app(argc, argv);
// Create an instance of your main window
Window window;
// Set it to be displayed
window.show();
// Return the return value from your application when
// it is finished
return app.exec();
}
The main window code will be divided into a header and a source file.
The Header file:
// Set it to only include once
#ifndef WINDOW_H
#define WINDOW_H
// A simple object for the window is just a generic widget
#include <QWidget>
// Class pre-declarations just to speed up compilation
class QSlider;
class GLWidget;
// Create a class for the window based on the generic QWidget
class Window : public QWidget{
// A bit of magic – Just put it here.
Q_OBJECT
public:
// Default constructor
Window();
private:
// Part of the magic. This connects the event (changedSignal)
// to the handler (setterSlot)
QSlider *createSlider(const char *changedSignal,
const char *setterSlot);
// A gl Widget for the main object
GLWidget *glWidget;
// Three sliders to create manipulate the object.
QSlider *xSlider;
QSlider *ySlider;
QSlider *zSlider;
};
#endif
The implementation file (window.cpp) for the window class:
#include <QtGui>Now to create the actual glWidget. It too needs two files, the header and the implementation. First the header (glwidget.h):
// Need to include glwidget (the one we will create next)
#include "glwidget.h"
// Include the header for this window
#include "window.h"
// The constructor for this window
Window::Window()
{
// Add the OpenGL widget
glWidget = new GLWidget;
// Create three sliders to control the X, Y, and Z rotation
// and connect their changed event (SIGNAL)
// to the proper handler function (SLOT)
xSlider = createSlider(SIGNAL(xRotationChanged(int)),
SLOT(setXRotation(int)));
ySlider = createSlider(SIGNAL(yRotationChanged(int)),
SLOT(setYRotation(int)));
zSlider = createSlider(SIGNAL(zRotationChanged(int)),
SLOT(setZRotation(int)));
// Create a horizontal layout box
// (container for all the widgets)
QHBoxLayout *mainLayout = new QHBoxLayout;
// Add each of the widgets
mainLayout->addWidget(glWidget);
mainLayout->addWidget(xSlider);
mainLayout->addWidget(ySlider);
mainLayout->addWidget(zSlider);
// Set the layout box as the main object
setLayout(mainLayout);
// And give some initial values
xSlider->setValue(15 * 16);
ySlider->setValue(345 * 16);
zSlider->setValue(0 * 16);
setWindowTitle(tr("Hello GL"));
}
//Function that creates the sliders
QSlider *Window::createSlider(const char *changedSignal,
const char *setterSlot)
{
QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(0, 360 * 16);
slider->setSingleStep(16);
slider->setPageStep(15 * 16);
slider->setTickInterval(15 * 16);
slider->setTickPosition(QSlider::TicksRight);
connect(slider, SIGNAL(valueChanged(int)),
glWidget, setterSlot);
connect(glWidget, changedSignal, slider,
SLOT(setValue(int)));
return slider;
}
// Only include onceAnd now the fun stuff, the implementation (glwidget.cpp):
#ifndef GLWIDGET_H
#define GLWIDGET_H
// base class for the GLWidget
#include <QGLWidget>
class GLWidget : public QGLWidget
{
// Magic stuff here
Q_OBJECT
public:
// Constructor and destructor
GLWidget(QWidget *parent = 0);
~GLWidget();
//Basic setup
QSize minimumSizeHint() const;
QSize sizeHint() const;
// The holders for the values for the rotation
int xRotation() const { return xRot; }
int yRotation() const { return yRot; }
int zRotation() const { return zRot; }
// Not really C++ but these are the handler functions
// for the events
public slots:
void setXRotation(int angle);
void setYRotation(int angle);
void setZRotation(int angle);
// And these are the messages that connect to the previous
// handlers
signals:
void xRotationChanged(int angle);
void yRotationChanged(int angle);
void zRotationChanged(int angle);
protected:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void normalizeAngle(int *angle);
GLuint object;
int xRot;
int yRot;
int zRot;
QPoint lastPos;
};
#endif
#include <QtGui>
#include <QtOpenGL>
#include <math.h>
#include "glwidget.h"
GLWidget::GLWidget(QWidget *parent)
: QGLWidget(parent)
{
// Start off at no rotation
xRot = 0;
yRot = 0;
zRot = 0;
}
GLWidget::~GLWidget()
{
// Delete anything you have created
}
QSize GLWidget::minimumSizeHint() const
{
return QSize(50, 50);
}
QSize GLWidget::sizeHint() const
{
return QSize(400, 400);
}
void GLWidget::setXRotation(int angle)
{
normalizeAngle(&angle);
if (angle != xRot) {
xRot = angle;
emit xRotationChanged(angle);
updateGL();
}
}
void GLWidget::setYRotation(int angle)
{
normalizeAngle(&angle);
if (angle != yRot) {
yRot = angle;
emit yRotationChanged(angle);
updateGL();
}
}
void GLWidget::setZRotation(int angle)
{
normalizeAngle(&angle);
if (angle != zRot) {
zRot = angle;
// tell everyone we have changed the angle
emit zRotationChanged(angle);
updateGL();
}
}
void GLWidget::initializeGL()
{
// Set the way we want things to look
glShadeModel(GL_FLAT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslated(0.0, 0.0, -10.0);
glRotated(xRot / 16.0, 1.0, 0.0, 0.0);
glRotated(yRot / 16.0, 0.0, 1.0, 0.0);
glRotated(zRot / 16.0, 0.0, 0.0, 1.0);
// A basic square to impress all your friends
glBegin(GL_LINE_LOOP);
glVertex3d(0,0,0);
glVertex3d(1,0,0);
glVertex3d(1,1,0);
glVertex3d(0,1,0);
glEnd();
}
void GLWidget::resizeGL(int width, int height)
{
int side = qMin(width, height);
glViewport((width - side) / 2, (height - side) / 2, side, side);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.5, +1.5, +1.5, -1.5, 4.0, 15.0);
glMatrixMode(GL_MODELVIEW);
}
void GLWidget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if (event->buttons() & Qt::LeftButton) {
setXRotation(xRot + 8 * dy);
setYRotation(yRot + 8 * dx);
} else if (event->buttons() & Qt::RightButton) {
setXRotation(xRot + 8 * dy);
setZRotation(zRot + 8 * dx);
}
lastPos = event->pos();
}
void GLWidget::normalizeAngle(int *angle)
{
while (*angle < 0)
*angle += 360 * 16;
while (*angle > 360 * 16)
*angle -= 360 * 16;
}
Now, you have the files you need: main.cpp, window.h, window.cpp, glwidget.h, glwidget.cpp. Again, the main function only starts up the instance of your application. The window is the class that defines the window and the placements of the widgets. The glwidget is an OpenGL widget that gets placed in the window object.
Building the Application
This is a bit old fashioned but you will need to use the command prompt to build the project. Integration into Visual Studio is not available on the free version of QT. The QT 4.0.1 Command Prompt (The version installed in the lab) is on the start menu under the QT by Trolltech group. It is just a plain command prompt but it has all the environment variables telling where the compiler and include files and such are. Just change directory to the directory where your source files are and begin to build your application.
There are three things you need to do to create the project and build it. First , you need to run the qmake program in project mode to have it create the project file. Run qmake with the –project option:
qmake –projectNext, you need to edit the project file that qmake created. By default, it made the .pro file with the name of the directory (myproject.pro for example). You need to tell the qmake that you wanted to use the OpenGL libraries by adding QT += opengl to the project file:
##################################################################
# Automatically generated by qmake (2.00a) Thu Jun 8 14:56:11 2006
##################################################################
TEMPLATE = app
TARGET +=
DEPENDPATH += .
INCLUDEPATH += .
QT += opengl
# Input
HEADERS += glwidget.h window.h
SOURCES += glwidget.cpp main.cpp window.cpp
Now you need to run qmake again but this time you give it the name of the project file which will cause it to create the make files to build the project:
qmake myproject.pro
When this runs, it will build all the make files to build the project. To create the executable, you just need to type:
make
and it will call the compiler and build the executable in a directory named release. You can later get set up to do debug mode but you are on your own there, this is just to get you started. The full documentation for QT is included with the installation and they have many tutorials and example programs to help you get going. If you want to install QT on your own computer to do the programs, you can download it from http://www.trolltech.com where you can also find online versions of the documentation. If you want to use it on the Linux platform the process is exactly the same but you need to make sure that you are using a version 4 of QT. The version 3 has a bit different setup and capabilities. On a Mac, there are two different ways you can create the programs. One is through X11 and the other is as a native Mac OS X application.
Differences from GLUT
The programming through QT as opposed to GLUT and GLU (and GLUI) is a bit simpler as you do not have all the parameter issues of defining the callbacks and do not need to have all the variables defined globally. This makes it much easier to deal with. Also, double buffering is the default. The GLUT method of having to define where to find all the callback functions is eliminated and you just have your paintGL for the display function. If you want to set up a timer for animation, look at the window.h and window.cpp in the Textures example to see how that is done. All the examples included in the documentation use call lists (you will find out more about this in class) as opposed to directly drawing in the paintGL function which I have shown here in this simplified example. All the GL functions are available for setting cameras, viewports, and such which are documented in the OpenGL primer and Redbook. GLU and GLUI are not available directly (that I know of- although you might be able to include them independently but I have never tried.)