Lab 4 - Qt + OpenGL

  1. Qt vs. GLUT
  2. Qt Widgets
  3. Qt Layouts
  4. Programming task

1. Qt vs. GLUT

There are a lot of equivalencies between the two APIs, summarized below.

GLUT Qt
glutDisplayFunc callback override paintGL()
init before main loop override initializeGL()
glutReshapeFunc callback override resizeGL()
glutMouseFunc callback override mousePressEvent etc.
glutKeyboardFunc callback override keyPressEvent()
glutPostRedisplay() updateGL()
glutIdleFunc callback use QTimer
glutMainLoop() QApplication::exec()


2. Qt Widgets

Widgets are Qt terminology for GUI elements, such as buttons, sliders, menus, and so on. They are quite easy to add to your application and provide functionality to your user.

First, grab the barebones application. It has been updated since the first lab, so I suggest you refresh your files to this version. (The new version removed some unnecessary junk.)

Let's consider a small example of adding a drop-down list for selecting 1-of-n options. In Qt, the widget for drop-down lists is oddly named the QComboBox. Take a look at the documentation in that link, and you'll find out all you want to know about creating and adding items to the combo-box.

 // Add to window.h, before main layout activation
 QComboBox* dropList = new QComboBox(false, this);
 dropList->insertItem("Item 1");
 dropList->insertItem("Item 3");
 dropList->insertItem("Item 2");
 mainLayout->addWidget(dropList);

Questions to think about:

  • What are the parameters passed to the QComboBox constructor? What does the false specify?
  • How does the placement of the code affect the location of the widget in the GUI? How could you move it?
  • What other options are available for configuring the combo-box?

Okay, so now we have a drop-down list in our GUI, either above or below the GL widget. How do we get events or otherwise interact with it?

Return to the QComboBox documentation. At the top are Public Members, but if you scroll down a little bit you'll find Public Slots and Signals. Recall that these are equivalent to event handlers and events in Qt parlance. Take a look at the names of these signals and slots, and click through to the full documentation if you wish.

Let's focus on the activated() signal. There are two varieties, one taking an int and the other taking a QString. Either way, This signal is emitted when a new item has been activated (selected), and the parameter indicates the selected item. Let's use the QString version.

The way that signals and slots work is much like a callback in GLUT. In GLUT, the callback-register function (eg. glutDisplayFunc) takes a pointer to a function with certain parameters -- the form of the callback is defined by the argument to the registration function.

In Qt, the form of the callback function (the slot) is defined by the form of the signal. In particular, any slot must have the same parameters as the signal you want to connect it to. Therefore, if we want to register a slot to the activate(const QString&) signal, our slot should take 1 QString& parameter only.

Let's add a slot to the Renderer class for handling these activate signals. As a simple example, consider:

 // In renderer.h, under public slots:
 void itemSelected(const QString&);

and

 // In renderer.cpp
 void Renderer::itemSelected(const QString& item)
 {
 	if (item == "Item 1")
 		cout << "Item 1 selected\n";
 	else if (item == "Item 2")
 		cout << "Item 2 selected\n";
 	else if (item == "Item 3")
 		cout << "Item 3 selected\n";
 }

If you try to compile the program at this point, it should compile. However, the slot will never execute because it hasn't been registered yet. To do this, you use the QObject::connect(...) function (link).

 // In window.cpp
 QObject::connect(dropList, SIGNAL(activated(const QString &)), 
 	glwidget, SLOT(itemSelected(const QString &)));

If you compile and run the program now, you should see console output when selecting items from the drop-down box.

Common Qt widgets


3. Qt Layouts

To create a nice-looking GUI, you'll want to do more than simply instantiating some widgets and seeing where they end up getting placed. That is, you'll want to have widgets aligned and grouped in a particular way, and perhaps even you'll want to specify resizing behavior.

There are several classes in Qt designed to help you arrange widgets. The main ones are listed below, along with a figure illustrating their layout behavior.

QHBox/QHBoxLayout
QVBox/QVBoxLayout
QGrid/QGridLayout
QGroupBox


Layouts can be (and frequently are) nested within each other.

Question:

  • What is the main difference between classes that end with Layout and those that don't?

Example usage

Let's look at an example of how to use layouts.

 // Will lay out widgets in 2 columns, from left to right, 
 // making a new row when last row is full
 // To lay out widgets from top to bottom, use Qt::Vertical
 QGrid *mainGrid = new QGrid(2, Qt::Horizontal); 
 new QLabel( "One", mainGrid );
 new QLabel( "Two", mainGrid );
 new QLabel( "Three", mainGrid );


In the barebones application, there are already a couple of layout managers in use. Take a look at the code in window.cpp and think about the structure created by the layouts. Then consider the following programming task.

Programming task

Download the barebones application and then:

  • Replace the colored triangle rendering with a wireframe cube.
  • Add a vertical slider to the right of the GL widget, and make the slider control the size of the cube's axis.
  • Add a check-box above the GL widget that toggles the cube rendering on and off.

Further Reading: