From bd0f3345a938b35ce6a12f6150373b0955b8dd12 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Sun, 10 Jul 2011 15:24:15 -0500 Subject: Add Qt3 development HEAD version --- doc/html/tutorial1-08.html | 297 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 doc/html/tutorial1-08.html (limited to 'doc/html/tutorial1-08.html') diff --git a/doc/html/tutorial1-08.html b/doc/html/tutorial1-08.html new file mode 100644 index 0000000..beb1f66 --- /dev/null +++ b/doc/html/tutorial1-08.html @@ -0,0 +1,297 @@ + + + + + +Qt Tutorial - Chapter 8: Preparing for Battle + + + + + + + +
+ +Home + | +All Classes + | +Main Classes + | +Annotated + | +Grouped Classes + | +Functions +

Qt Tutorial - Chapter 8: Preparing for Battle

+ + +

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). +

+

Line-by-line Walkthrough +

+

t8/lcdrange.h +

+

This file is very similar to the lcdrange.h in Chapter 7. We have added +one slot: setRange(). +

+ +

        void setRange( int minVal, int maxVal );
+
+

We now add the possibility of setting the range of the LCDRange. +Until now, it has been fixed at 0..99. +

t8/lcdrange.cpp +

+

+ +

There is a change to the constructor (we'll discuss that later). +

    void LCDRange::setRange( int minVal, int maxVal )
+    {
+        if ( minVal < 0 || maxVal > 99 || minVal > maxVal ) {
+          qWarning( "LCDRange::setRange(%d,%d)\n"
+                   "\tRange must be 0..99\n"
+                   "\tand minVal must not be greater than maxVal",
+                   minVal, maxVal );
+          return;
+        }
+        slider->setRange( minVal, maxVal );
+    }
+
+

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 minVal and 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 stderr. If you want, you can install your own handler +function using ::qInstallMsgHandler(). +

t8/cannon.h +

+

CannonField is a new custom widget that knows how to display itself. +

+ +

    class CannonField : public QWidget
+    {
+        Q_OBJECT
+    public:
+        CannonField( QWidget *parent=0, const char *name=0 );
+
+

CannonField inherits QWidget, and we use the same idiom as for LCDRange. +

        int angle() const { return ang; }
+        QSizePolicy sizePolicy() const;
+
+    public slots:
+        void setAngle( int degrees );
+
+    signals:
+        void angleChanged( int );
+
+

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. +

    protected:
+        void paintEvent( QPaintEvent * );
+
+

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). +

t8/cannon.cpp +

+

+ +

    CannonField::CannonField( QWidget *parent, const char *name )
+            : QWidget( parent, name )
+    {
+
+

Again, we use the same idiom as for LCDRange in the previous chapter. +

        ang = 45;
+        setPalette( QPalette( QColor( 250, 250, 200) ) );
+    }
+
+

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.) +

    void CannonField::setAngle( int degrees )
+    {
+        if ( degrees < 5 )
+            degrees = 5;
+        if ( degrees > 70 )
+            degrees = 70;
+        if ( ang == degrees )
+            return;
+        ang = degrees;
+        repaint();
+        emit angleChanged( ang );
+    }
+
+

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 really has changed. +

Then we set the new angle value and repaint our widget. The 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 emit keyword is unique to Qt and +not regular C++ syntax. In fact, it is a macro. +

    void CannonField::paintEvent( QPaintEvent * )
+    {
+        QString s = "Angle = " + QString::number( ang );
+        QPainter p( this );
+        p.drawText( 200, 200, s );
+    }
+
+

This is our first attempt to write a paint event handler. The event +argument contains a description of the paint event. 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. +

t8/main.cpp +

+

+ +

    #include "cannon.h"
+
+

We include our new class. +

    class MyWidget: public QWidget
+    {
+    public:
+        MyWidget( QWidget *parent=0, const char *name=0 );
+    };
+
+

This time we include a single LCDRange and a CannonField in our top-level +widget. +

        LCDRange *angle = new LCDRange( this, "angle" );
+
+

In the constructor, we create and set up our LCDRange. +

        angle->setRange( 5, 70 );
+
+

We set the LCDRange to accept ranges from 5 to 70 degrees. +

        CannonField *cannonField
+            = new CannonField( this, "cannonField" );
+
+

We create our CannonField. +

        connect( angle, SIGNAL(valueChanged(int)),
+                 cannonField, SLOT(setAngle(int)) );
+        connect( cannonField, SIGNAL(angleChanged(int)),
+                 angle, SLOT(setValue(int)) );
+
+

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. +

        QGridLayout *grid = new QGridLayout( this, 2, 2, 10 );
+        //2x2, 10 pixel border
+
+

So far we have used the no-assembly-required 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 any widget. +

As the comment indicates, we create a two-by-two array with ten pixel +borders. (The constructor for QGridLayout can be a little cryptic, +so it's good to put in such comments.) +

        grid->addWidget( quit, 0, 0 );
+
+

We add the Quit button in the top-left cell of the grid: 0, 0. +

        grid->addWidget( angle, 1, 0, Qt::AlignTop );
+
+

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.) +

        grid->addWidget( cannonField, 1, 1 );
+
+

We put the CannonField object in the bottom-right cell. (The top- +right cell is empty.) +

        grid->setColStretch( 1, 10 );
+
+

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. +

        angle->setValue( 60 );
+
+

We set an initial angle value. Note that this will trigger the +connection from LCDRange to CannonField. +

        angle->setFocus();
+
+

Our last action is to set 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: +

+ +

        setFocusProxy( slider );
+
+

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. +

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 Compiling for how to create a +makefile and build the application.) +

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 Chapter 9. +

[Previous tutorial] +[Next tutorial] +[Main tutorial page] +

+ +


+ +
Copyright © 2007 +TrolltechTrademarks +
Qt 3.3.8
+
+ -- cgit v1.2.3