Go back to Richel Bilderbeek's homepage.
Go back to Richel Bilderbeek's C++ page.
This article shows the way to create a TicTacToe widget when using the
Wt library.
Downloads
Overview
This article follows the same architecture as
Thinking Wt 1: general. First the bare-bone architecture is implemented, so the programmer
can test the application as early as possible. Next follows the dialog and widget implementation, ending with
a conclusion.
Architecture
The architecture, from biggest to smallest, consists of:
Step 1: implementing the bare-bone architecture
The purpose of first implementing the bare-bone architecture is to get the program running, so it can be tested.
The same architecture as Thinking Wt 1 is used:
The most notable in this setup is that WtTicTacToeWidget is a derived class
of Wt::WPaintedWidget. This is because then there is most control in drawing the TicTacToe board.
For now, the widget shows an image called 'R.png'.
Running the Wt application
Add the following line to your Qt project file (to prevent
link errors like
undefined reference to 'Wt::WRun(int, char**, Wt::WApplication* (*)(Wt::WEnvironment const&))'):
Add the following line to your Qt project file (to prevent
link errors like [MyUnit.o] Error 1
and compile errors like
cc1plus: internal compiler error: Segmentation fault):
QMAKE_CXXFLAGS += -DNDEBUG
|
Add the following arguments to the Run Settings (to
prevent the misc error
stat: No such file or directory. Document root ("") not valid.
--docroot . --http-address 0.0.0.0 --http-port 8080
|
Start the program and your favorite webbrowser. Take the webbrowser to the following address:
Step 2: implementing the WtTicTacToeDialog
In this simple example, the WtTicTacToeDialog shows both a WtTicTacToeWidget and a restart button,
aligned vertically. The restart button also shows the state of the game (that is: player 1 has won,
player 2 has won, draw, game is still unfinished):
Note that the widget has only dummy member functions yet, but that the dialog is fully
functional. Because I prefer using the same signal/slot system in all my programs, I use the
Boost signals instead of the Wt signals. To get the widgets align vertically,
I put a Wt::WBreak between the two relevant widgets. The unnamed
Wt::WBreak will be deleted by the dialog.
Step 3: implementing the WtTicTacToeWidget
The widget handles the interface between the TicTacToe class and the user's mouse clicks.
#include <boost/signals2.hpp>
#include <Wt/WApplication>
#include <Wt/WBreak>
#include <Wt/WBrush>
#include <Wt/WContainerWidget>
#include <Wt/WEnvironment>
#include <Wt/WEvent>
#include <Wt/WPaintDevice>
#include <Wt/WPaintedWidget>
#include <Wt/WPainter>
#include <Wt/WPen>
#include <Wt/WPushButton>
#include "tictactoe.h"
//---------------------------------------------------------------------------
struct WtTicTacToeWidget : public Wt::WPaintedWidget
{
WtTicTacToeWidget()
{
//Without resize, there is nothing to paint on
this->resize(GetWidth(),GetHeight());
this->clicked().connect(this,&WtTicTacToeWidget::OnClicked);
this->update();
}
boost::signals2::signal<void ()> m_signal_has_winner;
boost::signals2::signal<void ()> m_signal_state_changed;
int GetState() const
{
return m_tictactoe.GetWinner();
}
void Restart()
{
m_tictactoe = TicTacToe();
this->update();
}
protected:
void paintEvent(Wt::WPaintDevice *paintDevice)
{
Wt::WPainter painter(paintDevice);
const int width = GetWidth();
const int height = GetHeight();
//Set black pen
Wt::WPen pen = painter.pen();
pen.setCapStyle(Wt::RoundCap);
pen.setColor(Wt::WColor(255,255,255));
painter.setPen(pen);
painter.setBrush(Wt::WBrush(Wt::WColor(255,255,255)));
painter.drawRect(0.0,0.0,GetWidth(),GetHeight());
//Set thick white pen
pen.setColor(Wt::WColor(0,0,0));
const int line_width = std::min(width,height) / 15;
pen.setWidth(line_width);
painter.setPen(pen);
//Vertical lines
painter.drawLine(
((1*width)/3)+4, 0+(line_width/2),
((1*width)/3)-4,height-(line_width/2));
painter.drawLine(
((2*width)/3)-4, 0+(line_width/2),
((2*width)/3)+8,height-(line_width/2));
//Horizontal lines
painter.drawLine(
0+(line_width/2),((1*height)/3)+4,
width-(line_width/2),((1*height)/3)-4);
painter.drawLine(
0+(line_width/2),((2*height)/3)-4,
width-(line_width/2),((2*height)/3)+8);
for (int row=0; row!=3; ++row)
{
const int x1 = ((row + 0) * (width / 3)) + (line_width/1) + 4;
const int x2 = ((row + 1) * (width / 3)) - (line_width/1) - 4;
for (int col=0; col!=3; ++col)
{
const int y1 = ((col + 0) * (height / 3)) + (line_width/1) + 4;
const int y2 = ((col + 1) * (height / 3)) - (line_width/1) - 4;
const int state = m_tictactoe.GetSquare(row,col);
if (state == TicTacToe::player1)
{
//player1 = cross
painter.drawLine(x1,y1,x2,y2);
painter.drawLine(x1,y2,x2,y1);
}
else if (state == TicTacToe::player2)
{
//player1 = circle
painter.drawEllipse(x1,y1,x2-x1,y2-y1);
}
}
}
}
private:
TicTacToe m_tictactoe;
int GetWidth() const { return 300.0; }
int GetHeight() const { return 300.0; }
void OnClicked(const Wt::WMouseEvent& e)
{
if (m_tictactoe.GetWinner() != TicTacToe::no_winner) return;
const int x = 3 * e.widget().x / this->GetWidth();
if (x < 0 || x > 2) return;
const int y = 3 * e.widget().y / this->GetHeight();
if (y < 0 || y > 2) return;
if (m_tictactoe.CanDoMove(x,y))
{
m_tictactoe.DoMove(x,y);
//emit that the state has changed
this->m_signal_state_changed();
}
if (m_tictactoe.GetWinner() != TicTacToe::no_winner)
{
//emit that there is a winner
this->m_signal_has_winner();
}
this->update();
}
};
|
Conclusion
This article described the gradual development of a custom dialog and widget.
Using the architecture described in Thinking Wt 1: general, it is possible
to have multiple steps-in-between to check if the program still works.
The tool TestTicTacToe contains the polished
and slightly extended version of the code in this article.
My next article, Thinking Wt 3: TicTacToe game describes how I implement the
WtTicTacToeWidget in a full game.
External links
- Wt homepage
- Victor Volkman. Wt: C++ Web Toolkit Library Lets You Write Scripting-Independent Web Apps. www.codeguru.com
- Wt homepage, source code of the 'Hello world' example
Go back to Richel Bilderbeek's C++ page.
Go back to Richel Bilderbeek's homepage.
