PyQt5开发国际象棋游戏出现GUI逐步卡顿问题求助
解决PyQt5国际象棋GUI逐步卡顿问题
核心问题原因
你的代码中重复连接按钮的点击信号是导致卡顿的根本原因:
- 在
initiate_move函数里,每次点击棋子后,都会给所有64个棋盘按钮重新连接play_move槽函数 - 在
play_move函数结束时,又会给所有按钮重新连接initiate_move槽函数 - 每走一步,每个按钮的信号连接数就会翻倍,到第8步时每个按钮会有256个连接,点击一次会触发几百次函数调用,直接导致GUI线程阻塞无响应
修复方案
改用状态标记来区分当前操作阶段(选棋子/选目标位置),不再反复切换信号连接,同时优化棋盘样式更新逻辑:
关键修改点
- 添加状态变量
self.selecting_piece = True,标记当前是选择棋子还是目标位置 - 移除
initiate_move和play_move中所有self.Chess_Board[i].clicked.connect(...)的循环代码,初始化时只连接一次通用的点击处理函数 - 用一个变量记录当前选中的按钮,只重置该按钮的样式,避免每次遍历所有64个按钮重置样式
- 简化棋盘样式设置逻辑,减少重复操作
修复后的完整代码
import PyQt5 from PyQt5 import QtWidgets from PyQt5 import QtCore from PyQt5 import QtGui from PyQt5 import uic import chess import sys import tabulate class Chess_App(QtWidgets.QMainWindow): def __init__(self): super(Chess_App, self).__init__() uic.loadUi("CHESS.ui", self) self.move_name = "" self.move = "" self.selected_button = None # 记录选中的按钮 self.selecting_piece = True # 状态标记:True=选棋子,False=选目标位置 self.board_rows = [8,7,6,5,4,3,2,1] self.board_columns = 'a b c d e f g h'.split(" ") font = QtGui.QFont() font.setPointSize(1) self.win = None self.draw = None self.valid_move = True self.Chess_Board = {n:QtWidgets.QPushButton(self) for n in range(64)} # 初始化棋盘按钮样式和连接 for i in range(64): self.Chess_Board[i].setFont(font) self.Chess_Board[i].setObjectName(f"{self.board_columns[i%8]}{self.board_rows[i//8]}") self.Chess_Board[i].setGeometry(60+((i%8)*50),140+((i//8)*50),50,50) self.Chess_Board[i].clicked.connect(self.handle_click) # 只连接一次通用处理函数 self.reset_button_style(i) # 初始化样式 self.lineEdit_2.setDisabled(True) self.lineEdit.isReadOnly() self.pushButton_A.clicked.connect(self.play_move) self.pushButton_B.clicked.connect(self.resignation) self.pushButton_C.clicked.connect(self.draw_game) self.pushButton_D.clicked.connect(self.save_game) self.pushButton_E.clicked.connect(self.reset_game) self.board = chess.Board() self.chessboard() self.show_board() self.show() def reset_button_style(self, index): """重置单个按钮的样式为棋盘原色""" if (index // 8) % 2 == 0: bg_color = "white" if index % 2 == 0 else "lightgreen" else: bg_color = "white" if index % 2 == 1 else "lightgreen" self.Chess_Board[index].setStyleSheet(f"QPushButton {{background-color : {bg_color}}}") def chessboard(self): self.pgn = self.board.epd() self.final_list = [] self.printable_list = [] pieces = self.pgn.split(" ", 1)[0] rows = pieces.split("/") for row in rows: self.initial_list = [] for chessmen in row: if chessmen.isdigit(): self.initial_list.extend(["."]*int(chessmen)) else: self.initial_list.append(chessmen) self.final_list.append(self.initial_list) def handle_click(self): """通用点击处理函数,根据当前状态执行不同逻辑""" button = self.sender() if self.selecting_piece: self.initiate_move(button) else: self.play_move_from_click(button) def initiate_move(self, button): """选择棋子的逻辑""" # 重置之前选中按钮的样式 if self.selected_button: btn_index = list(self.Chess_Board.keys())[list(self.Chess_Board.values()).index(self.selected_button)] self.reset_button_style(btn_index) self.selected_button = button self.move = button.text().upper() if self.move and len(self.move) == 1: if self.move != "P": self.move_name += self.move button.setStyleSheet("QPushButton {background-color : cyan}") self.selecting_piece = False # 切换到选目标位置状态 def play_move_from_click(self, button): """选择目标位置并执行移动的逻辑""" self.textBrowser.setText("") self.valid_move = True if len(button.text()) == 1: self.move_name += f"x{button.objectName()}" else: self.move_name += button.objectName() try: self.board.push_san(self.move_name) except Exception as ex: self.textBrowser.setText(f"{ex}") self.valid_move = False if self.valid_move: # 切换玩家输入框 if self.lineEdit.isEnabled(): self.lineEdit.setDisabled(True) self.lineEdit_2.setDisabled(False) else: self.lineEdit.setDisabled(False) self.lineEdit_2.setDisabled(True) # 检查游戏结束状态 if self.board.is_checkmate(): self.textBrowser.setText("Checkmate !") self.win = True self.end_the_game() elif self.board.is_variant_draw() or self.board.is_stalemate() or self.board.is_fifty_moves() or self.board.is_repetition(): if self.board.is_fifty_moves(): self.textBrowser.setText("Draw by fifty-move rule !") elif self.board.is_repetition(): self.textBrowser.setText("Draw by three-fold repetition !") else: self.textBrowser.setText("Draw !") self.draw = True self.end_the_game() # 更新棋盘 self.chessboard() self.show_board() else: self.textBrowser.setText("Invalid move, please try again") # 重置选中状态和移动记录 if self.selected_button: btn_index = list(self.Chess_Board.keys())[list(self.Chess_Board.values()).index(self.selected_button)] self.reset_button_style(btn_index) self.selected_button = None self.move = "" self.move_name = "" self.selecting_piece = True # 切回选棋子状态 def play_move(self): """原按钮触发的移动逻辑(保留兼容)""" pass def show_board(self): for x in range(8): for y in range(8): piece = self.final_list[x][y] btn = self.Chess_Board[x*8+y] if piece.isupper(): btn.setText(piece.upper()) btn.setIcon(QtGui.QIcon(f"{piece}@.png")) btn.setIconSize(QtCore.QSize(40,40)) elif piece.islower(): btn.setText(piece.lower()) btn.setIcon(QtGui.QIcon(f"{piece}&.png")) btn.setIconSize(QtCore.QSize(40,40)) else: btn.setIcon(QtGui.QIcon()) btn.setText("") def reset_game(self): self.board.reset() self.lineEdit.setText("") self.lineEdit_2.setText("") self.lineEdit.setDisabled(False) self.lineEdit_2.setDisabled(True) self.textBrowser.setText("") self.selected_button = None self.selecting_piece = True self.move_name = "" self.move = "" self.chessboard() self.show_board() self.end_the_game() def draw_game(self): self.draw = True self.end_the_game() self.textBrowser.setText("It's a draw .") def resignation(self): if self.lineEdit.text() == self.lineEdit_2.text() == "": self.textBrowser.setText("Match Aborted") else: who_resigned = "Black" if self.lineEdit.isEnabled() else "White" self.win = self.lineEdit.isEnabled() self.end_the_game() self.textBrowser.setText(f"{who_resigned} resigned") def save_game(self): self.chesstable = tabulate.tabulate(self.final_list, tablefmt="grid", stralign="left") with open("chess_save_list.txt","a") as writer: writer.write(self.chesstable + "\n\n") # 添加换行分隔保存内容 def end_the_game(self): if self.win is not None or self.draw is not None: self.pushButton_A.setDisabled(True) self.pushButton_B.setDisabled(True) self.pushButton_C.setDisabled(True) self.win, self.draw = None, None else: self.pushButton_A.setDisabled(False) self.pushButton_B.setDisabled(False) self.pushButton_C.setDisabled(False) app = QtWidgets.QApplication(sys.argv) window = Chess_App() app.exec_()
额外优化建议
- 棋子图标加载可以提前缓存,避免每次
show_board都重新加载图标文件 - 棋盘样式可以用QSS统一设置,减少逐个按钮设置样式的开销
- 游戏状态判断可以合并为一次遍历,减少重复调用
board.is_xxx()方法
内容的提问来源于stack exchange,提问作者Vishal 10B
相关产品推荐
相关产品推荐

