Python 如何在Tic Tac Toe PyQT5中显示获胜的移动?

Python 如何在Tic Tac Toe PyQT5中显示获胜的移动?,python,python-3.x,pyqt5,Python,Python 3.x,Pyqt5,我目前正在熟悉强化学习的基础知识,为了方便起见(而不是在终端中手动输入坐标),我创建了一个非常简单的UI,用于测试经过培训的代理和玩游戏。出于测试目的,我创建了一个方法,在人类玩家点击单元格后生成一个随机移动,每个移动都会在点击/随机移动后显示,但是当移动是成功移动时,它不会显示,并且板会重置。我不知道为什么会发生这种情况,但看起来执行顺序不是连续的/有些事情我不明白。我尝试在板更新后使用time.sleep(1),以确保我不会忽略实际显示的移动,但是移动从未显示,窗口只是冻结 预期成果: 我

我目前正在熟悉强化学习的基础知识,为了方便起见(而不是在终端中手动输入坐标),我创建了一个非常简单的UI,用于测试经过培训的代理和玩游戏。出于测试目的,我创建了一个方法,在人类玩家点击单元格后生成一个随机移动,每个移动都会在点击/随机移动后显示,但是当移动是成功移动时,它不会显示,并且板会重置。我不知道为什么会发生这种情况,但看起来执行顺序不是连续的/有些事情我不明白。我尝试在板更新后使用
time.sleep(1)
,以确保我不会忽略实际显示的移动,但是移动从未显示,窗口只是冻结

预期成果:

我实际得到的(给定我将单击单元格(0,2)):


在大多数GUI工具包中,您从未直接绘制到屏幕。取而代之的是,您要么正在绘制到辅助缓冲区,要么只是在更新小部件的对象模型,当您将控制权交回工具箱的主循环时,工具箱将复制到实际屏幕


如果要更新实际接口,则需要从回调方法返回。因此,如果您想更新屏幕,然后在几秒钟后再次更新,则需要在设置计时器以进行回调以执行第二次屏幕更新后,从第一次回调返回。在QT中,您可以使用QTimer进行此操作。

感谢您的明确解释,如果您能告诉我如何操作,我将非常高兴
from PyQt5.QtWidgets import (
    QMainWindow,
    QDesktopWidget,
    QApplication,
    QWidget,
    QHBoxLayout,
    QVBoxLayout,
    QPushButton,
    QLabel,
)
from PyQt5.QtCore import Qt
import numpy as np
import sys


class TicCell(QPushButton):
    """
    Tic Tac Toe cell.
    """

    def __init__(self, location):
        """
        Initialize cell location.
        Args:
            location: Tuple, (row, col).
        """
        super().__init__()
        self.location = location


class TicUI(QMainWindow):
    """
    Tic Tac Toe interface.
    """

    def __init__(
        self,
        window_title='Smart Tic Tac Toe',
        board_size=3,
        empty_value=0,
        x_value=1,
        o_value=2,
        agent=None,
    ):
        """
        Initialize game settings.
        Args:
            window_title: Display window name.
            board_size: int, the board will be of size(board_size, board_size).
            empty_value: int representation of an empty cell.
            x_value: int representation of a cell containing X.
            o_value: int representation of a cell containing O.
            agent: Trained TicAgent object.
        """
        super().__init__()
        self.setWindowTitle(window_title)
        self.board_size = board_size
        self.empty_value = empty_value
        self.x_value = x_value
        self.o_value = o_value
        self.agent = agent
        self.text_map = {
            x_value: 'X',
            o_value: 'O',
            empty_value: '',
            'X': x_value,
            'O': o_value,
            '': empty_value,
        }
        win_rectangle = self.frameGeometry()
        center_point = QDesktopWidget().availableGeometry().center()
        win_rectangle.moveCenter(center_point)
        self.central_widget = QWidget(self)
        self.main_layout = QVBoxLayout()
        self.score_layout = QHBoxLayout()
        self.human_score = 0
        self.agent_score = 0
        self.score_board = QLabel()
        self.update_score_board()
        self.setStyleSheet('QPushButton:!hover {color: yellow}')
        self.cells = [
            [TicCell((c, r)) for r in range(board_size)]
            for c in range(board_size)
        ]
        self.cell_layouts = [QHBoxLayout() for _ in self.cells]
        self.board = np.ones((board_size, board_size)) * self.empty_value
        self.adjust_layouts()
        self.adjust_cells()
        self.update_cell_values()
        self.show()

    def adjust_layouts(self):
        """
        Adjust score board and cell layouts.

        Returns:
            None
        """
        self.main_layout.addLayout(self.score_layout)
        for cell_layout in self.cell_layouts:
            self.main_layout.addLayout(cell_layout)
        self.central_widget.setLayout(self.main_layout)
        self.setCentralWidget(self.central_widget)

    def adjust_cells(self):
        """
        Adjust display cells.

        Returns:
            None
        """
        self.score_layout.addWidget(self.score_board)
        self.score_board.setAlignment(Qt.AlignCenter)
        for row_index, row in enumerate(self.cells):
            for cell in row:
                cell.setFixedSize(50, 50)
                cell.clicked.connect(self.game_step)
                self.cell_layouts[row_index].addWidget(cell)

    def get_empty_cells(self):
        """
        Get empty cell locations.

        Returns:
            A list of indices that reepresent currently empty cells.
        """
        empty_locations = np.where(self.board == self.empty_value)
        empty_locations = list(zip(empty_locations[0], empty_locations[1]))
        for empty_location in empty_locations:
            r, c = empty_location
            cell_text = self.cells[r][c].text()
            assert cell_text == self.text_map[self.empty_value], (
                f'location {empty_location} has a cell value of {cell_text}'
                f'and board value of {self.board[r][c]} {self.board}'
            )
        return empty_locations

    def update_score_board(self):
        """
        Update the display scores.

        Returns:
            None
        """
        self.score_board.setText(
            f'Human {self.human_score} - ' f'{self.agent_score} Agent'
        )

    def check_win(self, player_value):
        """
        Check current game state for winner.
        Args:
            player_value: int, self.x_value or self.o_value.

        Returns:
            True if player_value won, False otherwise.
        """
        return (
            np.all(self.board == player_value, axis=0).any()
            or np.all(self.board == player_value, axis=1).any()
            or np.all(self.board.diagonal() == player_value)
            or np.all(self.board[::-1].diagonal() == player_value)
        )

    def reset_cell_colors(self):
        """
        Reset display cell text colors.

        Returns:
            None
        """
        for row_idx in range(self.board_size):
            for col_idx in range(self.board_size):
                self.cells[row_idx][col_idx].setStyleSheet('color: yellow')

    def reset_game(self, winner=None):
        """
        Reset board and display cells and update display scores.
        Args:
            winner: int, self.x_value or self.o_value.

        Returns:
            None
        """
        self.board = (
            np.ones((self.board_size, self.board_size)) * self.empty_value
        )
        self.update_cell_values()
        if winner == self.x_value:
            self.human_score += 1
        if winner == self.o_value:
            self.agent_score += 1
        self.update_score_board()
        self.reset_cell_colors()

    def modify_step(self, cell_location, value):
        """
        Modify board and display cells.
        Args:
            cell_location: tuple, representing indices(row, col).
            value: int, self.x_value or self.o_value.

        Returns:
            True if the clicked cell is not empty, None otherwise.
        """
        r, c = cell_location
        board_value = self.board[r, c]
        cell_value = self.cells[r][c].text()
        if not board_value == self.empty_value:
            return True
        assert cell_value == self.text_map[self.empty_value], (
            f'mismatch between board value({board_value}) '
            f'and cell value({cell_value}) for location {(r, c)}'
        )
        if value == self.x_value:
            self.cells[r][c].setStyleSheet('color: red')
        self.board[r, c] = value
        self.cells[r][c].setText(f'{self.text_map[value]}')

    def game_step(self):
        """
        Post cell-click step(human step and agent step)

        Returns:
            None
        """
        cell = self.sender()
        stop = self.modify_step(cell.location, self.x_value)
        if stop:
            return
        x_win = self.check_win(self.x_value)
        if x_win:
            self.reset_game(self.x_value)
            return
        empty_locations = self.get_empty_cells()
        if not empty_locations:
            self.reset_game()
            return
        choice = np.random.choice(range(len(empty_locations)))
        if self.agent:
            choice = self.agent.generate_move(self.board, empty_locations)
        self.modify_step(empty_locations[choice], self.o_value)
        o_win = self.check_win(self.o_value)
        if o_win:
            self.reset_game(self.o_value)

    def update_cell_values(self):
        """
        Sync display cells with self.board

        Returns:
            None
        """
        for row_index, row in enumerate(self.board):
            for col_index, col in enumerate(row):
                update_value = self.text_map[self.board[row_index][col_index]]
                self.cells[row_index][col_index].setText(f'{update_value}')


if __name__ == '__main__':
    test = QApplication(sys.argv)
    window = TicUI()
    sys.exit(test.exec_())