Time for action – Creating a game board widget
Locate the tictactoe folder in the project tree (it's the top-level entry corresponding to our whole project), open its context menu, and select Add New... Select C++ in the left list and C++ Class in the central list. Click on the Choose button, input TicTacToeWidget in the Class name field, and select QWidget in the Base class drop-down list. Click on Next and Finish. Qt Creator will create header and source files for our new class and add them to the project.
Open the tictactoewidget.h file in Creator and update it by adding the highlighted code:
#ifndef TICTACTOEWIDGET_H #define TICTACTOEWIDGET_H #include <QWidget> class TicTacToeWidget : public QWidget { Q_OBJECT public: TicTacToeWidget(QWidget *parent = nullptr); ~TicTacToeWidget(); private: QVector<QPushButton*> m_board; }; #endif // TICTACTOEWIDGET_H
Our additions create a QVector object (a container similar to std::vector) that can hold pointers to instances of the QPushButton class, which is the most commonly used button class in Qt. We have to include the Qt header containing the QPushButton declaration. Qt Creator can help us do this quickly. Set the text cursor on QPushButton, press Alt + Enter, and select Add #include <QPushButton>. The include directive will appear at the beginning of the file. As you may have noted, each Qt class is declared in the header file that is called exactly the same as the class itself.
The next step is to create all the buttons and use a layout to manage their geometries. Switch to the tictactoewidget.cpp file and locate the constructor.
First, let's create a layout that will hold our buttons:
QGridLayout *gridLayout = new QGridLayout(this);
By passing the this pointer to the layout's constructor, we attached the layout to our widget. Then, we can start adding buttons to the layout:
for(int row = 0; row < 3; ++row) { for(int column = 0; column < 3; ++column) { QPushButton *button = new QPushButton(" "); gridLayout->addWidget(button, row, column); m_board.append(button); } }
The code creates a loop over rows and columns of the board. In each iteration, it creates an instance of the QPushButton class. The content of each button is set to a single space so that it gets the correct initial size. Then, we add the button to the layout in row and column. At the end, we store the pointer to the button in the vector that was declared earlier. This lets us reference any of the buttons later on. They are stored in the vector in such an order that the first three buttons of the first row are stored first, then the buttons from the second row, and finally those from the last row.
This should be enough for testing the widget. Let's add it to our main window. Open the mainwindow.ui file. Invoke the context menu of the empty widget called gameBoard and choose Promote to. This allows us to promote a widget to another class, that is, substitute a widget in the form with an instance of another class.
In our case, we will want to replace the empty widget with our game board. Select QWidget in the Base class name list, because our TicTacToeWidget is inherited from QWidget. Input TicTacToeWidget into the Promoted class name field and verify that the Header file field contains the correct name of the class's header file, as illustrated:
Then, click on the button labeled Add and then Promote, to close the dialog and confirm the promotion. You will not note any changes in the form, because the replacement only takes place at runtime (however, you will see the TicTacToeWidget class name next to gameBoard in the object tree).
Run the application and check whether the game board appears in the main window: