C.4. Chapter 4

C.4.1. Exercises

C.4.1.1. Modify the method KTicTacToe::processClicks() so that the user is required to take turns between X and O.
C.4.1.2. Reimplement KXOSquare::sizeHint() and use this method appropriately in the constructor of KTicTacToe. Compare what happens now when you resize the window to what happened before.
C.4.1.3. What's the difference between QPen and QBrush? Examine the KDisc code and consult the Qt documentation.
C.4.1.4. Get to know QPainter. Construct different QPens and QBrushes in KDisc. Draw figures other than a disc.
C.4.1.5. Try using mousePressEvent() instead of mouseReleaseEvent() in KDisc. Can you tell the difference? Which feels right?

C.4.1.1. Modify the method KTicTacToe::processClicks() so that the user is required to take turns between X and O.

Listing C.7 highlights the modified ktictactoe.cpp file.


Example C.7. Modified ktictactoe.cpp File

   1 
   2 #include <qlayout.h>
   3 #include <qlabel.h>
   4 
   5 #include "ktictactoe.moc"
   6 
   7 KTicTacToe::KTicTacToe (QWidget *parent, const char *name) :
   8   QWidget (parent, name)
   9 {
  10   int row, col;
  11 
  12   QGridLayout *layout = new QGridLayout (this, 4, 3);
  13 
  14   const int rowlabel0 = 0, rowlabel1 = 0, collabel0 = 0, collabel1 = 2,
  15     rowsquares0 = 1, rowsquares1 = 4, colsquares0 = 0, colsquares1 = 3;
  16   for (row=rowsquares0; row<rowsquares1; row++)
  17     for (col=colsquares0; col<colsquares1; col++)
  18       {
  19         KXOSquare *kxosquare = new KXOSquare (this);
  20         layout->addWidget (kxosquare, row, col);
  21         connect ( kxosquare,
  22                  SIGNAL (changeRequest (KXOSquare *, KXOSquare::State)),
  23                  SLOT (processClicks (KXOSquare *, KXOSquare::State)) );
  24       }
  25 
  26   QLabel *label = new QLabel ("Tic-Tac-Toe, this);
  27   label->setAlignment (Qt::AlignCenter);
  28   layout->addMultiCellWidget (label,
  29                              rowlabel0, rowlabel1,
  30                              collabel0, collabel1);
  31 }
  32 
  33 void
  34 KTicTacToe::processClicks (KXOSquare *square, KXOSquare::State state)
  35 {
  36   //Chapter 4, Exercise 1
  37   if (state!=previousstate)
  38     {
  39       square->newState (state);
  40       previousstate=state;
  41     }
  42 }
  43 

C.4.1.2. Reimplement KXOSquare::sizeHint() and use this method appropriately in the constructor of KTicTacToe. Compare what happens now when you resize the window to what happened before.

Listings C.8–C.10 demonstrate this.


Example C.8. Modified ktictactoe.h Method

   1 
   2 #ifndef __KTICTACTOE_H__
   3 #define __KTICTACTOE_H__
   4 
   5 #include <qarray.h>
   6 #include <qwidget.h>
   7 
   8 #include "kxosquare.h"
   9 
  10 /**
  11  * KTicTacToe
  12  * Draw and manage a Tic-Tac-Toe board using KXOSquare.
  13  **/
  14 class KTicTacToe : public QWidget
  15 {
  16  Q_OBJECT
  17 
  18  public:
  19   /**
  20    * Create an empty game board.
  21    **/
  22   KTicTacToe (QWidget *parent, const char *name=0);
  23 
  24 
  25  protected slots:
  26    /**
  27     * Process user input.
  28     **/
  29    void processClicks (KXOSquare *, KXOSquare::State);
  30 };
  31 
  32 #endif
  33                 Modified ktictactoe.cpp
  34 #include <qlayout.h>
  35 #include <qlabel.h>
  36 #include "ktictactoe.moc"
  37 
  38 KTicTacToe::KTicTacToe (QWidget *parent, const char *name) :
  39   QWidget (parent, name)
  40 {
  41   int row, col;
  42 
  43   QGridLayout *layout = new QGridLayout (this, 4, 3);
  44 
  45   const int rowlabel0 = 0, rowlabel1 = 0, collabel0 = 0, collabel1 = 2,
  46     rowsquares0 = 1, rowsquares1 = 4, colsquares0 = 0, colsquares1 = 3;
  47 
  48   for (row=rowsquares0; row<rowsquares1; row++)
  49     for (col=colsquares0; col<colsquares1; col++)
  50       {
  51         KXOSquare *kxosquare = new KXOSquare (this);
  52         //Chapter 4, Exercise 2
  53         kxosquare->setMinimumSize (kxosquare->sizeHint());
  54 
  55         layout->addWidget (kxosquare, row, col);
  56         connect ( kxosquare,
  57                  SIGNAL (changeRequest (KXOSquare *, KXOSquare::State)),
  58                  SLOT (processClicks (KXOSquare *, KXOSquare::State)) );
  59       }
  60 
  61   QLabel *label = new QLabel ("Tic-Tac-Toe", this);
  62   label->setAlignment (Qt::AlignCenter);
  63   layout->addMultiCellWidget (label,
  64                             rowlabel0, rowlabel1,
  65                             collabel0, collabel1);
  66 }
  67 
  68 
  69 void
  70 KTicTacToe::processClicks (KXOSquare *square, KXOSquare::State state)
  71 {
  72   //In this simple example, just pass along the click to the appropriate
  73   // square.
  74   square->newState (state);
  75 }
  76 


Example C.9. Modified kxosquare.h Method

   1 
   2 #ifndef __KXOSQUARE_H__
   3 #define __KXOSQUARE_H__
   4 
   5 
   6 #include <qwidget.h>
   7 #include <qsize.h>
   8 
   9 /**
  10  * KXOSquare
  11  * Draws a square in one of three states: empty, with an X inside,
  12  *  or with an O inside.
  13  **/
  14 class KXOSquare : public QWidget
  15 {
  16  Q_OBJECT
  17 
  18  public:
  19   enum State {None=0, X=1, O=2};
  20 
  21   /**
  22    * Create the widget.
  23    **/
  24   KXOSquare (QWidget *parent, const char *name=0);
  25 
  26   /**
  27    * Chapter 4, Exercise 2
  28    * Return a recommended size for this widget.
  29    **/
  30   QSize sizeHint() const;
  31 
  32   public slots:
  33   /**
  34    * Change the state of the widget to <i>state</i>.
  35    **/
  36     void newState (State state);
  37 
  38  signals:
  39   /**
  40    * The user has requested that the state be changed to <i>state</i>
  41    *  by clicking on the square.
  42    **/
  43     void changeRequest (KXOSquare *, KXOSquare::State state);
  44 
  45  protected:
  46     /**
  47      * Draw the widget.
  48      **/
  49     void paintEvent (QPaintEvent *);
  50 
  51     /**
  52      * Process mouse clicks.
  53      **/
  54     void mousePressEvent (QMouseEvent *);
  55 
  56  private:
  57   State thestate;
  58 };
  59 
  60 #endif
  61 


Example C.10. Modified kxosquare.cpp Method

   1 
   2 #ifndef __KXOSQUARE_H__
   3 #define __KXOSQUARE_H__
   4 
   5 
   6 #include <qwidget.h>
   7 #include <qsize.h>
   8 
   9 /**
  10  * KXOSquare
  11  * Draws a square in one of three state: empty, with an X inside,
  12  *  or with an O inside.
  13  **/
  14 class KXOSquare : public QWidget
  15 {
  16  Q_OBJECT
  17 
  18  public:
  19   enum State {None=0, X=1, O=2};
  20 
  21   /**
  22    * Create the widget.
  23    **/
  24   KXOSquare (QWidget *parent, const char *name=0);
  25 
  26   /**
  27    * Chapter 4, Exercise 2
  28    * Return a recommended size for this widget.
  29    **/
  30   QSize sizeHint() const;
  31 
  32   public slots:
  33   /**
  34    * Change the state of the widget to <i>state</i>.
  35    **/
  36     void newState (State state);
  37 
  38  signals:
  39   /**
  40    * The user has requested that the state be changed to <i>state</i>
  41    *  by clicking on the square.
  42    **/
  43     void changeRequest (KXOSquare *, KXOSquare::State state);
  44 
  45  protected:
  46     /**
  47      * Draw the widget.
  48      **/
  49     void paintEvent (QPaintEvent *);
  50 
  51     /**
  52      * Process mouse clicks.
  53      **/
  54     void mousePressEvent (QMouseEvent *);
  55 
  56  private:
  57   State thestate;
  58 };
  59 
  60 #endif
  61 

C.4.1.4. Get to know QPainter. Construct different QPens and QBrushes in KDisc. Draw figures other than a disc.

Listing C.11 lists the modified methods from kdisc.cpp.


Example C.11. Modified Methods from kdisc.cpp

   1 
   2 void
   3 KDisc::paintEvent (QPaintEvent *)
   4 {
   5   QPainter painter (this);
   6 
   7   //Chapter 4, Exercise 4
   8 
   9   painter.setPen ( QPen (QColor (200, 100, 0), 1) );
  10   painter.setBrush ( QBrush (Qt::green, Qt::SolidPattern) );
  11 
  12   painter.drawPie (discposition.x(), discposition.y(),
  13                       45, 45, 0, 4760);
  14 }
  15 void
  16 KDisc::mouseMoveEvent (QMouseEvent *qmouseevent)
  17 {
  18 
  19   if (qmouseevent->state()==Qt::LeftButton)
  20     {
  21       //Chapter 4, Exercise 4
  22       QPoint qpoint = qmouseevent->pos();
  23       qpoint.setX(qpoint.x() - 45/2);
  24       qpoint.setY(qpoint.y() - 45/2);
  25       discposition = qpoint;
  26       update();
  27     }
  28 
  29 }
  30 

C.4.1.5. Try using mousePressEvent() instead of mouseReleaseEvent() in KDisc. Can you tell the difference? Which feels right?