aboutsummaryrefslogtreecommitdiffstats
path: root/examples/designer/taskmenuextension/tictactoe.py
blob: aa1c3158c8cfe020b12a135b8be805b5906ea8b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

from PySide6.QtCore import Qt, QPoint, QRect, QSize, Property, Slot
from PySide6.QtGui import QPainter, QPen
from PySide6.QtWidgets import QWidget


EMPTY = '-'
CROSS = 'X'
NOUGHT = 'O'
DEFAULT_STATE = "---------"


class TicTacToe(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._state = DEFAULT_STATE
        self._turn_number = 0

    def minimumSizeHint(self):
        return QSize(200, 200)

    def sizeHint(self):
        return QSize(200, 200)

    def setState(self, new_state):
        self._turn_number = 0
        self._state = DEFAULT_STATE
        for position in range(min(9, len(new_state))):
            mark = new_state[position]
            if mark == CROSS or mark == NOUGHT:
                self._turn_number += 1
                self._change_state_at(position, mark)
            position += 1
        self.update()

    def state(self):
        return self._state

    @Slot()
    def clear_board(self):
        self._state = DEFAULT_STATE
        self._turn_number = 0
        self.update()

    def _change_state_at(self, pos, new_state):
        self._state = (self._state[:pos] + new_state
                       + self._state[pos + 1:])

    def mousePressEvent(self, event):
        if self._turn_number == 9:
            self.clear_board()
            return
        for position in range(9):
            cell = self._cell_rect(position)
            if cell.contains(event.position().toPoint()):
                if self._state[position] == EMPTY:
                    new_state = CROSS if self._turn_number % 2 == 0 else NOUGHT
                    self._change_state_at(position, new_state)
                    self._turn_number += 1
                    self.update()

    def paintEvent(self, event):
        with QPainter(self) as painter:
            painter.setRenderHint(QPainter.Antialiasing)

            painter.setPen(QPen(Qt.darkGreen, 1))
            painter.drawLine(self._cell_width(), 0,
                             self._cell_width(), self.height())
            painter.drawLine(2 * self._cell_width(), 0,
                             2 * self._cell_width(), self.height())
            painter.drawLine(0, self._cell_height(),
                             self.width(), self._cell_height())
            painter.drawLine(0, 2 * self._cell_height(),
                             self.width(), 2 * self._cell_height())

            painter.setPen(QPen(Qt.darkBlue, 2))

            for position in range(9):
                cell = self._cell_rect(position)
                if self._state[position] == CROSS:
                    painter.drawLine(cell.topLeft(), cell.bottomRight())
                    painter.drawLine(cell.topRight(), cell.bottomLeft())
                elif self._state[position] == NOUGHT:
                    painter.drawEllipse(cell)

            painter.setPen(QPen(Qt.yellow, 3))

            for position in range(0, 8, 3):
                if (self._state[position] != EMPTY
                        and self._state[position + 1] == self._state[position]
                        and self._state[position + 2] == self._state[position]):
                    y = self._cell_rect(position).center().y()
                    painter.drawLine(0, y, self.width(), y)
                    self._turn_number = 9

            for position in range(3):
                if (self._state[position] != EMPTY
                        and self._state[position + 3] == self._state[position]
                        and self._state[position + 6] == self._state[position]):
                    x = self._cell_rect(position).center().x()
                    painter.drawLine(x, 0, x, self.height())
                    self._turn_number = 9

            if (self._state[0] != EMPTY and self._state[4] == self._state[0]
                    and self._state[8] == self._state[0]):
                painter.drawLine(0, 0, self.width(), self.height())
                self._turn_number = 9

            if (self._state[2] != EMPTY and self._state[4] == self._state[2]
                    and self._state[6] == self._state[2]):
                painter.drawLine(0, self.height(), self.width(), 0)
                self._turn_number = 9

    def _cell_rect(self, position):
        h_margin = self.width() / 30
        v_margin = self.height() / 30
        row = int(position / 3)
        column = position - 3 * row
        pos = QPoint(column * self._cell_width() + h_margin,
                     row * self._cell_height() + v_margin)
        size = QSize(self._cell_width() - 2 * h_margin,
                     self._cell_height() - 2 * v_margin)
        return QRect(pos, size)

    def _cell_width(self):
        return self.width() / 3

    def _cell_height(self):
        return self.height() / 3

    state = Property(str, state, setState)