如何在Python Turtle GUI中实现鼠标追踪与列高亮功能?
Connect Four游戏鼠标列高亮功能实现方案
问题说明
我正在使用Python的Turtle库开发Connect Four游戏,作业要求实现鼠标追踪功能:当鼠标处于棋子占位符所在列时,高亮该占位符(如图所示),但当前代码无法正确实现此效果。

原代码问题分析
- 鼠标移动事件处理引入冗余延迟和状态变量,导致高亮响应卡顿
- 列判断仅通过x坐标差做近似比较,精准度不足
- 状态变量管理混乱,易出现逻辑冲突导致高亮失效
修复后的完整代码
import turtle from turtle import * textinput("游戏说明", "你需要在任意一列放置棋子,率先在任意方向连成4枚棋子的玩家获胜。明白了吗?(输入任意内容继续)") def init_board(): global gameBoard, winner gameBoard = [] winner = None global tokenBoard tokenBoard = [] for i in range(8): gameBoard.append([0, 0, 0, 0, 0, 0, 0, 0]) tokenBoard.append([None, None, None, None, None, None, None, None]) '''初始化游戏棋盘,创建8个灰色矩形作为棋子占位符''' def start_game(canvas): global rectangleHeight, rectangleWidth, gap rectangleWidth = 60 rectangleHeight = 10 gap = (canvas.window_width() - (rectangleWidth * 8)) / 9 global rectangleTurtles rectangleTurtles = [] for i in range(8): rectangle = turtle.Turtle() rectangle.speed(0) rectangle.hideturtle() rectangle.penup() # 计算每个矩形的中心坐标 x = -canvas.window_width()/2 + gap + i*(rectangleWidth + gap) + 0.5*rectangleWidth y = -canvas.window_height()/2 + 100 + 0.5*rectangleHeight rectangle.goto(x, y) rectangle.color("gray") rectangle.shape("square") rectangle.shapesize(1, 3) rectangle.fillcolor("gray") rectangle.showturtle() rectangleTurtles.append(rectangle) '''查找指定列的第一个空行(从底部开始),列满则返回None''' def search_horizontally(table, column): rowNum = 8 for row in table[::-1]: rowNum -= 1 try: if row[column] == 0: return rowNum except IndexError: pass return None '''创建指定颜色的棋子,并放置在对应位置''' def create_token(x, y, color): token = turtle.Turtle() token.speed(0) token.hideturtle() token.penup() token.goto(x, y) token.color(color) token.shape("circle") token.shapesize(3, 3) token.showturtle() return token '''检查当前落子是否获胜,判断横向、纵向、斜向的四连子''' def check_winner(column, row): global gameBoard, winner directions = [(-1, 0), (0, 1), (-1, 1), (1, 1)] player = gameBoard[row][column] for direction in directions: count = 1 winning_tokens = [(row, column)] # 正向遍历 for i in range(1, 4): newRow = row + direction[1] * i newCol = column + direction[0] * i if 0 <= newRow < 8 and 0 <= newCol < 8 and gameBoard[newRow][newCol] == player: count += 1 winning_tokens.append((newRow, newCol)) else: break # 反向遍历 for i in range(1, 4): newRow = row - direction[1] * i newCol = column - direction[0] * i if 0 <= newRow < 8 and 0 <= newCol < 8 and gameBoard[newRow][newCol] == player: count += 1 winning_tokens.append((newRow, newCol)) else: break if count >= 4: winner = player highlight_token(winning_tokens) return '''高亮获胜的棋子,添加黄色边框''' def highlight_token(winning_tokens): global tokenBoard for row, column in winning_tokens: token = tokenBoard[row][column] token.shapesize(3, 3, 5) token.pencolor('yellow') canvas.title(f'恭喜!玩家{3 - winner}获胜啦 (*^ - ^*)') canvas.update() '''更新游戏棋盘的落子记录''' def update_board(column, row): global gameBoard gameBoard[row][column] = currentPlayer + 1 '''处理鼠标移动事件,实现列高亮''' def motion_handler(event, color1, color2): global currentPlayer, winner if winner is not None: return canvas_width = event.widget.winfo_width() canvas_height = event.widget.winfo_height() # 转换鼠标坐标为Turtle坐标系 x = event.x - canvas_width // 2 y = -event.y + canvas_height // 2 current_highlight_col = -1 # 判断鼠标所在的列 for idx, rect in enumerate(rectangleTurtles): rect_x = rect.xcor() # 计算列的左右边界 left = rect_x - rectangleWidth/2 right = rect_x + rectangleWidth/2 if left <= x <= right: current_highlight_col = idx break # 更新所有列的样式 for idx, rect in enumerate(rectangleTurtles): if idx == current_highlight_col: # 高亮当前列:设置对应玩家颜色的边框,加粗 rect.shapesize(1, 3, 5) rect.color(color1 if currentPlayer == 0 else color2, 'gray') else: # 恢复默认样式 rect.shapesize(1, 3, 1) rect.color('gray', 'gray') canvas.update() '''处理鼠标点击事件,执行落子逻辑''' def on_click(event, color1, color2): global currentPlayer, winner if winner is not None: return canvas_width = event.widget.winfo_width() canvas_height = event.widget.winfo_height() x = event.x - canvas_width // 2 y = -event.y + canvas_height // 2 # 查找点击的列 column = -1 for idx, rect in enumerate(rectangleTurtles): rect_x = rect.xcor() left = rect_x - rectangleWidth/2 right = rect_x + rectangleWidth/2 if left <= x <= right: column = idx break if column == -1: return row = search_horizontally(gameBoard, column) if row is None: return # 列已满 # 创建棋子 rect = rectangleTurtles[column] token_y = rect.ycor() + (8 - row) * 60 if currentPlayer == 0: token = create_token(rect.xcor(), token_y, color1) currentPlayer = 1 else: token = create_token(rect.xcor(), token_y, color2) currentPlayer = 0 canvas.update() update_board(column, row) tokenBoard[row][column] = token check_winner(column, row) def __main__(): color1 = textinput('玩家1','输入除黄色外的任意颜色:') color2 = textinput('玩家2', '输入除黄色外的任意颜色:') global currentPlayer, canvas currentPlayer = 0 canvas = turtle.Screen() canvas.title('四子棋游戏') canvas.tracer(0) init_board() start_game(canvas) # 绑定鼠标事件 canvas.getcanvas().bind("<Motion>", lambda event: motion_handler(event, color1, color2)) canvas.getcanvas().bind("<Button-1>", lambda event: on_click(event, color1, color2)) turtle.mainloop() __main__()
关键修改说明
- 简化鼠标移动处理:移除冗余延迟函数和状态变量,直接在
motion_handler中完成列判断和样式更新,响应更流畅 - 精准列判断:通过计算列的左右边界确定鼠标位置,避免近似判断的误差
- 优化高亮逻辑:仅对当前悬停列应用高亮样式,其他列恢复默认,减少画布更新开销
- 统一坐标转换:点击和移动事件使用相同的坐标转换逻辑,确保判断一致性
- 状态判断优化:事件处理优先判断游戏是否结束,避免无效操作
内容的提问来源于stack exchange,提问作者Mandakh Tselmeg
相关产品推荐
相关产品推荐

