You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tqt3/doc/tutorial.doc

2652 lines
78 KiB

/****************************************************************************
**
** 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]
*/