You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从坐标数组识别正方形?PyQt5网格绘图功能实现遇阻求助

我来帮你一步步拆解解决这两个核心问题——先搞定PyQt5里点击网格时的位置识别与绘制时机问题,再实现从坐标数组里检测正方形的逻辑。

一、解决PyQt5点击网格与绘制时机/位置的识别问题

你目前的核心问题应该是没有正确绑定鼠标点击事件与Qt的绘制流程,Qt的绘制系统有严格的触发机制,直接随意调用QPainter会导致画面异常。这里给你一套可落地的实现方案:

  1. 正确捕获网格内的点击坐标
    重写你的画布类的mousePressEvent方法,把屏幕像素坐标转换为网格逻辑坐标(比如每个网格单元固定50x50像素),再存入对应玩家的点击列表。同时要记得触发重绘:

    from PyQt5.QtWidgets import QWidget
    from PyQt5.QtGui import QPainter, QPen
    from PyQt5.QtCore import Qt
    
    class GridCanvas(QWidget):
        def __init__(self):
            super().__init__()
            self.grid_cell_size = 50  # 单个网格单元的像素尺寸
            # 初始化玩家的点击数据
            self.clicked0 = []
            self.distlist0 = []
            self.distdict0 = {}
            self.clicked1 = []
            self.distlist1 = []
            self.distdict1 = {}
            self.current_player = 0  # 当前操作的玩家标识
    
        def mousePressEvent(self, event):
            # 将屏幕坐标转换为网格单元的左上角坐标(对齐网格)
            grid_x = (event.x() // self.grid_cell_size) * self.grid_cell_size
            grid_y = (event.y() // self.grid_cell_size) * self.grid_cell_size
            click_point = (grid_x, grid_y)
    
            # 根据当前玩家存入对应列表(避免重复点击同一个点)
            target_clicked = self.clicked0 if self.current_player == 0 else self.clicked1
            if click_point not in target_clicked:
                target_clicked.append(click_point)
    
            # 触发重绘——这是Qt绘制的核心,所有绘制必须在paintEvent中执行
            self.update()
    
  2. 在paintEvent中完成绘制与距离计算
    所有绘制操作都要放在paintEvent方法里,update()会自动触发这个方法。这里可以同时完成网格绘制、点击点标记,以及两点间距离的计算与存储:

    def paintEvent(self, event):
            painter = QPainter(self)
            # 1. 绘制网格背景
            painter.setPen(QPen(Qt.gray, 1))
            for x in range(0, self.width(), self.grid_cell_size):
                painter.drawLine(x, 0, x, self.height())
            for y in range(0, self.height(), self.grid_cell_size):
                painter.drawLine(0, y, self.width(), y)
    
            # 2. 处理玩家0的点击点与距离数据
            self._draw_player_data(painter, self.clicked0, self.distlist0, self.distdict0, Qt.blue)
            # 3. 处理玩家1的点击点与距离数据
            self._draw_player_data(painter, self.clicked1, self.distlist1, self.distdict1, Qt.red)
    
        def _draw_player_data(self, painter, clicked_list, dist_list, dist_dict, color):
            # 绘制玩家的点击标记点
            painter.setPen(QPen(color, 3))
            for point in clicked_list:
                # 在网格单元中心画个小圆标记
                painter.drawEllipse(point[0]+15, point[1]+15, 20, 20)
    
            # 计算并更新两点间距离列表与字典
            dist_list.clear()
            dist_dict.clear()
            if len(clicked_list) >= 2:
                for i in range(len(clicked_list)):
                    for j in range(i+1, len(clicked_list)):
                        p1, p2 = clicked_list[i], clicked_list[j]
                        # 用距离平方代替实际距离,避免浮点精度问题
                        dist_sq = (p1[0]-p2[0])**2 + (p1[1]-p2[1])**2
                        dist_list.append(dist_sq)
                        dist_dict[dist_sq] = (p1, p2)
    

    这里要重点强调:绝对不要在mousePressEvent或其他非paintEvent方法里直接调用QPainter绘制,Qt的绘制上下文只有在paintEvent中才是有效的,直接绘制会导致画面刷新紊乱。

二、从坐标数组中识别正方形的算法

正方形的核心几何特征可以转化为两点间距离的规律:4个点之间会产生6组距离,其中4组是边长(长度相同),2组是对角线(长度相同,且是边长的√2倍)。基于这个特征,我们可以用距离平方来避免浮点误差,实现高效识别:

from itertools import combinations

def is_valid_square(points):
    """判断给定的4个点是否能构成正方形"""
    if len(points) != 4:
        return False

    # 计算所有两点间的距离平方
    dist_sq_list = []
    for i in range(4):
        for j in range(i+1, 4):
            dx = points[i][0] - points[j][0]
            dy = points[i][1] - points[j][1]
            dist_sq = dx * dx + dy * dy
            dist_sq_list.append(dist_sq)

    # 去重后必须只有两种距离,且不能有0(排除重复点)
    unique_dists = list(set(dist_sq_list))
    if len(unique_dists) != 2 or 0 in unique_dists:
        return False

    # 验证对角线平方是边长平方的2倍
    min_dist, max_dist = sorted(unique_dists)
    if max_dist != 2 * min_dist:
        return False

    # 验证边长出现4次,对角线出现2次
    return dist_sq_list.count(min_dist) == 4 and dist_sq_list.count(max_dist) == 2

def find_all_squares(points):
    """从给定的点列表中找出所有能构成正方形的4点组合"""
    squares = []
    # 生成所有4点的组合并逐一验证
    for combo in combinations(points, 4):
        if is_valid_square(combo):
            squares.append(combo)
    return squares

额外优化(针对网格场景)

如果你的点击点都是网格单元的顶点(坐标是网格尺寸的整数倍),还可以用更高效的判断逻辑:

  • 4个点的x坐标必须恰好有两个不同的值,y坐标也恰好有两个不同的值
  • 两个x值的差的绝对值等于两个y值的差的绝对值(保证是正方形而非长方形)

这样可以跳过距离计算,直接通过坐标特征判断,效率更高。


内容的提问来源于stack exchange,提问作者user9727520

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:17:14