28.10.2014 Views

CSCI 102

CSCI 102

CSCI 102

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!