|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Tutorial
|
|
|
|
**
|
|
|
|
** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
|
|
|
|
**
|
|
|
|
** This file is part of the Qt GUI Toolkit.
|
|
|
|
**
|
|
|
|
** This file may be used under the terms of the GNU General
|
|
|
|
** Public License versions 2.0 or 3.0 as published by the Free
|
|
|
|
** Software Foundation and appearing in the files LICENSE.GPL2
|
|
|
|
** and LICENSE.GPL3 included in the packaging of this file.
|
|
|
|
** Alternatively you may (at your option) use any later version
|
|
|
|
** of the GNU General Public License if such license has been
|
|
|
|
** publicly approved by Trolltech ASA (or its successors, if any)
|
|
|
|
** and the KDE Free Qt Foundation.
|
|
|
|
**
|
|
|
|
** Please review the following information to ensure GNU General
|
|
|
|
** Public Licensing retquirements will be met:
|
|
|
|
** http://trolltech.com/products/qt/licenses/licensing/opensource/.
|
|
|
|
** If you are unsure which license is appropriate for your use, please
|
|
|
|
** review the following information:
|
|
|
|
** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
|
|
|
|
** or contact the sales department at sales@trolltech.com.
|
|
|
|
**
|
|
|
|
** This file may be used under the terms of the Q Public License as
|
|
|
|
** defined by Trolltech ASA and appearing in the file LICENSE.QPL
|
|
|
|
** included in the packaging of this file. Licensees holding valid Qt
|
|
|
|
** Commercial licenses may use this file in accordance with the Qt
|
|
|
|
** Commercial License Agreement provided with the Software.
|
|
|
|
**
|
|
|
|
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
|
|
|
|
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
|
|
|
|
** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
|
|
|
|
** herein.
|
|
|
|
**
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
|
|
/*! \page tutorial.html
|
|
|
|
|
|
|
|
\title Qt Tutorial #1 - The 14 Steps
|
|
|
|
|
|
|
|
This tutorial gives an introduction to GUI programming using the Qt
|
|
|
|
toolkit. It doesn't cover everything: the emphasis is on teaching the
|
|
|
|
programming philosophy of GUI programming, and Qt's features are
|
|
|
|
introduced as needed. Some commonly used features are never used in
|
|
|
|
this tutorial.
|
|
|
|
|
|
|
|
Chapter one starts with a ten-line hello-world and each subsequent
|
|
|
|
chapter introduces one or a few more concepts. By Chapter 14, the ten
|
|
|
|
lines from Chapter 1 have turned into a 650-line game.
|
|
|
|
|
|
|
|
If you're completely new to Qt, please read \link how-to-learn-qt.html
|
|
|
|
How to Learn Qt\endlink if you haven't already done so.
|
|
|
|
|
|
|
|
Tutorial chapters:
|
|
|
|
\list 1
|
|
|
|
\i \link tutorial1-01.html Hello, World!\endlink
|
|
|
|
\i \link tutorial1-02.html Calling it Quits\endlink
|
|
|
|
\i \link tutorial1-03.html Family Values\endlink
|
|
|
|
\i \link tutorial1-04.html Let There Be Widgets\endlink
|
|
|
|
\i \link tutorial1-05.html Building Blocks\endlink
|
|
|
|
\i \link tutorial1-06.html Building Blocks Galore!\endlink
|
|
|
|
\i \link tutorial1-07.html One Thing Leads to Another\endlink
|
|
|
|
\i \link tutorial1-08.html Preparing for Battle\endlink
|
|
|
|
\i \link tutorial1-09.html With Cannon You Can\endlink
|
|
|
|
\i \link tutorial1-10.html Smooth as Silk\endlink
|
|
|
|
\i \link tutorial1-11.html Giving It a Shot\endlink
|
|
|
|
\i \link tutorial1-12.html Hanging in the Air the Way Bricks Don't\endlink
|
|
|
|
\i \link tutorial1-13.html Game Over\endlink
|
|
|
|
\i \link tutorial1-14.html Facing the Wall\endlink
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
This little game doesn't look much like a modern GUI application. It
|
|
|
|
uses a good few of the GUI techniques, but after you've worked through
|
|
|
|
it, we recommend reading \link tutorial2.html Tutorial #2\endlink. The
|
|
|
|
second tutorial is a little more formal and covers the features of
|
|
|
|
typical application including menubars, toolbars, loading and saving,
|
|
|
|
dialogs, etc.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \page tutorial1-01.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 1: Hello, World!
|
|
|
|
|
|
|
|
\img t1.png Screenshot of tutorial one
|
|
|
|
|
|
|
|
This first program is a simple hello-world example. It contains only
|
|
|
|
the bare minimum you need to get a Qt application up and running.
|
|
|
|
The picture above is a snapshot of this program.
|
|
|
|
|
|
|
|
\include t1/main.cpp
|
|
|
|
\quotefile t1/main.cpp
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\skipto include
|
|
|
|
\printline qapp
|
|
|
|
|
|
|
|
This line includes the QApplication class definition. There has to be
|
|
|
|
exactly one QApplication object in every application that uses Qt.
|
|
|
|
QApplication manages various application-wide resources, such as the
|
|
|
|
default font and cursor.
|
|
|
|
|
|
|
|
\printline qpushbutton
|
|
|
|
|
|
|
|
This line includes the QPushButton class definition. The
|
|
|
|
\link hierarchy.html reference documentation \endlink for each class
|
|
|
|
mentions at the top which file needs to be included to use that class.
|
|
|
|
|
|
|
|
QPushButton is a classical GUI push button that the user can press
|
|
|
|
and release. It manages its own look and feel, like every other \l
|
|
|
|
QWidget. A widget is a user interface object that can process user
|
|
|
|
input and draw graphics. The programmer can change both the overall
|
|
|
|
\link QApplication::setStyle() look and feel\endlink and many minor
|
|
|
|
properties of it (such as color), as well as the widget's content. A
|
|
|
|
QPushButton can show either a text or a \l QPixmap.
|
|
|
|
|
|
|
|
\printline main
|
|
|
|
\printline {
|
|
|
|
|
|
|
|
The main() function is the entry point to the program. Almost always
|
|
|
|
when using Qt, main() only needs to perform some kind of initialization
|
|
|
|
before passing the control to the Qt library, which then tells the
|
|
|
|
program about the user's actions via events.
|
|
|
|
|
|
|
|
\c argc is the number of command-line arguments and \c argv is the
|
|
|
|
array of command-line arguments. This is a C/C++ feature. It is not
|
|
|
|
specific to Qt; however, Qt needs to process these arguments (see
|
|
|
|
following).
|
|
|
|
|
|
|
|
\printline QApplication
|
|
|
|
|
|
|
|
\c a is this program's QApplication. Here it is created and processes
|
|
|
|
some of the command-line arguments (such as -display under X Window).
|
|
|
|
Note that all command-line arguments recognized by Qt are removed from
|
|
|
|
\c argv (and \c argc is decremented accordingly). See the \l
|
|
|
|
QApplication::argv() documentation for details.
|
|
|
|
|
|
|
|
<strong>Note:</strong> It is essential that the QApplication object be
|
|
|
|
created before any window-system parts of Qt are used.
|
|
|
|
|
|
|
|
\printline QPushButton
|
|
|
|
|
|
|
|
Here, \e after the QApplication, comes the first window-system code: A
|
|
|
|
push button is created.
|
|
|
|
|
|
|
|
The button is set up to display the text "Hello world!" and be a
|
|
|
|
window of its own (because the constructor specifies 0 for the parent
|
|
|
|
window, inside which the button should be located).
|
|
|
|
|
|
|
|
\printline resize
|
|
|
|
|
|
|
|
The button is set up to be 100 pixels wide and 30 pixels high (plus the
|
|
|
|
window system frame). In this case we don't care about the button's
|
|
|
|
position, and we accept the default value.
|
|
|
|
|
|
|
|
\printline setMainWidget
|
|
|
|
|
|
|
|
The push button is chosen as the main widget for the application. If
|
|
|
|
the user closes a main widget, the application exits.
|
|
|
|
|
|
|
|
You don't have to have a main widget, but most programs do have one.
|
|
|
|
|
|
|
|
\printline show
|
|
|
|
|
|
|
|
A widget is never visible when you create it. You must call show() to
|
|
|
|
make it visible.
|
|
|
|
|
|
|
|
\printline exec
|
|
|
|
|
|
|
|
This is where main() passes control to Qt, and exec() will return when
|
|
|
|
the application exits.
|
|
|
|
|
|
|
|
In exec(), Qt receives and processes user and system events and passes
|
|
|
|
these on to the appropriate widgets.
|
|
|
|
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
You should now try to compile and run this program.
|
|
|
|
|
|
|
|
\target compiling
|
|
|
|
\section1 Compiling
|
|
|
|
|
|
|
|
To compile a C++ application you need to create a makefile. The
|
|
|
|
easiest way to create a makefile for Qt is to use the \link
|
|
|
|
qmake-manual.book qmake\endlink build tool supplied with Qt. If you've
|
|
|
|
saved \c main.cpp in its own directory, all you have to do is:
|
|
|
|
\code
|
|
|
|
qmake -project
|
|
|
|
qmake
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
The first command tells \link qmake-manual.book qmake\endlink to
|
|
|
|
create a \c .pro (project) file. The second command tells it to create
|
|
|
|
a (platform-specific) makefile based on the project file. You should
|
|
|
|
now be able to type \c make (or \c nmake if you're using Visual
|
|
|
|
Studio) and then run your first Qt application!
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
When you run it, you will see a small window filled with a single
|
|
|
|
button, and on it you can read the famous words, Hello World!
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Try to resize the window. Press the button. If you're running X
|
|
|
|
Window, try running the program with the -geometry option
|
|
|
|
(for example, \c {-geometry 100x200+10+20}).
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-02.html Chapter 2.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-02.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \page tutorial1-02.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 2: Calling it Quits
|
|
|
|
|
|
|
|
\img t2.png Screenshot of tutorial two
|
|
|
|
|
|
|
|
Having created a window in \link tutorial1-01.html Chapter 1, \endlink we will
|
|
|
|
now go on to make the application quit properly when the user tells it to.
|
|
|
|
|
|
|
|
We will also use a font that is more exciting than the default one.
|
|
|
|
|
|
|
|
\include t2/main.cpp
|
|
|
|
\quotefile t2/main.cpp
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\skipto qfont
|
|
|
|
\printline qfont
|
|
|
|
|
|
|
|
Since this program uses QFont, it needs to include qfont.h. Qt's font
|
|
|
|
abstraction is rather different from the horror provided by X, and
|
|
|
|
loading and using fonts has been highly optimized.
|
|
|
|
|
|
|
|
\skipto QPushButton
|
|
|
|
\printline QPushButton
|
|
|
|
|
|
|
|
This time, the button says "Quit" and that's exactly what the program
|
|
|
|
will do when the user clicks the button. This is not a coincidence.
|
|
|
|
We still pass 0 as the parent, since the button is a top-level window.
|
|
|
|
|
|
|
|
\printline resize
|
|
|
|
|
|
|
|
We've chosen another size for the button since the text is a bit
|
|
|
|
shorter than "Hello world!". We could also have used \l QFontMetrics
|
|
|
|
to set right size.
|
|
|
|
|
|
|
|
\printline setFont
|
|
|
|
|
|
|
|
Here we choose a new font for the button, an 18-point bold font from
|
|
|
|
the Times family. Note that we create the font on the spot.
|
|
|
|
|
|
|
|
It is also possible to change the default font (using \l
|
|
|
|
QApplication::setFont()) for the whole application.
|
|
|
|
|
|
|
|
\printline connect
|
|
|
|
|
|
|
|
connect() is perhaps \e the most central feature of Qt.
|
|
|
|
Note that connect() is a static function in QObject. Do not confuse it
|
|
|
|
with the connect() function in the socket library.
|
|
|
|
|
|
|
|
This line establishes a one-way connection between two Qt objects (objects
|
|
|
|
that inherit QObject, directly or indirectly). Every Qt object can have
|
|
|
|
both \c signals (to send messages) and \c slots (to receive messages). All
|
|
|
|
widgets are Qt objects. They inherit QWidget which in turn inherits
|
|
|
|
QObject.
|
|
|
|
|
|
|
|
Here, the \e clicked() signal of \e quit is connected to the \e
|
|
|
|
quit() slot of \e a, so that when the button is clicked, the
|
|
|
|
application quits.
|
|
|
|
|
|
|
|
The \link signalsandslots.html Signals and Slots\endlink documentation
|
|
|
|
describes this topic in detail.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
When you run this program, you will see an even smaller window than in
|
|
|
|
Chapter 1, filled with an even smaller button.
|
|
|
|
|
|
|
|
(See \link tutorial1-01.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Try to resize the window. Press the button. Oops! That connect()
|
|
|
|
would seem to make some difference.
|
|
|
|
|
|
|
|
Are there any other signals in QPushButton you can connect to quit?
|
|
|
|
Hint: The QPushButton inherits most of its behavior from QButton.
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-03.html Chapter 3.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-01.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-03.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \page tutorial1-03.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 3: Family Values
|
|
|
|
|
|
|
|
\img t3.png Screenshot of tutorial three
|
|
|
|
|
|
|
|
This example shows how to create parent and child widgets.
|
|
|
|
|
|
|
|
We'll keep it simple and use just a single parent and a lone child.
|
|
|
|
|
|
|
|
\include t3/main.cpp
|
|
|
|
\quotefile t3/main.cpp
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\skipto qvbox.h
|
|
|
|
\printline qvbox.h
|
|
|
|
|
|
|
|
We add an include of qvbox.h to get the layout class we'll use.
|
|
|
|
|
|
|
|
\skipto QVBox
|
|
|
|
\printline QVBox
|
|
|
|
|
|
|
|
Here we simply create a vertical box container. The QVBox arranges
|
|
|
|
its child widgets in a vertical row, one above the other, handing out
|
|
|
|
space according to each child's \l QWidget::sizePolicy().
|
|
|
|
|
|
|
|
\printline resize
|
|
|
|
|
|
|
|
We set its width to 200 pixels and the height to 120 pixels.
|
|
|
|
|
|
|
|
\printline quit
|
|
|
|
|
|
|
|
A child is born.
|
|
|
|
|
|
|
|
This QPushButton is created with both a text ("Quit") and a parent
|
|
|
|
(box). A child widget is always on top of its parent. When
|
|
|
|
displayed, it is clipped by its parent's bounds.
|
|
|
|
|
|
|
|
The parent widget, the QVBox, automatically adds the child centered in
|
|
|
|
its box. Because nothing else is added, the button gets all the space
|
|
|
|
the parent has.
|
|
|
|
|
|
|
|
\skipto show
|
|
|
|
\printline show
|
|
|
|
|
|
|
|
When a parent widget is shown, it will call show for all its children
|
|
|
|
(except those on which you have done an explicit \l QWidget::hide()).
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
The button no longer fills the entire widget. Instead, it gets a
|
|
|
|
"natural" size. This is because there is now a new top-level widget,
|
|
|
|
which uses the button's size hint and size change policy to set the
|
|
|
|
button's size and position. (See \l QWidget::sizeHint() and \l
|
|
|
|
QWidget::setSizePolicy() for more information about these functions.)
|
|
|
|
|
|
|
|
(See \link tutorial1-01.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Try resizing the window. How does the button change? What is the
|
|
|
|
button's size-change policy? What happens to the button's height if
|
|
|
|
you run the program with a bigger font? What happens if you try to
|
|
|
|
make the window \e really small?
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-04.html Chapter 4.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-02.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-04.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \page tutorial1-04.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 4: Let There Be Widgets
|
|
|
|
|
|
|
|
\img t4.png Screenshot of tutorial four
|
|
|
|
|
|
|
|
This example shows how to create your own widget, describes how to control the
|
|
|
|
minimum and maximum sizes of a widget, and introduces widget names.
|
|
|
|
|
|
|
|
\include t4/main.cpp
|
|
|
|
\quotefile t4/main.cpp
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\skipto MyWidget
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
Here we create a new class. Because this class inherits from QWidget,
|
|
|
|
the new class is a widget and may be a top level window or a child
|
|
|
|
widget (like the push button in Chapter 3).
|
|
|
|
|
|
|
|
This class has only one member, a constructor (in addition to the
|
|
|
|
members it inherits from QWidget). The constructor is a standard Qt
|
|
|
|
widget constructor; you should always include a similar constructor
|
|
|
|
when you create widgets.
|
|
|
|
|
|
|
|
The first argument is its parent widget. To create a top-level window
|
|
|
|
you specify a null pointer as the parent. As you can see, this widget
|
|
|
|
defaults to be a top-level window.
|
|
|
|
|
|
|
|
The second argument is the widget's name. This is \e not the text
|
|
|
|
that appears in the window's title bar or in the button. It is a name
|
|
|
|
associated with a widget to make it possible to \link
|
|
|
|
QObject::queryList() look up \endlink this widget later, and there is
|
|
|
|
also a \link QObject::dumpObjectTree() handy debugging function
|
|
|
|
\endlink that will list a complete widget hierarchy.
|
|
|
|
|
|
|
|
\printline MyWidget
|
|
|
|
\printline QWidget
|
|
|
|
|
|
|
|
The implementation of the constructor starts here. Like most widgets,
|
|
|
|
it just passes on the \c parent and \c name to the QWidget
|
|
|
|
constructor.
|
|
|
|
|
|
|
|
\printuntil setMaximumSize
|
|
|
|
|
|
|
|
Because this widget doesn't know how to handle resizing, we fix its size
|
|
|
|
by setting the minimum and maximum to be equal. In the next chapter
|
|
|
|
we will show how a widget can respond to resize event from the user.
|
|
|
|
|
|
|
|
\printuntil setFont
|
|
|
|
|
|
|
|
Here we create and set up a child widget of this widget (the new widget's
|
|
|
|
parent is \c this) which has the widget name "quit". The widget
|
|
|
|
name has nothing to do with the button text; it just happens to be
|
|
|
|
similar in this case.
|
|
|
|
|
|
|
|
Note that \c quit is a local variable in the constructor. MyWidget
|
|
|
|
does not keep track of it, but Qt does, and will by default delete it
|
|
|
|
when MyWidget is deleted. This is why MyWidget doesn't need a
|
|
|
|
destructor. (On the other hand, there is no harm in deleting a child
|
|
|
|
when you choose to, the child will automatically tell Qt about its
|
|
|
|
imminent death.)
|
|
|
|
|
|
|
|
The setGeometry() call does the same as move() and resize() did in the
|
|
|
|
previous chapters.
|
|
|
|
|
|
|
|
\printline qApp
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
Because the MyWidget class doesn't know about the application object, it
|
|
|
|
has to connect to Qt's pointer to it, \c qApp.
|
|
|
|
|
|
|
|
A widget is a software component and should know as little as possible
|
|
|
|
about its environment in order to be as general and reusable as
|
|
|
|
possible.
|
|
|
|
|
|
|
|
Knowing the name of the application object would break this principle,
|
|
|
|
so Qt offers an alias, qApp, for the cases in which a component such as
|
|
|
|
MyWidget needs to talk to the application object.
|
|
|
|
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
Here we instantiate our new child, set it to be the main widget, and
|
|
|
|
execute the application.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
This program is very similar in behavior to the previous one. The
|
|
|
|
difference lies in the way we have implemented it. It does behave
|
|
|
|
slightly differently, however. Just try to resize it to see.
|
|
|
|
|
|
|
|
(See \link tutorial1-01.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Try to create another MyWidget object in main(). What happens?
|
|
|
|
|
|
|
|
Try to add more buttons or put in widgets other than QPushButton.
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-05.html Chapter 5.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-03.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-05.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \page tutorial1-05.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 5: Building Blocks
|
|
|
|
|
|
|
|
\img t5.png Screenshot of tutorial five
|
|
|
|
|
|
|
|
This example shows how to create and connect together several widgets
|
|
|
|
by using signals and slots, and how to handle resize events.
|
|
|
|
|
|
|
|
\include t5/main.cpp
|
|
|
|
\quotefile t5/main.cpp
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\skipto qapp
|
|
|
|
\printuntil qvbox
|
|
|
|
|
|
|
|
Three new include files are shown here. qslider.h and qlcdnumber.h are there
|
|
|
|
because we use two new widgets, QSlider and QLCDNumber. qvbox.h is
|
|
|
|
here because we use Qt's automatic layout support.
|
|
|
|
|
|
|
|
\skipto MyWidget
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
\target constructor
|
|
|
|
\printuntil {
|
|
|
|
|
|
|
|
MyWidget is now derived from QVBox instead of QWidget. That way we use
|
|
|
|
the layout of the QVBox (which places all of its children vertically
|
|
|
|
inside itself). Resizes are now handled automatically by the QVBox and
|
|
|
|
therefore by MyWidget, too.
|
|
|
|
|
|
|
|
\skipto lcd
|
|
|
|
\printline lcd
|
|
|
|
|
|
|
|
\c lcd is a QLCDNumber, a widget that displays numbers in an LCD-like
|
|
|
|
fashion. This instance is set up to display two digits and to be a child of
|
|
|
|
\e this. It is named "lcd".
|
|
|
|
|
|
|
|
\printline QSlider
|
|
|
|
\printline slider
|
|
|
|
\printline slider
|
|
|
|
|
|
|
|
QSlider is a classical slider; the user can use the widget to drag
|
|
|
|
something to adjust an integer value in a range. Here we create a
|
|
|
|
horizontal one, set its range to 0-99 (inclusive, see the \l
|
|
|
|
QSlider::setRange() documentation) and its initial value to 0.
|
|
|
|
|
|
|
|
\printline connect
|
|
|
|
|
|
|
|
Here we use the \link signalsandslots.html signal/slot mechanism \endlink
|
|
|
|
to connect the slider's valueChanged() signal to the LCD number's
|
|
|
|
display() slot.
|
|
|
|
|
|
|
|
Whenever the slider's value changes it broadcasts the new value by
|
|
|
|
emitting the valueChanged() signal. Because that signal is connected to
|
|
|
|
the LCD number's display() slot, the slot is called when the signal is
|
|
|
|
broadcast. Neither of the objects knows about the other. This is
|
|
|
|
essential in component programming.
|
|
|
|
|
|
|
|
Slots are otherwise normal C++ member functions and follow the normal
|
|
|
|
C++ access rules.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
The LCD number reflects everything you do to the slider, and the
|
|
|
|
widget handles resizing well. Notice that the LCD number widget
|
|
|
|
changes in size when the window is resized (because it can), but the
|
|
|
|
others stay about the same (because otherwise they would look stupid).
|
|
|
|
|
|
|
|
(See \link tutorial1-01.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Try changing the LCD number to add more digits or \link
|
|
|
|
QLCDNumber::setMode() to change mode.\endlink You can even add four push
|
|
|
|
buttons to set the number base.
|
|
|
|
|
|
|
|
You can also change the slider's range.
|
|
|
|
|
|
|
|
Perhaps it would have been better to use \l QSpinBox than a slider?
|
|
|
|
|
|
|
|
Try to make the application quit when the LCD number overflows.
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-06.html Chapter 6.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-04.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-06.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \page tutorial1-06.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 6: Building Blocks Galore!
|
|
|
|
|
|
|
|
\img t6.png Screenshot of tutorial six
|
|
|
|
|
|
|
|
This example shows how to encapsulate two widgets into a new component and
|
|
|
|
how easy it is to use many widgets. For the first time, we use a custom
|
|
|
|
widget as a child widget.
|
|
|
|
|
|
|
|
\target main
|
|
|
|
\include t6/main.cpp
|
|
|
|
\quotefile t6/main.cpp
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\skipto LCDRange
|
|
|
|
\printuntil };
|
|
|
|
|
|
|
|
The LCDRange widget is a widget without any API. It just has a
|
|
|
|
constructor. This sort of widget is not very useful, so we'll add some API later.
|
|
|
|
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This is lifted straight from the
|
|
|
|
\link tutorial1-05.html#constructor MyWidget constructor \endlink in Chapter 5.
|
|
|
|
The only differences are that the button is left out and the class
|
|
|
|
is renamed.
|
|
|
|
|
|
|
|
\printline MyWidget
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
MyWidget, too, contains no API except a constructor.
|
|
|
|
|
|
|
|
\printline MyWidget
|
|
|
|
\printuntil connect
|
|
|
|
|
|
|
|
The push button that used to be in what is now LCDRange has been
|
|
|
|
separated so that we can have one "Quit" button and many LCDRange
|
|
|
|
objects.
|
|
|
|
|
|
|
|
\printline grid
|
|
|
|
|
|
|
|
We create a QGrid object with four columns. The QGRid widget
|
|
|
|
automatically arranges its children in rows and columns; you can
|
|
|
|
specify the number of rows or of columns, and QGrid will discover its
|
|
|
|
new children and fit them into the grid.
|
|
|
|
|
|
|
|
\printline for
|
|
|
|
\printline for
|
|
|
|
\printline LCDRange
|
|
|
|
|
|
|
|
Four columns, four rows.
|
|
|
|
|
|
|
|
We create 4*4 LCDRanges, all of which are children of the grid object.
|
|
|
|
The QGrid widget will arrange them.
|
|
|
|
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
That's all.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
This program shows how easy it is to use many widgets at a time. Each
|
|
|
|
one behaves like the slider and LCD number in the previous
|
|
|
|
chapter. Again, the difference lies in the implementation.
|
|
|
|
|
|
|
|
(See \link tutorial1-01.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Initialize each slider with a different/random value on startup.
|
|
|
|
|
|
|
|
The source contains three occurrences of "4". What happens if you
|
|
|
|
change the one in the \l QGrid constructor call? What about the other
|
|
|
|
two? Why is this?
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-07.html Chapter 7.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-05.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-07.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file t7/lcdrange.h */
|
|
|
|
/*! \file t7/lcdrange.cpp */
|
|
|
|
/*! \file t7/main.cpp */
|
|
|
|
|
|
|
|
/*! \page tutorial1-07.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 7: One Thing Leads to Another
|
|
|
|
|
|
|
|
\img t7.png Screenshot of tutorial seven
|
|
|
|
|
|
|
|
This example shows how to create custom widgets with signals and
|
|
|
|
slots, and how to connect them together in more complex ways. For the
|
|
|
|
first time, the source is split among several files which we've placed
|
|
|
|
in the \c t7 subdirectory.
|
|
|
|
|
|
|
|
\list
|
|
|
|
\i \l t7/lcdrange.h contains the LCDRange class definition.
|
|
|
|
\i \l t7/lcdrange.cpp contains the LCDRange implementation.
|
|
|
|
\i \l t7/main.cpp contains MyWidget and main.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\section2 \l t7/lcdrange.h
|
|
|
|
|
|
|
|
This file is mainly lifted from \link tutorial1-06.html#main main.cpp \endlink in
|
|
|
|
Chapter 6; only the changes are noted here.
|
|
|
|
|
|
|
|
\quotefile t7/lcdrange.h
|
|
|
|
|
|
|
|
\skipto ifndef
|
|
|
|
\printuntil define
|
|
|
|
|
|
|
|
This is the classic C construction to avoid errors if a header file
|
|
|
|
happens to be included more than once. If you don't use it already,
|
|
|
|
it is a very good habit to develop. The #ifndef should enclose \e all of the
|
|
|
|
header file.
|
|
|
|
|
|
|
|
\printline include
|
|
|
|
|
|
|
|
\c qvbox.h is included. LCDRange inherits QVBox, and the header file
|
|
|
|
of a parent class must always be included. We cheated a bit in the
|
|
|
|
previous chapters, and we let \c qwidget.h be included indirectly via
|
|
|
|
other header files such as \c qpushbutton.h.
|
|
|
|
|
|
|
|
\printline QSlider
|
|
|
|
|
|
|
|
This is another classic trick, but one that's much less used often. Because
|
|
|
|
we don't need QSlider in the \e interface of the class, only in the
|
|
|
|
implementation, we use a forward declaration of the class in the
|
|
|
|
header file and include the header file for QSlider in the .cpp
|
|
|
|
file.
|
|
|
|
|
|
|
|
This makes the compilation of big projects much faster, because when a
|
|
|
|
header file has changed, fewer files need to be recompiled. It can
|
|
|
|
often speed up big compilations by a factor of two or more.
|
|
|
|
|
|
|
|
\skipto LCDRange
|
|
|
|
\printuntil parent=0
|
|
|
|
|
|
|
|
Note the Q_OBJECT. This macro must be included in \e all classes that
|
|
|
|
contain signals and/or slots. If you are curious, it defines the
|
|
|
|
functions that are implemented in the
|
|
|
|
\link metaobjects.html meta object file \endlink.
|
|
|
|
|
|
|
|
\printline value
|
|
|
|
\printuntil valueChanged
|
|
|
|
|
|
|
|
These three members make up an interface between this widget and other
|
|
|
|
components in a program. Until now, LCDRange didn't really have an
|
|
|
|
interface at all.
|
|
|
|
|
|
|
|
value() is a public function for accessing the value of the LCDRange.
|
|
|
|
setValue() is our first custom slot and valueChanged() is our first
|
|
|
|
custom signal.
|
|
|
|
|
|
|
|
Slots must be implemented in the normal way (remember that a slot is also
|
|
|
|
a C++ member function). Signals are automatically implemented in the
|
|
|
|
\link signalsandslots.html meta object\endlink file. Signals follow the
|
|
|
|
access rules of protected C++ functions (i.e., they can be emitted only
|
|
|
|
by the class they are defined in or by classes inheriting from it).
|
|
|
|
|
|
|
|
The signal valueChanged() is used when the LCDRange's value has
|
|
|
|
changed - just as you guessed from the name. This is not the last
|
|
|
|
signal you'll see called <i>something</i>Changed().
|
|
|
|
|
|
|
|
\section2 \l t7/lcdrange.cpp
|
|
|
|
|
|
|
|
\quotefile t7/lcdrange.cpp
|
|
|
|
|
|
|
|
This file is mainly lifted from \link tutorial1-06.html#main t6/main.cpp \endlink, and
|
|
|
|
only the changes are noted here.
|
|
|
|
|
|
|
|
\skipto connect
|
|
|
|
\printline connect
|
|
|
|
\printline display
|
|
|
|
\printline connect
|
|
|
|
\printline valueChanged
|
|
|
|
|
|
|
|
This code is from the LCDRange constructor.
|
|
|
|
|
|
|
|
The first connect is the same that you have seen in the previous chapter.
|
|
|
|
The second is new; it connects slider's valueChanged() signal to this
|
|
|
|
object's valueChanged \e signal. Connect() with 3 arguments always
|
|
|
|
connects to signals or slots in \c this object.
|
|
|
|
|
|
|
|
Yes, that's right. Signals can be connected to other signals. When
|
|
|
|
the first is emitted, the second signal is also emitted.
|
|
|
|
|
|
|
|
Let's look at what happens when the user operates the slider. The
|
|
|
|
slider sees that its value has changed and emits the valueChanged()
|
|
|
|
signal. That signal is connected both to the display() slot of the
|
|
|
|
QLCDNumber and to the valueChanged() signal of the LCDRange.
|
|
|
|
|
|
|
|
Thus, when the signal is emitted, LCDRange emits its own
|
|
|
|
valueChanged() signal. In addition, QLCDNumber::display() is called
|
|
|
|
and shows the new number.
|
|
|
|
|
|
|
|
Note that you're not guaranteed any particular order of execution -
|
|
|
|
LCDRange::valueChanged() may be emitted before or after
|
|
|
|
QLCDNumber::display()and is entirely arbitrary.
|
|
|
|
|
|
|
|
\skipto LCDRange::value
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
The implementation of value() is straightforward; it simply returns
|
|
|
|
the slider's value.
|
|
|
|
|
|
|
|
\printline setValue
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
The implementation of setValue() is equally straightforward. Note
|
|
|
|
that because the slider and LCD number are connected, setting the
|
|
|
|
slider's value automatically updates the LCD number as well. In
|
|
|
|
addition, the slider will automatically adjust the value if it is
|
|
|
|
outside its legal range.
|
|
|
|
|
|
|
|
\section2 \l t7/main.cpp
|
|
|
|
|
|
|
|
\quotefile t7/main.cpp
|
|
|
|
|
|
|
|
\skipto previous
|
|
|
|
\printline previous
|
|
|
|
\printuntil setValue
|
|
|
|
\printline previous
|
|
|
|
\printline }
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
All of main.cpp is copied from the previous chapter except in
|
|
|
|
the constructor for MyWidget. When we create the 16 LCDRange object, we
|
|
|
|
now connect them using the \link signalsandslots.html
|
|
|
|
signal/slot\endlink mechanism. Each has its valueChanged() signal
|
|
|
|
connected to the setValue() slot in the previous one. Because LCDRange
|
|
|
|
emits the signal valueChanged() when its value changes (surprise!), we
|
|
|
|
are here creating a "chain" of signals and slots.
|
|
|
|
|
|
|
|
\target compiling
|
|
|
|
\section1 Compiling
|
|
|
|
|
|
|
|
Creating a makefile for a multi-file application is no different from
|
|
|
|
creating one for a single-file application. If you've saved all the
|
|
|
|
files in this example in their own directory, all you have to do is:
|
|
|
|
\code
|
|
|
|
qmake -project
|
|
|
|
qmake
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
The first command tells \link qmake-manual.book qmake\endlink to
|
|
|
|
create a \c .pro (project) file. The second command tells it to create
|
|
|
|
a (platform-specific) makefile based on the project file. You should
|
|
|
|
now be able to type \c make (or \c nmake if you're using Visual
|
|
|
|
Studio) to build your application.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
On startup, the program's appearance is identical to the previous one.
|
|
|
|
Try operating the slider to the bottom right...
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Use the bottom right slider to set all LCDs to 50. Then set the top
|
|
|
|
half to 40 by clicking once to the left of the slider handle. Now,
|
|
|
|
use the one to the left of the last one operated to set the first
|
|
|
|
seven LCDs back to 50.
|
|
|
|
|
|
|
|
Click to the left of the handle on the bottom right slider. What
|
|
|
|
happens? Why is this the correct behavior?
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-08.html Chapter 8.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-06.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-08.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file t8/lcdrange.h */
|
|
|
|
/*! \file t8/lcdrange.cpp */
|
|
|
|
/*! \file t8/cannon.h */
|
|
|
|
/*! \file t8/cannon.cpp */
|
|
|
|
/*! \file t8/main.cpp */
|
|
|
|
|
|
|
|
/*! \page tutorial1-08.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 8: Preparing for Battle
|
|
|
|
|
|
|
|
\img t8.png Screenshot of tutorial eight
|
|
|
|
|
|
|
|
In this example, we introduce the first custom widget that can paint
|
|
|
|
itself. We also add a useful keyboard interface (with two lines of
|
|
|
|
code).
|
|
|
|
|
|
|
|
\list
|
|
|
|
\i \l t8/lcdrange.h contains the LCDRange class definition.
|
|
|
|
\i \l t8/lcdrange.cpp contains the LCDRange implementation.
|
|
|
|
\i \l t8/cannon.h contains the CannonField class definition.
|
|
|
|
\i \l t8/cannon.cpp contains the CannonField implementation.
|
|
|
|
\i \l t8/main.cpp contains MyWidget and main.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\section2 \l t8/lcdrange.h
|
|
|
|
|
|
|
|
This file is very similar to the lcdrange.h in Chapter 7. We have added
|
|
|
|
one slot: setRange().
|
|
|
|
|
|
|
|
\quotefile t8/lcdrange.h
|
|
|
|
|
|
|
|
\skipto setRange
|
|
|
|
\printline setRange
|
|
|
|
|
|
|
|
We now add the possibility of setting the range of the LCDRange.
|
|
|
|
Until now, it has been fixed at 0..99.
|
|
|
|
|
|
|
|
\section2 \l t8/lcdrange.cpp
|
|
|
|
|
|
|
|
\quotefile t8/lcdrange.cpp
|
|
|
|
|
|
|
|
There is a change to the constructor (we'll discuss that later).
|
|
|
|
|
|
|
|
\skipto ::setRange
|
|
|
|
\printuntil slider
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
SetRange() sets the range of the slider in the LCDRange. Because we
|
|
|
|
have set up the QLCDNumber to always display two digits, we want to
|
|
|
|
limit the possible range of \c minVal and \c maxVal to 0..99 to avoid
|
|
|
|
overflow of the QLCDNumber. (We could have allowed values down to -9
|
|
|
|
but chose not to.) If the arguments are illegal, we use Qt's
|
|
|
|
qWarning() function to issue a warning to the user and return
|
|
|
|
immediately. qWarning() is a printf-like function that by default
|
|
|
|
sends its output to \c stderr. If you want, you can install your own handler
|
|
|
|
function using \l ::qInstallMsgHandler().
|
|
|
|
|
|
|
|
\section2 \l t8/cannon.h
|
|
|
|
|
|
|
|
CannonField is a new custom widget that knows how to display itself.
|
|
|
|
|
|
|
|
\quotefile t8/cannon.h
|
|
|
|
|
|
|
|
\skipto include
|
|
|
|
\skipto class
|
|
|
|
\printuntil parent=0
|
|
|
|
|
|
|
|
CannonField inherits QWidget, and we use the same idiom as for LCDRange.
|
|
|
|
|
|
|
|
\printuntil angleChanged
|
|
|
|
|
|
|
|
For the time being, CannonField only contains an angle value for which we
|
|
|
|
provide an interface using the same idiom as for value in LCDRange.
|
|
|
|
|
|
|
|
\printline protected
|
|
|
|
\printline paintEvent
|
|
|
|
|
|
|
|
This is the second of the many event handlers in QWidget that we
|
|
|
|
encounter. This virtual function is called by Qt whenever a widget needs
|
|
|
|
to update itself (i.e., paint the widget's surface).
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t8/cannon.cpp
|
|
|
|
|
|
|
|
\quotefile t8/cannon.cpp
|
|
|
|
|
|
|
|
\skipto ::CannonField
|
|
|
|
\printuntil {
|
|
|
|
|
|
|
|
Again, we use the same idiom as for LCDRange in the previous chapter.
|
|
|
|
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
The constructor initializes the angle value to 45 degrees and sets a
|
|
|
|
custom palette for this widget.
|
|
|
|
|
|
|
|
This palette uses the indicated color as background and picks other
|
|
|
|
colors suitably. (For this widget only the background and text
|
|
|
|
colors will actually be used.)
|
|
|
|
|
|
|
|
\skipto ::setAngle
|
|
|
|
\printuntil emit
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
This function sets the angle value. We have chosen a legal range of
|
|
|
|
5..70 and adjust the given number of degrees accordingly. We have
|
|
|
|
chosen not to issue a warning if the new angle is out of range.
|
|
|
|
|
|
|
|
If the new angle equals the old one, we return immediately. It is
|
|
|
|
important to only emit the signal angleChanged() when the angle \e
|
|
|
|
really has changed.
|
|
|
|
|
|
|
|
Then we set the new angle value and repaint our widget. The \l
|
|
|
|
QWidget::repaint() function clears the widget (usually filling it with
|
|
|
|
its background color) and sends a paint event to the widget. This
|
|
|
|
results in a call to the paint event function of the widget.
|
|
|
|
|
|
|
|
Finally, we emit the angleChanged() signal to tell the outside world
|
|
|
|
that the angle has changed. The \c emit keyword is unique to Qt and
|
|
|
|
not regular C++ syntax. In fact, it is a macro.
|
|
|
|
|
|
|
|
\skipto ::paintEvent
|
|
|
|
\printuntil drawText
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
This is our first attempt to write a paint event handler. The event
|
|
|
|
argument contains a description of the paint event. \l QPaintEvent
|
|
|
|
contains the region in the widget that must be updated. For the time
|
|
|
|
being, we will be lazy and just paint everything.
|
|
|
|
|
|
|
|
Our code displays the angle value in the widget at a fixed position.
|
|
|
|
First we create a QString with some text and the angle; then we create
|
|
|
|
a QPainter operating on this widget and use it to paint the string.
|
|
|
|
We'll come back to QPainter later; it can do a great many things.
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t8/main.cpp
|
|
|
|
|
|
|
|
\quotefile t8/main.cpp
|
|
|
|
|
|
|
|
\skipto cannon.h
|
|
|
|
\printline cannon.h
|
|
|
|
|
|
|
|
We include our new class.
|
|
|
|
|
|
|
|
\skipto MyWidget
|
|
|
|
\printuntil };
|
|
|
|
|
|
|
|
This time we include a single LCDRange and a CannonField in our top-level
|
|
|
|
widget.
|
|
|
|
|
|
|
|
\skipto angle
|
|
|
|
\printline angle
|
|
|
|
|
|
|
|
In the constructor, we create and set up our LCDRange.
|
|
|
|
|
|
|
|
\printline setRange
|
|
|
|
|
|
|
|
We set the LCDRange to accept ranges from 5 to 70 degrees.
|
|
|
|
|
|
|
|
\printline cannonField
|
|
|
|
\printline CannonField
|
|
|
|
|
|
|
|
We create our CannonField.
|
|
|
|
|
|
|
|
\printuntil setValue
|
|
|
|
|
|
|
|
Here we connect the valueChanged() signal of the LCDRange to the
|
|
|
|
setAngle() slot of the CannonField. This will update CannonField's angle
|
|
|
|
value whenever the user operates the LCDRange. We also make the reverse
|
|
|
|
connection so that changing the angle in the CannonField will update the
|
|
|
|
LCDRange value. In our example we never change the angle of the
|
|
|
|
CannonField directly; but by doing the last connect() we ensure that no
|
|
|
|
future changes will disrupt the synchronization between those two values.
|
|
|
|
|
|
|
|
This illustrates the power of component programming and proper
|
|
|
|
encapsulation.
|
|
|
|
|
|
|
|
Notice how important it is to emit the angleChanged() signal only when
|
|
|
|
the angle actually changes. If both the LCDRange and the CannonField
|
|
|
|
had omitted this check, the program would have entered an infinite
|
|
|
|
loop upon the first change of one of the values.
|
|
|
|
|
|
|
|
\printline QGridLayout
|
|
|
|
\printline 2x2
|
|
|
|
|
|
|
|
So far we have used the no-assembly-retquired QVBox and QGrid widgets
|
|
|
|
for geometry management. Now, however, we want to have a little more
|
|
|
|
control over the layout, and we switch to the more powerful QGridLayout
|
|
|
|
class. QGridLayout isn't a widget; it is a different class that can
|
|
|
|
manage the children of \e any widget.
|
|
|
|
|
|
|
|
As the comment indicates, we create a two-by-two array with ten pixel
|
|
|
|
borders. (The constructor for \l QGridLayout can be a little cryptic,
|
|
|
|
so it's good to put in such comments.)
|
|
|
|
|
|
|
|
\printline addWidget
|
|
|
|
|
|
|
|
We add the Quit button in the top-left cell of the grid: 0, 0.
|
|
|
|
|
|
|
|
\printline addWidget
|
|
|
|
|
|
|
|
We put the angle LCDRange in the bottom-left cell, aligned to the top
|
|
|
|
of its cell. (This alignment is one of the things QGridLayout allows
|
|
|
|
but QGrid does not allow.)
|
|
|
|
|
|
|
|
\printline addWidget
|
|
|
|
|
|
|
|
We put the CannonField object in the bottom-right cell. (The top-
|
|
|
|
right cell is empty.)
|
|
|
|
|
|
|
|
\printline setColStretch
|
|
|
|
|
|
|
|
We tell QGridLayout that the right column (column 1) is stretchable.
|
|
|
|
Because the left column isn't (it has stretch factor 0, the default
|
|
|
|
value), QGridLayout will try to let the left-hand widgets' sizes be
|
|
|
|
unchanged and will resize just the CannonField when the MyWidget is
|
|
|
|
resized.
|
|
|
|
|
|
|
|
\printline setValue
|
|
|
|
|
|
|
|
We set an initial angle value. Note that this will trigger the
|
|
|
|
connection from LCDRange to CannonField.
|
|
|
|
|
|
|
|
\printline setFocus
|
|
|
|
|
|
|
|
Our last action is to set \c angle to have keyboard focus so that
|
|
|
|
keyboard input will go to the LCDRange widget by default.
|
|
|
|
|
|
|
|
LCDRange does not contain any keyPressEvent(), so that would seem not
|
|
|
|
to be terribly useful. However, its constructor just got a new line:
|
|
|
|
|
|
|
|
\quotefile t8/lcdrange.cpp
|
|
|
|
\skipto setFocusProxy
|
|
|
|
\printline setFocusProxy
|
|
|
|
|
|
|
|
The LCDRange sets the slider to be its focus proxy. That means that
|
|
|
|
when someone (the program or the user) wants to give the LCDRange
|
|
|
|
keyboard focus, the slider should take care of it. QSlider has a decent
|
|
|
|
keyboard interface, so with just one line of code we've given LCDRange
|
|
|
|
one.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
The keyboard now does something - the arrow keys, Home, End, PageUp
|
|
|
|
and PageDown all do something vaguely sensible.
|
|
|
|
|
|
|
|
When the slider is operated, the CannonField displays the new angle
|
|
|
|
value. Upon resizing, CannonField is given as much space as possible.
|
|
|
|
|
|
|
|
On Windows machines with an 8-bit display the new background color is
|
|
|
|
dithered to death. The next chapter works around this.
|
|
|
|
|
|
|
|
(See \link tutorial1-07.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Try to resize the window. What happens if you make it really narrow
|
|
|
|
or really squat?
|
|
|
|
|
|
|
|
If you remove the AlignTop, what happens to the LCDRange's position
|
|
|
|
and size? Why?
|
|
|
|
|
|
|
|
If you give the left-hand column a non-zero stretch factor, what
|
|
|
|
happens when you resize the window?
|
|
|
|
|
|
|
|
Leave out the setFocus() call. Which behavior do you prefer?
|
|
|
|
|
|
|
|
Try to change "Quit" to "&Quit" in the QButton::setText() call. How
|
|
|
|
does the button's look change? What happens if you press Alt+Q while
|
|
|
|
the program's running? (It is Meta+Q on a few keyboards.)
|
|
|
|
|
|
|
|
Center the text in the CannonField.
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-09.html Chapter 9.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-07.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-09.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file t9/lcdrange.h */
|
|
|
|
/*! \file t9/lcdrange.cpp */
|
|
|
|
/*! \file t9/cannon.h */
|
|
|
|
/*! \file t9/cannon.cpp */
|
|
|
|
/*! \file t9/main.cpp */
|
|
|
|
|
|
|
|
/*! \page tutorial1-09.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 9: With Cannon You Can
|
|
|
|
|
|
|
|
\img t9.png Screenshot of tutorial nine
|
|
|
|
|
|
|
|
In this example we become graphic by drawing a cute little blue
|
|
|
|
cannon. Only cannon.cpp differs from the previous chapter.
|
|
|
|
|
|
|
|
\list
|
|
|
|
\i \l t9/lcdrange.h contains the LCDRange
|
|
|
|
class definition.
|
|
|
|
\i \l t9/lcdrange.cpp contains the LCDRange
|
|
|
|
implementation.
|
|
|
|
\i \l t9/cannon.h contains the CannonField class
|
|
|
|
definition.
|
|
|
|
\i \l t9/cannon.cpp contains the CannonField
|
|
|
|
implementation.
|
|
|
|
\i \l t9/main.cpp contains MyWidget and main.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\section2 \l t9/cannon.cpp
|
|
|
|
|
|
|
|
\quotefile t9/cannon.cpp
|
|
|
|
|
|
|
|
\skipto ::paintEvent
|
|
|
|
\printuntil QPainter
|
|
|
|
|
|
|
|
We'll now start to use QPainter in earnest. We create a painter that
|
|
|
|
operates on this widget.
|
|
|
|
|
|
|
|
\printline setBrush
|
|
|
|
|
|
|
|
When QPainter fills a rectangle, a circle, or whatever, it fills the
|
|
|
|
shape using its brush. Here we set it to use a blue brush. (We
|
|
|
|
could also use a pattern.)
|
|
|
|
|
|
|
|
\printline setPen
|
|
|
|
|
|
|
|
And the edges of what QPainter draws are drawn using the pen. Here we
|
|
|
|
set it to NoPen, meaning that there will be no special edge when we
|
|
|
|
draw something; the blue brush will go all the way to the edges of
|
|
|
|
the things we draw.
|
|
|
|
|
|
|
|
\skipto translate
|
|
|
|
\printline translate
|
|
|
|
|
|
|
|
The \l QPainter::translate() function translates the coordinate
|
|
|
|
system of the QPainter; i.e., it moves it by an offset. Here we set
|
|
|
|
the (0, 0) point to the bottom-left corner of the widget. The x and
|
|
|
|
y directions remain unchanged, i.e., all the y coordinates inside the
|
|
|
|
widget are now negative (see \link coordsys.html The Coordinate
|
|
|
|
System\endlink for more information about Qt's coordinate system).
|
|
|
|
|
|
|
|
\printline drawPie
|
|
|
|
|
|
|
|
The drawPie() function draws a pie shape inside the specified
|
|
|
|
rectangle using a start angle and an arc length. The angles are
|
|
|
|
specified in 1/16th of a degree. Zero degrees is at the 3 o'clock
|
|
|
|
position. The drawing direction is counter-clockwise. Here we draw a
|
|
|
|
quarter of a circle in the bottom-left corner of the widget. The pie
|
|
|
|
is filled with blue and has no outline.
|
|
|
|
|
|
|
|
\printline rotate
|
|
|
|
|
|
|
|
The QPainter::rotate() function rotates the coordinate system of the
|
|
|
|
QPainter around the origin. The rotation argument is a \c float given
|
|
|
|
in degrees (not given in 1/16th of a degree as above) and clockwise.
|
|
|
|
Here we rotate the coordinate system \c ang degrees counter-clockwise.
|
|
|
|
|
|
|
|
\printline drawRect
|
|
|
|
|
|
|
|
The QPainter::drawRect() function draws the specified rectangle. Here
|
|
|
|
we draw the barrel of the cannon.
|
|
|
|
|
|
|
|
It can often be difficult to envision the resulting drawing when the
|
|
|
|
coordinate system has been transformed (translated, rotated, scaled, or
|
|
|
|
sheared) as above.
|
|
|
|
|
|
|
|
In this case the coordinate system is first translated and then rotated.
|
|
|
|
If the rectangle QRect(33, -4, 15, 8) had been drawn in the translated
|
|
|
|
coordinate system, it would have looked like this:
|
|
|
|
|
|
|
|
\img t9_1.png The cannon translated but not rotated
|
|
|
|
|
|
|
|
Note that the rectangle is clipped by the border of the CannonField
|
|
|
|
widget. When we rotate the coordinate system, for instance 60
|
|
|
|
degrees, the rectangle will be rotated around (0, 0), which is the
|
|
|
|
bottom-left corner because we have translated the coordinate system.
|
|
|
|
The result looks like this:
|
|
|
|
|
|
|
|
\img t9_2.png The cannon translated and rotated
|
|
|
|
|
|
|
|
We're done, except that we haven't explained why Windows didn't dither
|
|
|
|
this time.
|
|
|
|
|
|
|
|
\quotefile t9/main.cpp
|
|
|
|
\skipto main
|
|
|
|
\printline main
|
|
|
|
\printline {
|
|
|
|
\printline CustomColor
|
|
|
|
\printline QApplication
|
|
|
|
|
|
|
|
We tell Qt that we want a different color-allocation strategy for this
|
|
|
|
program. There is no single correct color-allocation strategy. Because
|
|
|
|
this program uses an unusual yellow but not many colors, \c
|
|
|
|
CustomColor is best. There are several other allocation strategies; you can read about them in the \l QApplication::setColorSpec()
|
|
|
|
documentation.
|
|
|
|
|
|
|
|
Mostly you can ignore this, since the default is good. Occasionally
|
|
|
|
some applications with unusual color use look bad; changing the
|
|
|
|
allocation strategy often helps then.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
When the slider is operated the angle of the drawn cannon changes
|
|
|
|
accordingly.
|
|
|
|
|
|
|
|
The Q on the Quit button is now underlined, and Alt+Q does what you
|
|
|
|
think it does. If you do not know why, you didn't do the exercises in
|
|
|
|
Chapter 8.
|
|
|
|
|
|
|
|
You may notice that the cannon flickers annoyingly, especially on a
|
|
|
|
slow machine. We'll fix this in the next chapter.
|
|
|
|
|
|
|
|
(See \link tutorial1-07.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Set a different pen instead of NoPen. Set a patterned brush.
|
|
|
|
|
|
|
|
Try "Q&uit" or "Qu&it" as button text instead of "&Quit". What
|
|
|
|
happens?
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-10.html Chapter 10.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-08.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-10.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file t10/lcdrange.h */
|
|
|
|
/*! \file t10/lcdrange.cpp */
|
|
|
|
/*! \file t10/cannon.h */
|
|
|
|
/*! \file t10/cannon.cpp */
|
|
|
|
/*! \file t10/main.cpp */
|
|
|
|
|
|
|
|
/*! \page tutorial1-10.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 10: Smooth as Silk
|
|
|
|
|
|
|
|
\img t10.png Screenshot of tutorial ten
|
|
|
|
|
|
|
|
In this example, we introduce painting in a pixmap to remove flickering.
|
|
|
|
We also add a force control.
|
|
|
|
|
|
|
|
\list
|
|
|
|
\i \l t10/lcdrange.h contains the LCDRange
|
|
|
|
class definition.
|
|
|
|
\i \l t10/lcdrange.cpp contains the LCDRange
|
|
|
|
implementation.
|
|
|
|
\i \l t10/cannon.h contains the CannonField class
|
|
|
|
definition.
|
|
|
|
\i \l t10/cannon.cpp contains the CannonField
|
|
|
|
implementation.
|
|
|
|
\i \l t10/main.cpp contains MyWidget and main.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\section2 \l t10/cannon.h
|
|
|
|
|
|
|
|
The CannonField now has a force value in addition to the angle.
|
|
|
|
|
|
|
|
\quotefile t10/cannon.h
|
|
|
|
|
|
|
|
\skipto angle
|
|
|
|
\printuntil forceChanged
|
|
|
|
|
|
|
|
The interface to the force follows the same practice as for the angle.
|
|
|
|
|
|
|
|
\skipto private
|
|
|
|
\printuntil cannonRect
|
|
|
|
|
|
|
|
We have put the definition of the cannon's enclosing rectangle in a
|
|
|
|
separate function.
|
|
|
|
|
|
|
|
\skipto ang
|
|
|
|
\printuntil };
|
|
|
|
|
|
|
|
The force is stored in the integer \c f.
|
|
|
|
|
|
|
|
\section2 \l t10/cannon.cpp
|
|
|
|
|
|
|
|
\quotefile t10/cannon.cpp
|
|
|
|
|
|
|
|
\skipto include
|
|
|
|
\skipto pixmap
|
|
|
|
\printline pixmap
|
|
|
|
|
|
|
|
We include the QPixmap class definition.
|
|
|
|
|
|
|
|
\skipto ::CannonField
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
The force (\c f) is initialized to zero.
|
|
|
|
|
|
|
|
\skipto ::setAngle
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
We have made a slight change in the setAngle() function. It repaints
|
|
|
|
only the portion of the widget that contains the cannon. The FALSE
|
|
|
|
argument indicates that the specified rectangle should not be erased
|
|
|
|
before a paint event is sent to the widget. This speeds up and smooths
|
|
|
|
the drawing a little bit.
|
|
|
|
|
|
|
|
\skipto ::setForce
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
The implementation of setForce() is quite similar to that of
|
|
|
|
setAngle(). The only difference is that because we don't show the force
|
|
|
|
value, we don't need to repaint the widget.
|
|
|
|
|
|
|
|
\skipto ::paintEvent
|
|
|
|
\printuntil return
|
|
|
|
|
|
|
|
We have now optimized the paint event to repaint only the parts of the
|
|
|
|
widget that need updating. First we check whether we have to paint
|
|
|
|
anything at all, and we return if we don't.
|
|
|
|
|
|
|
|
\printline cannonRect
|
|
|
|
\printline pix
|
|
|
|
|
|
|
|
Then we create a temporary pixmap, which we use for flicker-free
|
|
|
|
painting. All the painting operations are done into this pixmap, and
|
|
|
|
then the pixmap is drawn on the screen in a single operation.
|
|
|
|
|
|
|
|
This is the essence of flicker-free drawing: Draw on each pixel
|
|
|
|
precisely once. Less, and you get drawing errors. More, and you get
|
|
|
|
flicker. It doesn't matter much in this example - when the code was
|
|
|
|
written there were still machines slow enough for it to flicker, but
|
|
|
|
not any more. We've kept the code for educational purposes.
|
|
|
|
|
|
|
|
\printline fill
|
|
|
|
|
|
|
|
We fill the pixmap with the background from this widget.
|
|
|
|
|
|
|
|
\printline QPainter
|
|
|
|
\printuntil end
|
|
|
|
|
|
|
|
We paint, as in Chapter 9, but now we paint in the pixmap.
|
|
|
|
|
|
|
|
At this point, we have a painter variable and a pixmap that looks
|
|
|
|
precisely right, but we still haven't painted on the screen.
|
|
|
|
|
|
|
|
\printline begin
|
|
|
|
\printline drawPixmap
|
|
|
|
|
|
|
|
So we open the painter on the CannonField itself and then draw the pixmap.
|
|
|
|
|
|
|
|
That's all. A couple of extra lines at the top and a couple at the
|
|
|
|
bottom, and the code is 100% flicker-free.
|
|
|
|
|
|
|
|
\skipto cannonRect
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This function returns the rectangle enclosing the cannon in widget
|
|
|
|
coordinates. First we create a rectangle with the size 50x50 and then
|
|
|
|
move it so its bottom left corner is equal to the widget's own bottom-
|
|
|
|
left corner.
|
|
|
|
|
|
|
|
The \l QWidget::rect() function returns the widget's enclosing
|
|
|
|
rectangle in the widget's own coordinates (where the top left corner
|
|
|
|
is 0, 0).
|
|
|
|
|
|
|
|
\section2 \l t10/main.cpp
|
|
|
|
|
|
|
|
\quotefile t10/main.cpp
|
|
|
|
|
|
|
|
\skipto MyWidget::MyWidget
|
|
|
|
\printuntil {
|
|
|
|
|
|
|
|
The constructor is mostly the same, but some new bits have been added.
|
|
|
|
|
|
|
|
\skipto force
|
|
|
|
\printline force
|
|
|
|
\printline force
|
|
|
|
|
|
|
|
We add a second LCDRange, which will be used to set the force.
|
|
|
|
|
|
|
|
\skipto force
|
|
|
|
\printline connect
|
|
|
|
\printline cannonField
|
|
|
|
\printline connect
|
|
|
|
\printline force
|
|
|
|
|
|
|
|
We connect the \c force widget and the \c cannonField widget, just like
|
|
|
|
we did for the \c angle widget.
|
|
|
|
|
|
|
|
\skipto QVBoxLayout
|
|
|
|
\printline QVBoxLayout
|
|
|
|
\printline addLayout
|
|
|
|
\printline addWidget
|
|
|
|
\printline addWidget
|
|
|
|
|
|
|
|
In Chapter 9 we put \c angle in the lower-left cell of the layout.
|
|
|
|
Now we want to have two widgets in that cell, so we make a vertical
|
|
|
|
box, put the vertical box in the grid cell, and put each of \c angle
|
|
|
|
and \c range in the vertical box.
|
|
|
|
|
|
|
|
\skipto force
|
|
|
|
\printline setValue
|
|
|
|
|
|
|
|
We initialize the force value to 25.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
The flicker has gone and we have a force control.
|
|
|
|
|
|
|
|
(See \link tutorial1-07.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Make the size of the cannon barrel be dependent on the force.
|
|
|
|
|
|
|
|
Put the cannon in the bottom-right corner.
|
|
|
|
|
|
|
|
Try adding a better keyboard interface. For example, make + and -
|
|
|
|
increase and decrease the force and enter shoot. Hint: \l QAccel and
|
|
|
|
new addStep() and subtractStep() slots in LCDRange, like \l
|
|
|
|
QSlider::addStep(). If you're bothered by the way the left and right
|
|
|
|
keys work (I am!), change that too.
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-11.html Chapter 11.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-09.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-11.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file t11/lcdrange.h */
|
|
|
|
/*! \file t11/lcdrange.cpp */
|
|
|
|
/*! \file t11/cannon.h */
|
|
|
|
/*! \file t11/cannon.cpp */
|
|
|
|
/*! \file t11/main.cpp */
|
|
|
|
|
|
|
|
/*! \page tutorial1-11.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 11: Giving It a Shot
|
|
|
|
|
|
|
|
\img t11.png Screenshot of tutorial eleven
|
|
|
|
|
|
|
|
In this example we introduce a timer to implement animated shooting.
|
|
|
|
|
|
|
|
\list
|
|
|
|
\i \l t11/lcdrange.h contains the LCDRange
|
|
|
|
class definition.
|
|
|
|
\i \l t11/lcdrange.cpp contains the LCDRange
|
|
|
|
implementation.
|
|
|
|
\i \l t11/cannon.h contains the CannonField class
|
|
|
|
definition.
|
|
|
|
\i \l t11/cannon.cpp contains the CannonField
|
|
|
|
implementation.
|
|
|
|
\i \l t11/main.cpp contains MyWidget and main.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\section2 \l t11/cannon.h
|
|
|
|
|
|
|
|
The CannonField now has shooting capabilities.
|
|
|
|
|
|
|
|
\quotefile t11/cannon.h
|
|
|
|
|
|
|
|
\skipto shoot
|
|
|
|
\printline shoot
|
|
|
|
|
|
|
|
Calling this slot will make the cannon shoot if a shot is not in the air.
|
|
|
|
|
|
|
|
\printline private
|
|
|
|
\printline moveShot
|
|
|
|
|
|
|
|
This private slot is used to move the shot while it is in the air,
|
|
|
|
using a \l QTimer.
|
|
|
|
|
|
|
|
\skipto private
|
|
|
|
\printline private
|
|
|
|
\printline paintShot
|
|
|
|
|
|
|
|
This private function paints the shot.
|
|
|
|
|
|
|
|
\skipto shotRect
|
|
|
|
\printline shotRect
|
|
|
|
|
|
|
|
This private function returns the shot's enclosing rectangle if
|
|
|
|
one is in the air; otherwise the returned rectangle is undefined.
|
|
|
|
|
|
|
|
\skipto timerCount
|
|
|
|
\printuntil shoot_f
|
|
|
|
\printline };
|
|
|
|
|
|
|
|
These private variables contain information that describes the shot. The
|
|
|
|
\c timerCount keeps track of the time passed since the shot was fired.
|
|
|
|
The \c shoot_ang is the cannon angle and \c shoot_f is the cannon force
|
|
|
|
when the shot was fired.
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t11/cannon.cpp
|
|
|
|
|
|
|
|
\quotefile t11/cannon.cpp
|
|
|
|
|
|
|
|
\skipto include
|
|
|
|
\skipto math
|
|
|
|
\printline math
|
|
|
|
|
|
|
|
We include the math library because we need the sin() and cos() functions.
|
|
|
|
|
|
|
|
\skipto ::CannonField
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
We initialize our new private variables and connect the \l
|
|
|
|
QTimer::timeout() signal to our moveShot() slot. We'll move the
|
|
|
|
shot every time the timer times out.
|
|
|
|
|
|
|
|
\skipto ::shoot
|
|
|
|
\printuntil start
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
This function shoots a shot unless a shot is in the air. The \c timerCount
|
|
|
|
is reset to zero. The \c shoot_ang and \c shoot_f are set to the current
|
|
|
|
cannon angle and force. Finally, we start the timer.
|
|
|
|
|
|
|
|
\skipto ::moveShot
|
|
|
|
\printuntil repaint
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
moveShot() is the slot that moves the shot, called every 50
|
|
|
|
milliseconds when the QTimer fires.
|
|
|
|
|
|
|
|
Its tasks are to compute the new position, repaint the screen with the
|
|
|
|
shot in the new position, and if necessary, stop the timer.
|
|
|
|
|
|
|
|
First we make a \l QRegion that holds the old shotRect(). A QRegion
|
|
|
|
is capable of holding any sort of region, and we'll use it here to
|
|
|
|
simplify the painting. ShotRect() returns the rectangle where the
|
|
|
|
shot is now - it is explained in detail later.
|
|
|
|
|
|
|
|
Then we increment the \c timerCount, which has the effect of moving the
|
|
|
|
shot one step along its trajectory.
|
|
|
|
|
|
|
|
Next we fetch the new shot rectangle.
|
|
|
|
|
|
|
|
If the shot has moved beyond the right or bottom edge of the widget, we
|
|
|
|
stop the timer or we add the new shotRect() to the QRegion.
|
|
|
|
|
|
|
|
Finally, we repaint the QRegion. This will send a single paint event
|
|
|
|
for just the one or two rectangles that need updating.
|
|
|
|
|
|
|
|
\skipto ::paintEvent
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
The paint event function has been split in two since the previous
|
|
|
|
chapter. Now we fetch the bounding rectangle of the region that
|
|
|
|
needs painting, check whether it intersects either the cannon and/or
|
|
|
|
the shot, and if necessary, call paintCannon() and/or paintShot().
|
|
|
|
|
|
|
|
\skipto ::paintShot
|
|
|
|
\printuntil drawRect
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
This private function paints the shot by drawing a black filled rectangle.
|
|
|
|
|
|
|
|
We leave out the implementation of paintCannon(); it is the same as
|
|
|
|
the paintEvent() from the previous chapter.
|
|
|
|
|
|
|
|
\skipto ::shotRect
|
|
|
|
\printuntil return
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
This private function calculates the center point of the shot and returns
|
|
|
|
the enclosing rectangle of the shot. It uses the initial cannon force and
|
|
|
|
angle in addition to \c timerCount, which increases as time passes.
|
|
|
|
|
|
|
|
The formula used is the classical Newtonian formula for frictionless
|
|
|
|
movement in a gravity field. For simplicity, we've chosen to
|
|
|
|
disregard any Einsteinian effects.
|
|
|
|
|
|
|
|
We calculate the center point in a coordinate system where y
|
|
|
|
coordinates increase upward. After we have calculated the center
|
|
|
|
point, we construct a QRect with size 6x6 and move its center point to
|
|
|
|
the point calculated above. In the same operation we convert the
|
|
|
|
point into the widget's coordinate system (see \link coordsys.html The
|
|
|
|
Coordinate System\endlink).
|
|
|
|
|
|
|
|
The qRound() function is an inline function defined in qglobal.h (included
|
|
|
|
by all other Qt header files). qRound() rounds a double to the closest
|
|
|
|
integer.
|
|
|
|
|
|
|
|
\section2 \l t11/main.cpp
|
|
|
|
|
|
|
|
\quotefile t11/main.cpp
|
|
|
|
|
|
|
|
\skipto class
|
|
|
|
\printuntil };
|
|
|
|
|
|
|
|
The only addition is the Shoot button.
|
|
|
|
|
|
|
|
\skipto ::MyWidget
|
|
|
|
\skipto shoot
|
|
|
|
\printuntil setFont
|
|
|
|
|
|
|
|
In the constructor we create and set up the Shoot button exactly like we
|
|
|
|
did with the Quit button. Note that the first argument to the constructor
|
|
|
|
is the button text, and the third is the widget's name.
|
|
|
|
|
|
|
|
\skipto connect
|
|
|
|
\printline connect
|
|
|
|
|
|
|
|
Connects the clicked() signal of the Shoot button to the shoot() slot
|
|
|
|
of the CannonField.
|
|
|
|
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
The cannon can shoot, but there's nothing to shoot at.
|
|
|
|
|
|
|
|
(See \link tutorial1-07.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Make the shot a filled circle. Hint: \l QPainter::drawEllipse() may
|
|
|
|
help.
|
|
|
|
|
|
|
|
Change the color of the cannon when a shot is in the air.
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-12.html Chapter 12.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-10.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-12.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file t12/lcdrange.h */
|
|
|
|
/*! \file t12/lcdrange.cpp */
|
|
|
|
/*! \file t12/cannon.h */
|
|
|
|
/*! \file t12/cannon.cpp */
|
|
|
|
/*! \file t12/main.cpp */
|
|
|
|
|
|
|
|
/*! \page tutorial1-12.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 12: Hanging in the Air the Way Bricks Don't
|
|
|
|
|
|
|
|
\img t12.png Screenshot of tutorial twelve
|
|
|
|
|
|
|
|
In this example, we extend our LCDRange class to include a text label.
|
|
|
|
We also provide something to shoot at.
|
|
|
|
|
|
|
|
\list
|
|
|
|
\i \l t12/lcdrange.h contains the LCDRange
|
|
|
|
class definition.
|
|
|
|
\i \l t12/lcdrange.cpp contains the LCDRange
|
|
|
|
implementation.
|
|
|
|
\i \l t12/cannon.h contains the CannonField class
|
|
|
|
definition.
|
|
|
|
\i \l t12/cannon.cpp contains the CannonField
|
|
|
|
implementation.
|
|
|
|
\i \l t12/main.cpp contains MyWidget and main.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t12/lcdrange.h
|
|
|
|
|
|
|
|
The LCDRange now has a text label.
|
|
|
|
|
|
|
|
\quotefile t12/lcdrange.h
|
|
|
|
|
|
|
|
\skipto QLabel
|
|
|
|
\printline QLabel
|
|
|
|
|
|
|
|
We name declare QLabel because we want to use a pointer to it in the class
|
|
|
|
definition.
|
|
|
|
|
|
|
|
\skipto class
|
|
|
|
\printuntil parent=0
|
|
|
|
\printline parent=0
|
|
|
|
\printline name=0
|
|
|
|
|
|
|
|
We have added a new constructor that sets the label text in addition to
|
|
|
|
the parent and name.
|
|
|
|
|
|
|
|
\skipto text
|
|
|
|
\printline text
|
|
|
|
|
|
|
|
This function returns the label text.
|
|
|
|
|
|
|
|
\skipto setText
|
|
|
|
\printline setText
|
|
|
|
|
|
|
|
This slot sets the label text.
|
|
|
|
|
|
|
|
\skipto private
|
|
|
|
\printuntil init
|
|
|
|
|
|
|
|
Because we now have two constructors, we have chosen to put the common
|
|
|
|
initialization in the private init() function.
|
|
|
|
|
|
|
|
\skipto QLabel
|
|
|
|
\printline label
|
|
|
|
|
|
|
|
We also have a new private variable: a QLabel. QLabel is one of Qt's
|
|
|
|
standard widgets and can show a text or a pixmap with or without a
|
|
|
|
frame.
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t12/lcdrange.cpp
|
|
|
|
|
|
|
|
\quotefile t12/lcdrange.cpp
|
|
|
|
|
|
|
|
\skipto qlabel
|
|
|
|
\printline include
|
|
|
|
|
|
|
|
Here we include the QLabel class definition.
|
|
|
|
|
|
|
|
\skipto ::LCDRange
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This constructor calls the init() function, which contains the common
|
|
|
|
initialization code.
|
|
|
|
|
|
|
|
\skipto ::LCDRange
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This constructor first calls init() and then sets the label text.
|
|
|
|
|
|
|
|
\skipto ::init
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
The setup of \c lcd and \c slider is the same as in the previous
|
|
|
|
chapter. Next we create a QLabel and tell it to align the contents
|
|
|
|
centered (both vertically and horizontally). The connect() statements
|
|
|
|
have also been taken from the previous chapter.
|
|
|
|
|
|
|
|
\skipto ::text
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This function returns the label text.
|
|
|
|
|
|
|
|
\skipto ::setText
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This function sets the label text.
|
|
|
|
|
|
|
|
\section2 \l t12/cannon.h
|
|
|
|
|
|
|
|
The CannonField now has two new signals: hit() and missed(). In addition
|
|
|
|
it contains a target.
|
|
|
|
|
|
|
|
\quotefile t12/cannon.h
|
|
|
|
|
|
|
|
\skipto slots
|
|
|
|
\skipto newTarget
|
|
|
|
\printline newTarget
|
|
|
|
|
|
|
|
This slot creates a target at a new position.
|
|
|
|
|
|
|
|
\skipto signals
|
|
|
|
\printuntil missed
|
|
|
|
|
|
|
|
The hit() signal is emitted when a shot hits the target. The missed()
|
|
|
|
signal is emitted when the shot moves beyond the right or bottom edge
|
|
|
|
of the widget (i.e., it is certain that it has not and will not
|
|
|
|
hit the target).
|
|
|
|
|
|
|
|
\skipto paintTarget
|
|
|
|
\printline paintTarget
|
|
|
|
|
|
|
|
This private function paints the target.
|
|
|
|
|
|
|
|
\skipto targetRect
|
|
|
|
\printline targetRect
|
|
|
|
|
|
|
|
This private function returns the enclosing rectangle of the target.
|
|
|
|
|
|
|
|
\skipto target
|
|
|
|
\printline target
|
|
|
|
|
|
|
|
This private variable contains the center point of the target.
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t12/cannon.cpp
|
|
|
|
|
|
|
|
\quotefile t12/cannon.cpp
|
|
|
|
|
|
|
|
\skipto qdatetime
|
|
|
|
\printline qdatetime
|
|
|
|
|
|
|
|
We include the QDate, QTime, and QDateTime class definitions.
|
|
|
|
|
|
|
|
\skipto stdlib
|
|
|
|
\printline stdlib
|
|
|
|
|
|
|
|
We include the stdlib library because we need the rand() function.
|
|
|
|
|
|
|
|
\skipto newTarget
|
|
|
|
\printline newTarget
|
|
|
|
|
|
|
|
This line has been added to the constructor. It creates a "random"
|
|
|
|
position for the target. In fact, the newTarget() function will try
|
|
|
|
to paint the target. Because we are in a constructor, the CannonField
|
|
|
|
widget is invisible. Qt guarantees that no harm is done when calling
|
|
|
|
repaint() on a hidden widget.
|
|
|
|
|
|
|
|
\skipto ::newTarget
|
|
|
|
\printuntil repaint
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
This private function creates a target center point at a new "random"
|
|
|
|
position.
|
|
|
|
|
|
|
|
We use the rand() function to fetch random integers. The rand() function
|
|
|
|
normally returns the same series of numbers each time you run a program.
|
|
|
|
This would make the target appear at the same position every time. To
|
|
|
|
avoid this, we must set a random seed the first time this function is
|
|
|
|
called. The random seed must also be random in order to avoid equal random
|
|
|
|
number series. The solution is to use the number of seconds that have
|
|
|
|
passed since midnight as a pseudo-random value.
|
|
|
|
|
|
|
|
First we create a static bool local variable. A static variable like
|
|
|
|
this one is guaranteed to keep its value between calls to the function.
|
|
|
|
|
|
|
|
The \c if test will succeed only the first time this function is called
|
|
|
|
because we set \c first_time to FALSE inside the \c if block.
|
|
|
|
|
|
|
|
Then we create the QTime object \c midnight, which represents the time
|
|
|
|
00:00:00. Next we fetch the number of seconds from midnight until
|
|
|
|
now and use it as a random seed. See the documentation for \l QDate,
|
|
|
|
\l QTime, and \l QDateTime for more information.
|
|
|
|
|
|
|
|
Finally we calculate the target's center point. We keep it within
|
|
|
|
the rectangle (x=200, y=35, width=190, height=255), (i.e., the
|
|
|
|
possible x and y values are x = 200..389 and y = 35..289) in a
|
|
|
|
coordinate system where we put y position 0 at the bottom edge of the
|
|
|
|
widget and let y values increase upwards X is as normal, with 0 at
|
|
|
|
the left edge and with x values increasing to the right.
|
|
|
|
|
|
|
|
By experimentation we have found this to always be in reach of the shot.
|
|
|
|
|
|
|
|
Note that rand() returns a random integer >= 0.
|
|
|
|
|
|
|
|
\skipto ::moveShot
|
|
|
|
\printuntil QRect
|
|
|
|
|
|
|
|
This part of the timer event has not changed from the previous chapter.
|
|
|
|
|
|
|
|
\printuntil hit
|
|
|
|
|
|
|
|
This \c if statement checks whether the shot rectangle intersects the
|
|
|
|
target rectangle. If it does, the shot has hit the target (ouch!).
|
|
|
|
We stop the shoot timer and emit the hit() signal to tell the outside
|
|
|
|
world that a target was destroyed, and return.
|
|
|
|
|
|
|
|
Note that we could have created a new target on the spot, but because the
|
|
|
|
CannonField is a component we leave such decisions to the user of the
|
|
|
|
component.
|
|
|
|
|
|
|
|
\printuntil missed
|
|
|
|
|
|
|
|
This \c if statement is the same as in the previous chapter, except that
|
|
|
|
it now emits the missed() signal to tell the outside world about the
|
|
|
|
failure.
|
|
|
|
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
And the rest of the function is as before.
|
|
|
|
|
|
|
|
CannonField::paintEvent() is as before, except that this has been
|
|
|
|
added:
|
|
|
|
|
|
|
|
\skipto ::paintEvent
|
|
|
|
\skipto targetRect
|
|
|
|
\printline updateR
|
|
|
|
\printline paintTarget
|
|
|
|
|
|
|
|
These two lines make sure that the target is also painted when necessary.
|
|
|
|
|
|
|
|
\skipto ::paintTarget
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This private function paints the target; a rectangle filled with red and
|
|
|
|
with a black outline.
|
|
|
|
|
|
|
|
\skipto ::targetRect
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This private function returns the enclosing rectangle of the target.
|
|
|
|
Remember from newTarget() that the \c target point uses y coordinate 0 at
|
|
|
|
the bottom of the widget. We calculate the point in widget coordinates
|
|
|
|
before we call \l QRect::moveCenter().
|
|
|
|
|
|
|
|
The reason we have chosen this coordinate mapping is to fix the distance
|
|
|
|
between the target and the bottom of the widget. Remember that the widget
|
|
|
|
can be resized by the user or the program at any time.
|
|
|
|
|
|
|
|
\section2 \l t12/main.cpp
|
|
|
|
|
|
|
|
\quotefile t12/main.cpp
|
|
|
|
|
|
|
|
There are no new members in the MyWidget class, but we have slightly
|
|
|
|
changed the constructor to set the new LCDRange text labels.
|
|
|
|
|
|
|
|
\skipto ::MyWidget
|
|
|
|
\skipto angle
|
|
|
|
\printline ANGLE
|
|
|
|
|
|
|
|
We set the angle text label to "ANGLE".
|
|
|
|
|
|
|
|
|
|
|
|
\skipto force
|
|
|
|
\printline FORCE
|
|
|
|
|
|
|
|
We set the force text label to "FORCE".
|
|
|
|
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
The LCDRange widgets look a bit strange - the built-in layout
|
|
|
|
management in QVBox gives the labels too much space and the rest not
|
|
|
|
enough. We'll fix that in the next chapter.
|
|
|
|
|
|
|
|
(See \link tutorial1-07.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Make a cheat button that, when pressed, makes the CannonField display
|
|
|
|
the shot trajectory for five seconds.
|
|
|
|
|
|
|
|
If you did the "round shot" exercise from the previous chapter, try
|
|
|
|
changing the shotRect() to a shotRegion() that returns a \l QRegion so
|
|
|
|
you can have really accurate collision detection.
|
|
|
|
|
|
|
|
Make a moving target.
|
|
|
|
|
|
|
|
Make sure that the target is always created entirely on-screen.
|
|
|
|
|
|
|
|
Make sure that the widget cannot be resized so that the target isn't
|
|
|
|
visible. Hint: \l QWidget::setMinimumSize() is your friend.
|
|
|
|
|
|
|
|
Not easy; make it possible to have several shots in the air at the
|
|
|
|
same time. Hint: make a Shot object.
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-13.html Chapter 13.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-11.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-13.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file t13/lcdrange.h */
|
|
|
|
/*! \file t13/lcdrange.cpp */
|
|
|
|
/*! \file t13/cannon.h */
|
|
|
|
/*! \file t13/cannon.cpp */
|
|
|
|
/*! \file t13/gamebrd.h */
|
|
|
|
/*! \file t13/gamebrd.cpp */
|
|
|
|
/*! \file t13/main.cpp */
|
|
|
|
|
|
|
|
/*! \page tutorial1-13.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 13: Game Over
|
|
|
|
|
|
|
|
\img t13.png Screenshot of tutorial thirteen
|
|
|
|
|
|
|
|
In this example we start to approach a real playable game with a
|
|
|
|
score. We give MyWidget a new name (GameBoard) and add some slots.
|
|
|
|
|
|
|
|
We put the definition in gamebrd.h and the implementation in gamebrd.cpp.
|
|
|
|
|
|
|
|
The CannonField now has a game over state.
|
|
|
|
|
|
|
|
The layout problems in LCDRange are fixed.
|
|
|
|
|
|
|
|
\list
|
|
|
|
\i \l t13/lcdrange.h contains the LCDRange
|
|
|
|
class definition.
|
|
|
|
\i \l t13/lcdrange.cpp contains the LCDRange
|
|
|
|
implementation.
|
|
|
|
\i \l t13/cannon.h contains the CannonField class
|
|
|
|
definition
|
|
|
|
\i \l t13/cannon.cpp contains the CannonField
|
|
|
|
implementation.
|
|
|
|
\i \l t13/gamebrd.h contains the GameBoard
|
|
|
|
class definition.
|
|
|
|
\i \l t13/gamebrd.cpp contains the GameBoard
|
|
|
|
implementation.
|
|
|
|
\i \l t13/main.cpp contains MyWidget and main.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t13/lcdrange.h
|
|
|
|
|
|
|
|
\quotefile t13/lcdrange.h
|
|
|
|
|
|
|
|
\skipto include
|
|
|
|
\printuntil QWidget
|
|
|
|
|
|
|
|
We inherit QWidget rather than QVBox. QVBox is very easy to use, but
|
|
|
|
again it showed its limitations so we switch to the more powerful and
|
|
|
|
slightly harder to use QVBoxLayout. (As you remember, QVBoxLayout is
|
|
|
|
not a widget, it manages one.)
|
|
|
|
|
|
|
|
\section2 \l t13/lcdrange.cpp
|
|
|
|
|
|
|
|
\quotefile t13/lcdrange.cpp
|
|
|
|
|
|
|
|
\skipto layout
|
|
|
|
\printline layout
|
|
|
|
|
|
|
|
We need to include qlayout.h now to get the other layout management
|
|
|
|
API.
|
|
|
|
|
|
|
|
\printline LCDRange
|
|
|
|
\printline QWidget
|
|
|
|
|
|
|
|
We inherit QWidget in the usual way.
|
|
|
|
|
|
|
|
The other constructor has the same change. init() is unchanged,
|
|
|
|
except that we've added some lines at the end:
|
|
|
|
|
|
|
|
\skipto QVBoxLayout
|
|
|
|
\printline QVBoxLayout
|
|
|
|
|
|
|
|
We create a QVBoxLayout with all the default values, managing this
|
|
|
|
widget's children.
|
|
|
|
|
|
|
|
\printline addWidget
|
|
|
|
|
|
|
|
At the top we add the QLCDNumber with a non-zero stretch.
|
|
|
|
|
|
|
|
\printline addWidget
|
|
|
|
\printline addWidget
|
|
|
|
|
|
|
|
Then we add the other two, both with the default zero stretch.
|
|
|
|
|
|
|
|
This stretch control is something QVBoxLayout (and QHBoxLayout, and
|
|
|
|
QGridLayout) offers but classes like QVBox do not. In this case
|
|
|
|
we're saying that the QLCDNumber should stretch and the others should
|
|
|
|
not.
|
|
|
|
|
|
|
|
\section2 \l t13/cannon.h
|
|
|
|
|
|
|
|
The CannonField now has a game over state and a few new functions.
|
|
|
|
|
|
|
|
\quotefile t13/cannon.h
|
|
|
|
|
|
|
|
\skipto gameOver
|
|
|
|
\printline gameOver
|
|
|
|
|
|
|
|
This function returns TRUE if the game is over or FALSE if a game
|
|
|
|
is going on.
|
|
|
|
|
|
|
|
\skipto setGameOver
|
|
|
|
\printuntil restartGame
|
|
|
|
|
|
|
|
Here are two new slots: setGameOver() and restartGame().
|
|
|
|
|
|
|
|
\skipto canShoot
|
|
|
|
\printline canShoot
|
|
|
|
|
|
|
|
This new signal indicates that the CannonField is in a state where the
|
|
|
|
shoot() slot makes sense. We'll use it below to enable/disable the
|
|
|
|
Shoot button.
|
|
|
|
|
|
|
|
\skipto gameEnded
|
|
|
|
\printline gameEnded
|
|
|
|
|
|
|
|
This private variable contains the game state. TRUE means that the
|
|
|
|
game is over, and FALSE means that a game is going on.
|
|
|
|
|
|
|
|
\section2 \l t13/cannon.cpp
|
|
|
|
|
|
|
|
\quotefile t13/cannon.cpp
|
|
|
|
|
|
|
|
\skipto ::CannonField
|
|
|
|
\skipto gameEnded
|
|
|
|
\printline gameEnded
|
|
|
|
|
|
|
|
This line has been added to the constructor. Initially, the game is not
|
|
|
|
over (luckily for the player :-).
|
|
|
|
|
|
|
|
\skipto ::shoot
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
We added a new isShooting() function, so shoot() uses it instead of
|
|
|
|
testing directly. Also, shoot tells the world that the CannonField
|
|
|
|
cannot shoot now.
|
|
|
|
|
|
|
|
\skipto ::setGameOver
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This slot ends the game. It must be called from outside CannonField,
|
|
|
|
because this widget does not know when to end the game. This is an
|
|
|
|
important design principle in component programming. We choose to
|
|
|
|
make the component as flexible as possible to make it usable with
|
|
|
|
different rules (for example, a multi-player version of this in which the
|
|
|
|
first player to hit ten times wins could use the CannonField unchanged).
|
|
|
|
|
|
|
|
If the game has already been ended we return immediately. If a game is
|
|
|
|
going on we stop the shot, set the game over flag, and repaint the entire
|
|
|
|
widget.
|
|
|
|
|
|
|
|
\skipto ::restartGame
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This slot starts a new game. If a shot is in the air, we stop shooting.
|
|
|
|
We then reset the \c gameEnded variable and repaint the widget.
|
|
|
|
|
|
|
|
moveShot() too emits the new canShoot(TRUE) signal at the same time as
|
|
|
|
either hit() or miss().
|
|
|
|
|
|
|
|
Modifications in CannonField::paintEvent():
|
|
|
|
|
|
|
|
\skipto ::paintEvent
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
The paint event has been enhanced to display the text "Game Over" if
|
|
|
|
the game is over, i.e., \c gameEnded is TRUE. We don't bother to
|
|
|
|
check the update rectangle here because speed is not critical when
|
|
|
|
the game is over.
|
|
|
|
|
|
|
|
To draw the text we first set a black pen; the pen color is used
|
|
|
|
when drawing text. Next we choose a 48 point bold font from the
|
|
|
|
Courier family. Finally we draw the text centered in the widget's
|
|
|
|
rectangle. Unfortunately, on some systems (especially X servers with
|
|
|
|
Unicode fonts) it can take a while to load such a large font. Because
|
|
|
|
Qt caches fonts, you will notice this only the first time the font is
|
|
|
|
used.
|
|
|
|
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
We draw the shot only when shooting and the target only when playing
|
|
|
|
(that is, when the game is not ended).
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t13/gamebrd.h
|
|
|
|
|
|
|
|
This file is new. It contains the definition of the GameBoard class,
|
|
|
|
which was last seen as MyWidget.
|
|
|
|
|
|
|
|
\quotefile t13/gamebrd.h
|
|
|
|
|
|
|
|
\skipto include
|
|
|
|
\skipto class
|
|
|
|
\printuntil };
|
|
|
|
|
|
|
|
We have now added four slots. These are protected and are used internally.
|
|
|
|
We have also added two QLCDNumbers (\c hits and \c shotsLeft) which display
|
|
|
|
the game status.
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t13/gamebrd.cpp
|
|
|
|
|
|
|
|
This file is new. It contains the implementation of the GameBoard
|
|
|
|
class, which was last seen as MyWidget.
|
|
|
|
|
|
|
|
\quotefile t13/gamebrd.cpp
|
|
|
|
|
|
|
|
We have made some changes in the GameBoard constructor.
|
|
|
|
|
|
|
|
\skipto ::GameBoard
|
|
|
|
\skipto cannonField
|
|
|
|
\printline cannonField
|
|
|
|
|
|
|
|
\c cannonField is now a member variable, so we carefully change the
|
|
|
|
constructor to use it. (The \e good programmers at Trolltech never
|
|
|
|
forget this, but I do. Caveat programmor - if "programmor" is Latin,
|
|
|
|
at least. Anyway, back to the code.)
|
|
|
|
|
|
|
|
\skipto hit
|
|
|
|
\printline connect
|
|
|
|
\printline hit
|
|
|
|
\printline connect
|
|
|
|
\printline missed
|
|
|
|
|
|
|
|
This time we want to do something when the shot has hit or missed the
|
|
|
|
target. Thus we connect the hit() and missed() signals of the
|
|
|
|
CannonField to two protected slots with the same names in this class.
|
|
|
|
|
|
|
|
\skipto shoot
|
|
|
|
\skipto connect
|
|
|
|
\printline fire
|
|
|
|
|
|
|
|
Previously we connected the Shoot button's clicked() signal directly
|
|
|
|
to the CannonField's shoot() slot. This time we want to keep track of
|
|
|
|
the number of shots fired, so we connect it to a protected slot in
|
|
|
|
this class instead.
|
|
|
|
|
|
|
|
Notice how easy it is to change the behavior of a program when you are
|
|
|
|
working with self-contained components.
|
|
|
|
|
|
|
|
\printline connect
|
|
|
|
\printline setEnabled
|
|
|
|
|
|
|
|
We also use the cannonField's canShoot() signal to enable or disable
|
|
|
|
the Shoot button appropriately.
|
|
|
|
|
|
|
|
\skipto restart
|
|
|
|
\printuntil connect
|
|
|
|
|
|
|
|
We create, set up, and connect the New Game button as we have done
|
|
|
|
with the other buttons. Clicking this button will activate the
|
|
|
|
newGame() slot in this widget.
|
|
|
|
|
|
|
|
\printuntil shotsLeftL
|
|
|
|
\printline QLabel
|
|
|
|
|
|
|
|
We create four new widgets. Note that we don't bother to keep the
|
|
|
|
pointers to the QLabel widgets in the GameBoard class because there's
|
|
|
|
nothing much we want to do with them. Qt will delete them when the
|
|
|
|
GameBoard widget is destroyed, and the layout classes will resize them
|
|
|
|
appropriately.
|
|
|
|
|
|
|
|
\skipto QHBoxLayout
|
|
|
|
\printuntil addStretch
|
|
|
|
\printline addWidget
|
|
|
|
|
|
|
|
The number of widgets in the top-right cell is getting large. Once it
|
|
|
|
was empty; now it's full enough that we group together the layout
|
|
|
|
setting for better overview.
|
|
|
|
|
|
|
|
Notice that we let all the widgets have their preferred sizes, instead
|
|
|
|
putting the stretch just to the left of the New Game button.
|
|
|
|
|
|
|
|
\skipto newGame
|
|
|
|
\printline newGame
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
We're all done constructing the GameBoard, so we start it all using
|
|
|
|
newGame(). (NewGame() is a slot, but as we said, slots can be used as
|
|
|
|
ordinary functions, too.)
|
|
|
|
|
|
|
|
\skipto ::fire
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This function fires a shot. If the game is over or if there is a shot in the
|
|
|
|
air, we return immediately. We decrement the number of shots left and tell
|
|
|
|
the cannon to shoot.
|
|
|
|
|
|
|
|
\skipto ::hit
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This slot is activated when a shot has hit the target. We increment the
|
|
|
|
number of hits. If there are no shots left, the game is over. Otherwise,
|
|
|
|
we make the CannonField generate a new target.
|
|
|
|
|
|
|
|
\skipto ::missed
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This slot is activated when a shot has missed the target. If there are no
|
|
|
|
shots left, the game is over.
|
|
|
|
|
|
|
|
\skipto ::newGame
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This slot is activated when the user clicks the Restart button. It is
|
|
|
|
also called from the constructor. First it sets the number of shots
|
|
|
|
to 15. Note that this is the only place in the program where we set
|
|
|
|
the number of shots. Change it to whatever you like to change the
|
|
|
|
game rules. Next we reset the number of hits, restart the game, and
|
|
|
|
generate a new target.
|
|
|
|
|
|
|
|
\section2 \l t13/main.cpp
|
|
|
|
|
|
|
|
This file has just been on a diet. MyWidget is gone, and the only
|
|
|
|
thing left is the main() function, unchanged except for the name
|
|
|
|
change.
|
|
|
|
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
The cannon can shoot at a target; a new target is automatically created
|
|
|
|
when one has been hit.
|
|
|
|
|
|
|
|
Hits and shots left are displayed and the program keeps track of them.
|
|
|
|
The game can end, and there's a button to start a new game.
|
|
|
|
|
|
|
|
(See \link tutorial1-07.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Add a random wind factor and show it to the user.
|
|
|
|
|
|
|
|
Make some splatter effects when the shot hits the target.
|
|
|
|
|
|
|
|
Implement multiple targets.
|
|
|
|
|
|
|
|
You're now ready for \link tutorial1-14.html Chapter 14.\endlink
|
|
|
|
|
|
|
|
[\link tutorial1-12.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-14.html Next tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \file t14/lcdrange.h */
|
|
|
|
/*! \file t14/lcdrange.cpp */
|
|
|
|
/*! \file t14/cannon.h */
|
|
|
|
/*! \file t14/cannon.cpp */
|
|
|
|
/*! \file t14/gamebrd.h */
|
|
|
|
/*! \file t14/gamebrd.cpp */
|
|
|
|
/*! \file t14/main.cpp */
|
|
|
|
|
|
|
|
/*! \page tutorial1-14.html
|
|
|
|
|
|
|
|
\title Qt Tutorial - Chapter 14: Facing the Wall
|
|
|
|
|
|
|
|
\img t14.png Screenshot of tutorial fourteen
|
|
|
|
|
|
|
|
This is the final example: a complete game.
|
|
|
|
|
|
|
|
We add keyboard accelerators and introduce mouse events to CannonField. We
|
|
|
|
put a frame around the CannonField and add a barrier (wall) to make the
|
|
|
|
game more challenging.
|
|
|
|
|
|
|
|
\list
|
|
|
|
\i \l t14/lcdrange.h contains the LCDRange
|
|
|
|
class definition.
|
|
|
|
\i \l t14/lcdrange.cpp contains the LCDRange
|
|
|
|
implementation.
|
|
|
|
\i \l t14/cannon.h contains the CannonField class
|
|
|
|
definition.
|
|
|
|
\i \l t14/cannon.cpp contains the CannonField
|
|
|
|
implementation.
|
|
|
|
\i \l t14/gamebrd.h contains the GameBoard
|
|
|
|
class definition.
|
|
|
|
\i \l t14/gamebrd.cpp contains the GameBoard
|
|
|
|
implementation.
|
|
|
|
\i \l t14/main.cpp contains MyWidget and main.
|
|
|
|
\endlist
|
|
|
|
|
|
|
|
\section1 Line-by-line Walkthrough
|
|
|
|
|
|
|
|
\section2 \l t14/cannon.h
|
|
|
|
|
|
|
|
The CannonField can now receive mouse events to make the user aim the
|
|
|
|
barrel by clicking on it and dragging. CannonField also has a barrier
|
|
|
|
wall.
|
|
|
|
|
|
|
|
\quotefile t14/cannon.h
|
|
|
|
|
|
|
|
\skipto CannonField
|
|
|
|
\skipto protected
|
|
|
|
\printuntil mouseReleaseEvent
|
|
|
|
|
|
|
|
In addition to the familiar event handlers, CannonField implements
|
|
|
|
three mouse event handlers. The names say it all.
|
|
|
|
|
|
|
|
\skipto paintBarrier
|
|
|
|
\printline paintBarrier
|
|
|
|
|
|
|
|
This private function paints the barrier wall.
|
|
|
|
|
|
|
|
\skipto barrierRect
|
|
|
|
\printline barrierRect
|
|
|
|
|
|
|
|
This private function returns the enclosing rectangle of the barrier.
|
|
|
|
|
|
|
|
\skipto barrelHit
|
|
|
|
\printline barrelHit
|
|
|
|
|
|
|
|
This private function checks if a point is inside the barrel of the cannon.
|
|
|
|
|
|
|
|
\skipto barrelPressed
|
|
|
|
\printline barrelPressed
|
|
|
|
|
|
|
|
This private variable is TRUE if the user has pressed the mouse on the
|
|
|
|
barrel and not released it.
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t14/cannon.cpp
|
|
|
|
|
|
|
|
\quotefile t14/cannon.cpp
|
|
|
|
|
|
|
|
\skipto ::CannonField
|
|
|
|
\skipto barrelPressed
|
|
|
|
\printline barrelPressed
|
|
|
|
|
|
|
|
This line has been added to the constructor. Initially, the mouse is
|
|
|
|
not pressed on the barrel.
|
|
|
|
|
|
|
|
\skipto ::moveShot
|
|
|
|
\skipto else
|
|
|
|
\printuntil {
|
|
|
|
|
|
|
|
Now that we have a barrier, there are three ways to miss. We test for
|
|
|
|
the third, too.
|
|
|
|
|
|
|
|
\skipto ::mousePressEvent
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This is a Qt event handler. It is called when the user presses a
|
|
|
|
mouse button when the mouse cursor is over the widget.
|
|
|
|
|
|
|
|
If the event was not generated by the left mouse button, we return
|
|
|
|
immediately. Otherwise, we check if the position of the mouse cursor
|
|
|
|
is within the cannon's barrel. If it is, we set \c barrelPressed to
|
|
|
|
TRUE.
|
|
|
|
|
|
|
|
Notice that the pos() function returns a point in the widget's
|
|
|
|
coordinate system.
|
|
|
|
|
|
|
|
\skipto ::mouseMoveEvent
|
|
|
|
\printuntil setAngle
|
|
|
|
\printline }
|
|
|
|
|
|
|
|
This is another Qt event handler. It is called when the user already
|
|
|
|
has pressed the mouse button inside this widget and then moves/drags
|
|
|
|
the mouse. (You can make Qt send mouse move events even when no
|
|
|
|
buttons are pressed. See \l QWidget::setMouseTracking().)
|
|
|
|
|
|
|
|
This handler repositions the cannon's barrel according to the position of
|
|
|
|
the mouse cursor.
|
|
|
|
|
|
|
|
First, if the barrel is not pressed, we return. Next, we fetch the
|
|
|
|
mouse cursor's position. If the mouse cursor is to the left or below
|
|
|
|
the widget, we adjust the point to be inside the widget.
|
|
|
|
|
|
|
|
Then we calculate the angle between the bottom edge of the widget and
|
|
|
|
the imaginary line between the bottom-left corner of the widget and
|
|
|
|
the cursor position. Finally we set the cannon's angle to the new
|
|
|
|
value converted to degrees.
|
|
|
|
|
|
|
|
Remember that setAngle() redraws the cannon.
|
|
|
|
|
|
|
|
\skipto ::mouseReleaseEvent
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This Qt event handler is called whenever the user releases a mouse
|
|
|
|
button and it was pressed inside this widget.
|
|
|
|
|
|
|
|
If the left button is released, we can be sure that the barrel is no
|
|
|
|
longer pressed.
|
|
|
|
|
|
|
|
The paint event has two extra lines:
|
|
|
|
|
|
|
|
\skipto ::paintEvent
|
|
|
|
\skipto barrierRect
|
|
|
|
\printline barrierRect
|
|
|
|
\printline paintBarrier
|
|
|
|
|
|
|
|
paintBarrier() does the same sort of thing as paintShot(),
|
|
|
|
paintTarget(), and paintCannon().
|
|
|
|
|
|
|
|
\skipto ::paintBarrier
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This private function paints the barrier as a rectangle filled with
|
|
|
|
yellow and with a black outline.
|
|
|
|
|
|
|
|
\skipto ::barrierRect
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This private function returns the rectangle of the barrier. We fix
|
|
|
|
the bottom edge of the barrier to the bottom edge of the widget.
|
|
|
|
|
|
|
|
\skipto ::barrelHit
|
|
|
|
\printuntil }
|
|
|
|
|
|
|
|
This function returns TRUE if the point is in the barrel; otherwise it returns
|
|
|
|
FALSE.
|
|
|
|
|
|
|
|
Here we use the class \l QWMatrix. It is defined in the header file
|
|
|
|
qwmatrix.h, which is included by qpainter.h.
|
|
|
|
|
|
|
|
QWMatrix defines a coordinate system mapping. It can perform the same
|
|
|
|
transformations as the QPainter.
|
|
|
|
|
|
|
|
Here we perform the same transformation steps as we do when drawing
|
|
|
|
the barrel in the paintCannon() function. First we translate the
|
|
|
|
coordinate system and then we rotate it.
|
|
|
|
|
|
|
|
Now we need to check whether the point \c p (in widget coordinates) lies
|
|
|
|
inside the barrel. To do this, we invert the transformation matrix.
|
|
|
|
The inverted matrix performs the inverse transformation that we used
|
|
|
|
when drawing the barrel. We map the point \c p using the inverted
|
|
|
|
matrix and return TRUE if it is inside the original barrel rectangle.
|
|
|
|
|
|
|
|
|
|
|
|
\section2 \l t14/gamebrd.cpp
|
|
|
|
|
|
|
|
\quotefile t14/gamebrd.cpp
|
|
|
|
|
|
|
|
\skipto qaccel.h
|
|
|
|
\printline qaccel.h
|
|
|
|
|
|
|
|
We include the class definition of \l QAccel.
|
|
|
|
|
|
|
|
\skipto ::GameBoard
|
|
|
|
\skipto QVBox
|
|
|
|
\printline QVBox
|
|
|
|
\printline setFrameStyle
|
|
|
|
\printline cannonField
|
|
|
|
|
|
|
|
We create and set up a \l QVBox, set its frame style, and then create
|
|
|
|
\c CannonField as a child of that box. Because nothing else is in the
|
|
|
|
box, the effect is that the QVBox will put a frame around the
|
|
|
|
CannonField.
|
|
|
|
|
|
|
|
\skipto QAccel
|
|
|
|
\printline accel
|
|
|
|
\printline connectItem
|
|
|
|
\printline fire
|
|
|
|
\printline connectItem
|
|
|
|
\printline fire
|
|
|
|
|
|
|
|
Here we create and set up an accelerator. An accelerator is an object
|
|
|
|
that intercepts keyboard events to an application and calls slots if
|
|
|
|
certain keys are pressed. This mechanism is also called shortcut
|
|
|
|
keys. Note that an accelerator is a child of a widget and will be
|
|
|
|
destroyed when that widget is destroyed. QAccel is \e not a widget
|
|
|
|
and has no visible effect on its parent.
|
|
|
|
|
|
|
|
We define two shortcut keys. We want the slot fire() to be called
|
|
|
|
when the user presses Enter, and we want the application to quit when
|
|
|
|
key Ctrl+Q is pressed. Because Enter is sometimes Return and there
|
|
|
|
are even keyboards with \e both keys, we make both Enter and Return
|
|
|
|
invoke fire().
|
|
|
|
|
|
|
|
\printline connectItem
|
|
|
|
\printline quit
|
|
|
|
|
|
|
|
And then we set up Ctrl+Q to do the same thing as Alt+Q. Some
|
|
|
|
people are more used to Ctrl+Q (and anyway it shows how do do it).
|
|
|
|
|
|
|
|
CTRL, Key_Enter, Key_Return and Key_Q are all constants provided by
|
|
|
|
Qt. They're actually Qt::Key_Enter, etc., but practically all classes
|
|
|
|
inherit the \l Qt namespace class.
|
|
|
|
|
|
|
|
\printline QGridLayout
|
|
|
|
\printline addWidget
|
|
|
|
\printline addWidget
|
|
|
|
\printline setColStretch
|
|
|
|
|
|
|
|
We put \c box (the QVBox), not the CannonField, in the lower-right
|
|
|
|
cell.
|
|
|
|
|
|
|
|
\section1 Behavior
|
|
|
|
|
|
|
|
The cannon now shoots when you press Enter. You can also position the
|
|
|
|
cannon's angle using the mouse. The barrier makes it a little more
|
|
|
|
challenging to play the game. We also have a nice looking frame
|
|
|
|
around the CannonField.
|
|
|
|
|
|
|
|
(See \link tutorial1-07.html#compiling Compiling\endlink for how to create a
|
|
|
|
makefile and build the application.)
|
|
|
|
|
|
|
|
|
|
|
|
\section1 Exercises
|
|
|
|
|
|
|
|
Write a space invaders game.
|
|
|
|
|
|
|
|
(This exercise was first done by
|
|
|
|
\link mailto:igorr@ifi.uio.no Igor Rafienko\endlink. You can
|
|
|
|
\link http://www.stud.ifi.uio.no/~igorr/download.html
|
|
|
|
download his game\endlink.)
|
|
|
|
|
|
|
|
The new exercise is: Write a Breakout game.
|
|
|
|
|
|
|
|
Final exhortation: Go forth now and create \e {masterpieces of the
|
|
|
|
programming art!}
|
|
|
|
|
|
|
|
\omit
|
|
|
|
Cf. Chapter 27 of The TeXbook
|
|
|
|
\endomit
|
|
|
|
|
|
|
|
[\link tutorial1-13.html Previous tutorial\endlink]
|
|
|
|
[\link tutorial1-01.html First tutorial\endlink]
|
|
|
|
[\link tutorial.html Main tutorial page\endlink]
|
|
|
|
|
|
|
|
*/
|