多个程序化点击事件的意外行为

时间:2017-03-29 15:00:33

标签: python qt pyqt pyqt5

我正在尝试使用PyQt5创建一个Tic-Tac-Toe UI。提供了三种模式

  1. 人与人
  2. 人与人之间
  3. 人工智能与人工智能游戏
  4. 除了在模式3(两个AI相互对战)之外,所有模式都工作得非常精细,UI将开始加载并显示最终结果,而不是刷新每个回合的中间步骤直到游戏开始,这是我预期的并且优选。我怎样才能实现它?谢谢!

    以下是我的代码:

    class TicTacToeUI(QtWidgets.QWidget):
        icons = ['icons/x-mark.png', 'icons/hollow-circle.png']
    
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setWindowTitle("TicTacToe")
            self.setStyleSheet('background-color: #D7CCC8;')
            # Players
            self._players_selector = QtWidgets.QGridLayout()
            self._players = list()
            for p in range(2):
                label = QtWidgets.QLabel('Player {:}'.format(p))
                selectbox = QtWidgets.QComboBox()
                selectbox.setFixedHeight(24)
                selectbox.addItems(list(PLAYERS.keys()))
                self._players_selector.addWidget(label, 0, p)
                self._players_selector.addWidget(selectbox, 1, p)
                self._players.append(selectbox)
            self._grid = QtWidgets.QGridLayout()
            self._gameboard_buttons = dict()
            for i in range(self.m):
                for j in range(self.n):
                    btn = self.create_gameboard_button(row=i, col=j)
                    self._grid.addWidget(btn, i, j)
                    self._gameboard_buttons.update({(i, j): btn})
            self._message_box = QtWidgets.QLabel('', parent=self)
            self._restart_button = QtWidgets.QPushButton('Start', parent=self)
            self._restart_button.setFixedHeight(32)
            self._restart_button.clicked.connect(self.restart)
            # Layout
            self._layout = QtWidgets.QVBoxLayout()
            self._layout.addLayout(self._players_selector)
            self._layout.addLayout(self._grid)
            self._layout.addWidget(self._message_box)
            self._layout.addWidget(self._restart_button)
            self.setLayout(self._layout)
            # The game
            self._game = TicTacToe()
            self._started = False
    
        def create_gameboard_button(self, row, col):
            btn = TicTacToeUIButton('', parent=self, style_sheet='background-color: #D7CCC8;')
            btn.setFixedSize(64, 64)
            btn.clicked.connect(functools.partial(self.click, row, col))
            return btn
    
        def click(self, row, col):
            if not self._started:
                pass
            elif self._game.is_ended:
                pass
            else:
                btn = self._gameboard_buttons.get((row, col))
                if btn is not None:
                    if btn.is_occupied:
                        msg = 'It is occupied. Please select another one.'
                        self._message_box.setText(msg)
                    else:
                        player = self._game.turn_player
                        # Icon appearance
                        for _, b in self._gameboard_buttons.items():
                            b.remove_border()
                        btn.setIcon(QtGui.QIcon(self.icons[player]))
                        btn.setIconSize(QtCore.QSize(48, 48))
                        btn.add_border('border: 1px solid #FF0000;')
                        btn.occupied_player = player
                        # Check result
                        self._game.move(row=row, col=col, player=player)
                        if self._game.winner is not None:
                            if self._game.winner == -1:
                                msg = 'Draw. What a game!'
                            else:
                                msg = 'Player {:} won. Congratulations!'
                                msg = msg.format(self._game.winner)
                        else:
                            msg = ''
                        self._message_box.setText(msg)
                # Next turn
                if self._game.is_ended:
                    pass
                else:
                    self._game.players[self._game.turn_player].decide_ui(self)
    
        def reset(self):
            self._started = True
            self._game.reset()
            self._game.player0 = PLAYERS.get(self._players[0].currentText())
            self._game.player1 = PLAYERS.get(self._players[1].currentText())
            self._restart_button.setText('Restart')
            self._message_box.setText('')
            for _, btn in self._gameboard_buttons.items():
                btn.reset()
            if self._game.player0.is_ai:
                self._game._player0.decide_ui(self)
    
        def restart(self):
            self.reset()
    

    一些注意事项:

    • 游戏将在点击“开始”/“重启”按钮后立即开始,玩家,无论是人类还是AI,都会根据他们的判断/算法点击游戏板按钮。

    • Human().decide_ui(self)什么也没做 SomeAI().decide_ui(self)将根据某些算法点击游戏板按钮。

    • TicTacToe()是存储游戏板矩阵和获胜标准的对象。

    如果您对玩游戏感兴趣,可以下载整套代码here

1 个答案:

答案 0 :(得分:1)

我已经下载了完整的应用程序,以便更好地了解整个过程。

因此,在运行时,AI会使主进程保持忙碌状态,并且永远不会处理Qt事件,并且UI上不会显示任何内容。

最简单的解决方案是每回合拨打QApplication.processEvents(),如下所示:

def click(self, row, col):
    # "click" code

    QtWidgets.qApp.processEvents()

    # Next turn
    if self._game.is_ended:
        pass
    else:
        self._game.players[self._game.turn_player].decide_ui(self)

这将处理所有待处理事件,并且此时将显示一个移动。