CSCI 102
CSCI 102
CSCI 102
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
<strong>CSCI</strong> <strong>102</strong><br />
Graphical User Interface Applications in Qt<br />
Mark Redekopp<br />
Michael Crowley
GUI Applications<br />
‣ Are quite different from command-line applications<br />
– Users have far more control over what happens next<br />
– Our code must be “ready” to handle what the user wants<br />
to do next<br />
• Execute a menu<br />
• Button gets clicked<br />
• Text typed into a text field<br />
• And so on<br />
‣ This makes GUI applications Event Based<br />
– An event is started by something with the hardware: keyboard,<br />
mouse, etc.<br />
‣ And complex in how things are “laid out”. We have<br />
menus, buttons, text fields, lists, combo boxes, etc.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
2
GUI and Qt<br />
‣ To ease the pain of laying out a nice looking GUI,<br />
Qt has a very handy class called QMainWindow. It<br />
contains:<br />
– A place for menus<br />
– A tool bar<br />
– A status bar<br />
– 5 locations for more complex widgets<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
3
QMainWindow Layout<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
4
MenuBar in QMainWindow<br />
‣ A QMenuBar already exists in QMainWindow. You<br />
don’t make one.<br />
‣ To get a reference to it (from a class that inherits<br />
QMainWindow, just call the menuBar() method.<br />
– QMenuBar *mb = menuBar();<br />
‣ You add QMenu objects to the menu bar. They<br />
are laid out from left to right.<br />
‣ You add QAction objects to menus. They are laid<br />
out from top to bottom.<br />
‣ You use the connect() method to connect a<br />
QAction to a method to handle the action for when<br />
the menu item is clicked on with the mouse.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
5
Setting Up Menus<br />
‣ The code below gets the QMenuBar object from a class<br />
that inherits from QMainWindow<br />
‣ It creates one menu – a ‘file’ menu<br />
‣ It adds one action to that menu – an ‘exit’ action<br />
‣ It connect the exit action to the slot function exitFunction().<br />
We must implement this function.<br />
‣ For each action in a menu, you repeat the last 3 lines.<br />
QMenuBar *mb = menuBar();<br />
fileMenu = new QMenu("File“, this);<br />
mb->addMenu(fileMenu);<br />
QAction *exitAction = new QAction( "Exit", this );<br />
fileMenu->addAction( exitAction );<br />
connect(exitAction, SIGNAL(triggered()), this, SLOT(exitFunction()));<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
6
Toolbars with QMainWindow<br />
‣ Unlike the menu bar section of a QMainWindow, Qt<br />
does not automatically make a QToolBar object.<br />
‣ Tool bars are essentially a place for a set of buttons<br />
‣ They are laid out horizontally.<br />
‣ Because tool bars are meant to be a place for<br />
buttons, you don’t have to create button objects.<br />
You create QAction objects directly and add them to<br />
the toolbar.<br />
‣ It’s like the menu bar, but you don’t have to make<br />
QMenu objects.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
7
Good GUI Program Organization<br />
‣ Up to 80% of code in a GUI program is just for<br />
creation of GUI objects, laying them out, and<br />
getting them displayed so they look nice.<br />
‣ For each UI component, you must create it, add it<br />
to a layout, possibly add an action, and connect<br />
the action to a method.<br />
‣ That’s up to 4 lines of code for a single UI<br />
component – like a button.<br />
‣ In addition, different parts of our application screen<br />
need to be laid out differently – some horizontally,<br />
some vertically, etc.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
8
Good GUI Program Organization<br />
‣ The best way to build any GUI application, is to<br />
have each different part of the UI window be a<br />
separate class.<br />
‣ Each class has its own layout, it also handles any<br />
signals/slots that it contains.<br />
‣ It has a pointer to the class that created it so it can<br />
pass responsibility for some tasks to the “boss”<br />
class.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
9
Proper Toolbar with Qt<br />
‣ Create a new class that inherits from QToolBar<br />
‣ Put your actions in this class<br />
‣ Put the connect statements for each action in this<br />
class<br />
‣ Put the methods for each connect statement in<br />
this class<br />
‣ For PA4, you need 3 buttons – start game, quit<br />
game, run A*. That can be 3 QAction objects with<br />
3 connect statements, and 3 methods for handling<br />
the actions.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
10
QToolBar with QMainWindow<br />
‣ In the class that inherits from QToolBar<br />
– Create one QAction object for each “button”<br />
– Add each QAction to this class with addAction<br />
– Have one connect statement for each QAction<br />
QAction *startGameAction = new QAction( "Start Game", this );<br />
addAction( startGameAction );<br />
connect( startGameAction, SIGNAL( triggered() ),<br />
this, SLOT( startGame() )));<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
11
Getting Started on PA4<br />
‣ If you haven’t started. Start with the code we gave<br />
you for lab 9.<br />
– Change MainWindow class to another class name.<br />
Have it inherit from QGraphicsView, not QWidget<br />
‣ Create a new MainWindow class that inherits from<br />
QMainWindow<br />
‣ Create an instance of your QGraphicsView class<br />
in the MainWindow constructor and put it into the<br />
Central Widget with the setCentralWidget method<br />
‣ Compile and run your code. You should see the<br />
bouncing rectangle in the middle of the window.<br />
You may have to resize the window to see it.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
12
PA4 Step 3<br />
‣ Create a new class that inherits from QToolBar<br />
‣ Create the 3 actions that you need and connect each of<br />
them to a method to handle the action<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
13
QMainWindow Central Widget<br />
‣ This is to be the main screen in a Qt GUI application<br />
‣ For PA4, this is going to be your ‘graphics’ window,<br />
where your puzzle game will be displayed and users<br />
will ‘play’ it.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
14
QDockWidgets<br />
‣ In the QMainWindow layout scheme, there are 4<br />
areas that immediately surround the Central<br />
Widget area. These are QDockWidgets.<br />
‣ QDockWidgets must be assigned to one of the<br />
four locations<br />
– Qt::TopDockWidgetArea, Qt::BottomDockWidgetArea,<br />
Qt::LeftDockWidgetArea, Qt::RightDockWidgetArea<br />
‣ QDockWidgets have their own layout, that you<br />
cannot change. You must create a separate class<br />
that inherits from QWidget that you add to a<br />
QDockWidget object to be able to lay out your UI<br />
components in the way you want.<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
15
PA4 Example - QLineEdits<br />
‣ In PA4, you must have three text fields to replace<br />
the three command line arguments from PA3. The<br />
QLineEdit class is handy for single-line user input.<br />
‣ QLineEdit text fields need a prompt to tell the user<br />
what the text field is for. You cannot assume the<br />
user knows what a given text field is for<br />
‣ Qt provides a very nice layout for QLineEdit<br />
objects - QFormLayout<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
16
Using QFormLayout<br />
‣ Like any other “screen” you will want to create a<br />
new class that inherits from QWidget<br />
‣ In the constructor, create a QFormLayout object (it<br />
needs to be an instance variable of the class)<br />
‣ Create each of the QLineEdit objects<br />
‣ For each “row” you use the addRow method of the<br />
layout<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
17
Show me the Code!<br />
layout = new QFormLayout();<br />
setLayout( layout );<br />
sizeEdit = new QLineEdit();<br />
startMovesEdit = new QLineEdit();<br />
randomSeedEdit = new QLineEdit();<br />
layout->addRow( “Board Size:", sizeEdit );<br />
layout->addRow( “Starting Moves:"), startMovesEdit );<br />
layout->addRow( “Random Seed Value:"), randomSeedEdit );<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
18
Adding my QWidget to the Dock<br />
‣ The last step is to create an instance of our new<br />
class and add it to the QDockWidget that we want.<br />
‣ In the example below, this variable is uiWindow.<br />
‣ We do this in our class that inherits from<br />
QMainWindow<br />
‣ The setFeatures method turns off the user’s ability<br />
to close our dock widget, or make it a separate<br />
window.<br />
QDockWidget *qdw = new QDockWidget();<br />
qdw->setWidget( uiWindow );<br />
addDockWidget(Qt::LeftDockWidgetArea, qdw );<br />
qdw->setFeatures(QDockWidget::NoDockWidgetFeatures);<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
19
PA4 Step 1 – From Lab 9<br />
‣ Rename MainWindow from Lab 9 to a new class<br />
that inherits from QGraphicsView – instead of<br />
QWidget. Do not create a QGraphicsView in this<br />
class – since it is one.<br />
‣ Create an instance of this class in your new<br />
MainWindow class that inherits from<br />
QMainWindow<br />
‣ Add this graphic window object to the Central<br />
Widget area in your new MainWindow class with<br />
the setCentralWidget method<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
20
PA4 Step 2<br />
‣ Replace the bouncing rectangle code in your graphic<br />
window class with code to create a solved puzzle<br />
– Don’t worry about a 9-tile, or a 16-tile puzzle, yet. Just ensure you<br />
can create a puzzle and have it displayed in the central widget<br />
‣ Integrate the code to scramble the puzzle from PA3. This<br />
requires moving the tiles to the proper locations<br />
– Compile and run the code. Make sure you graphically moved the<br />
tiles correctly.<br />
‣ Now you are ready to add the mousePressEvent method to<br />
your GUITile class so that the user can play the game<br />
using the mouse<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
21
PA4 Step 2 Continued<br />
‣ The mousePressEvent method is in your GUITile<br />
class.<br />
‣ The GUITile class knows only about a single<br />
GUITile<br />
‣ It doesn’t know if a tile can be moved, or not<br />
‣ The responsibility for “knowing” if a move is valid<br />
is in your graphic window class<br />
‣ This means the GUITile class must pass the<br />
responsibility for handling a mouse click, on a<br />
GUITile, to the graphic window class<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
22
PA4 Step 2 Continued<br />
‣ For the mousePressEvent method in the GUITile<br />
class to call a method of the graphic window class,<br />
it must have a reference to that class<br />
‣ Pass the reference to graphic window to the<br />
constructor of GUITile<br />
‣ Create an instance variable pointer in GUITile to<br />
the graphic window object. Set the variable in the<br />
GUITile constructor<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
23
Show me the Code!<br />
‣ Let’s say my graphic window class is called<br />
GraphicWindow. My variable in GUITile is called<br />
gWindow<br />
‣ In this class, I have a method moveTile that knows<br />
how/when to move a tile. This must be called from<br />
the mousePressEvent method in GUITile to get a<br />
tile moved, or not.<br />
void GUITile::mousePressEvent ( QGraphicsSceneMouseEvent* event ) {<br />
}<br />
gWindow->moveTile( this );<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
24
PA4 – And Beyond<br />
‣ Create one class at a time for some of the UI<br />
components. Compile and test each class as you<br />
create them.<br />
‣ Consider the following class organization:<br />
– One class for the 3 QLineEdit objects – use<br />
QFormLayout<br />
– One class for the QToolBar – the 3 “buttons” you need<br />
– One class for the 2 QRadioButton objects – use<br />
QHBoxLayout, or QVBoxLayout, depending on which<br />
dock widget you use<br />
– One class for the QListWidget (holds A* results)<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
25
OTHER NOTES<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
26
Layouts<br />
‣ We've seen different layouts<br />
– QVBoxLayout<br />
– QHBoxLayout<br />
– QFormLayout<br />
‣ Each widget (or derived class) can have only one Layout<br />
– Set by calling: widget->setLayout(pointer to the layout) method<br />
‣ But a layout may contain either widgets or OTHER<br />
LAYOUTS in each of its entries<br />
– Set by calling: layout->addLayout(pointer to child layout)<br />
– Set by calling: layout->addWidget(pointer to the child widget)<br />
‣ So for each widget think about whether you want to add<br />
items vertically or horizontally and pick a Vbox or Hbox<br />
Layout and then add child layouts within that context<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
27
More Notes<br />
‣ Widgets have a virtual function sizeHint()<br />
– Qsize sizeHint() const;<br />
– If you want your widget to start at a particular size, add this to your<br />
class and simply have it return a Qsize object which is just pixel<br />
rows x columns<br />
– Qsize MYCLASS::sizeHint() const { return QSize(400, 400); }<br />
‣ Defining your own signals<br />
– Signals go in the "signals:" section of your class<br />
– They are just prototypes (you don't write an implementation)<br />
– Use the 'emit' keyword followed by a "call" to this signal function<br />
– Whatever slot has been connected to this signal will in turn be<br />
called<br />
‣ Events are not slots (think of them as "slots" that are preconnected<br />
to certain actions/signals)<br />
– Just override them and usually call the BaseClass version<br />
© Copyright 2013 Brent Nash & Mark Redekopp, All Rights Reserved<br />
28