View Full Version : Tic-Tac-Toe using QGraphicsView

21st April 2014, 21:42
Hello Everyone!

I’ve created TTT before using QT widgets (a dialog, a grid, and pushbuttons). Now, I want to learn about QGraphicsView and its related classes. However, I cannot seem to wrap my brain around how to use QGraphicsView (and its related classes) to create TTT.

I’ve watched and read tutorials about creating the view/scene, adding rectangles, rotating the scene, and moving the rectangles, but still I draw a blank when it comes to figuring out how to create a set sized board that is the entire scene or how to create clickable regions on the scene.

In the QT documentation, I’ve read about QML, but I don’t want to use that. I really just want to get a good grasp of QGraphicsView (and all), so I can work on more sophisticated grid based games. Any suggestions on how to approach this?

22nd April 2014, 06:05
You're probably over-thinking it. The important thing to remember about the Graphics / View architecture is that the QGraphicsScene is an abstraction in which the items in a scene are defined and laid out independently of any QGraphicsView used to display it. This means you can pick any convenient coordinate system you want for the scene and rely on the transformation into the view to display it correctly in pixel coordinates.

For tic-tac-toe, I would probably design the scene to have two graphics item types in it: a board item and a square item. Both of these could be derived from QGraphicsRectItem as a base class. In the board's paint() method, you fill the board background by calling QGraphicsRectItem::paint() and then draw the lines representing the hash cross yourself using QPainter calls. Remember, you can choose whatever coordinates you want for tic-tac-toe "space", so if you want to imagine that your board is 300 cm x 300 cm, then you can do that, in which case you'd draw the dividing lines at 100 cm and 200 cm on the x and y axes.

The squares could also be derived from QGraphicsRectItem. There are 9 of them, all the same size, and all children of the board item. You'd position them at (0, 0), (0, 100), (0, 200), (100, 0), etc. The square items have a property that defines their state: "X", "O", or empty. You could do one of two things to draw them: Use the paint() method to draw text in the square, or create a QGraphicsSimpleTextItem instance as a child of each square. Position the text item at the square's (0, 0) coordinate. Since it is a child of the square, it will move with it as the square is repositioned. Simply setting the text of the item to "X", "O", or "" will cause it to be redrawn.

Now comes the interaction part. For each square item, be sure to set the ItemIsSelectable flag to true when you create it. Then, connect a slot (in whatever class you design to manage the tic-tac-toe engine logic) to the QGraphicsScene::selectionChanged() signal. When the user clicks a square, the scene will emit this signal. In your slot you can use QGraphicsScene::selectedItems() to learn which square was clicked, respond accordingly, then call QGraphicsScene::clearSelection() to set up for the next click.

Finally, you need to display the scene somewhere. Create a QMainWindow and a QGraphicsView as the central widget, or create a QDialog with a QGraphicsView inside a layout, and call QGraphicsView::setScene() with your scene pointer. To map from scene coordinates into the view's pixel coordinates, the simplest way is to handle the resizeEvent for your main window or dialog and call QGraphicsView::fitInView() through your QGraphicsView pointer, and give it the QRectF representing the scene coordinates for the part of the scene you want displayed. If you've defined your scene as 300 x 300, then you pass a QRectF of that size.

A lot of words, no code, but that's for you to figure out. If you get stuck, post what you have and describe what you've done (and failed to get working).

22nd April 2014, 13:37
Thanks d_stranz, that is an amazing amount of help!