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

父QWidget滚轮缩放时子部件尺寸按比例适配的实现问题

问题描述
  • 现有父QWidget通过paintEvent方法绘制网格,内部包含子QWidget,子部件仅能在网格单元格间移动(移动相关方法已重写)。需实现以下功能:
    1. 网格缩放:滚动鼠标滚轮时,屏幕可容纳的单元格数量对应增减,此功能已在wheelEvent方法中实现。
    2. 子部件尺寸适配缩放:网格缩放时,子部件尺寸需随单元格大小按比例变化。目前该功能失效,尝试使用坐标变换但未生效。
  • 特殊说明:未采用QGraphicsView实现,因为无法在QGraphicsScene中放置嵌入第三方应用窗口的部件,而程序需要拦截第三方窗口并将其嵌入部件后添加到网格中,恳请提供解决方案。

现有代码

from PyQt5.QtWidgets import QWidget, QStyleOption, QPainter, QGridLayout, QPushButton, QApplication
from PyQt5.QtGui import QPen, QColor, QTransform
from PyQt5.QtCore import QPoint, QRectF

class MoveWidget(QWidget):

    def __init__(self):
        super(MoveWidget, self).__init__()
        self.click_rect = self.rect()
        self.selected_edge = None
        self.click_pos = None
        self.setAutoFillBackground(True)
        self.setStyleSheet("background-color: yellow; "
                           "color: red;"
                           "border: 2px solid blue;")
        grid = QGridLayout(self)
        self.setLayout(grid)
        grid.addWidget(QPushButton("fdfdsfdsfdsf"), 0, 0)
        grid.addWidget(QPushButton("1111111111111"), 1, 0)
        self.setGeometry(0, 0, 300, 300)

    def paintEvent(self, a0) -> None:
        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)
        self.style().drawPrimitive(QStyle.PrimitiveElement.PE_Widget, opt, painter, self)

    def mousePressEvent(self, event):
        self.click_pos = event.pos()
        rect = self.rect()
        top = abs(rect.top())
        bottom = abs(rect.bottom())
        left = abs(rect.left())
        right = abs(rect.right())

        if abs(left - abs(self.click_pos.x())) < 30 and (
                abs(bottom - abs(self.click_pos.y())) > 30) and (abs(top - abs(self.click_pos.y())) > 30):
            self.selected_edge = 'left'

        elif abs(right - abs(self.click_pos.x())) < 30 and (
                abs(bottom - abs(self.click_pos.y())) > 30) and (abs(top - abs(self.click_pos.y())) > 30):
            self.selected_edge = 'right'

        elif abs(top - abs(self.click_pos.y())) < 30 and (abs(right - abs(self.click_pos.x())) > 30) and (
                abs(left - abs(self.click_pos.x())) > 30):
            self.selected_edge = 'top'

        elif abs(bottom - abs(self.click_pos.y())) < 30 and (
                abs(right - abs(self.click_pos.x())) > 30) and (abs(left - abs(self.click_pos.x())) > 30):
            self.selected_edge = 'bottom'

        elif (abs(bottom - abs(self.click_pos.y())) < 30) and (abs(right - abs(self.click_pos.x())) < 30):
            self.selected_edge = 'diag_bottom_right'

        elif (abs(bottom - abs(self.click_pos.y())) < 30) and (abs(left - abs(self.click_pos.x())) < 30):
            self.selected_edge = 'diag_bottom_left'

        elif (abs(top - abs(self.click_pos.y())) < 30) and (abs(right - abs(self.click_pos.x())) < 30):
            self.selected_edge = 'diag_top_right'

        elif (abs(top - abs(self.click_pos.y())) < 30) and (abs(left - abs(self.click_pos.x())) < 30):
            self.selected_edge = 'diag_top_left'

        else:
            self.selected_edge = None
        self.click_pos = event.pos()
        self.click_rect = rect
        self.cl = self.mapFromParent(QPoint(rect.x(), rect.y()))
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        pos = event.pos()

        x_diff = (round((pos.x() - self.click_pos.x()) / 300) * 300)
        y_diff = (round((pos.y() - self.click_pos.y()) / 300) * 300)

        rect = QRectF(self.click_rect)

        if self.selected_edge is None:
            self.move(self.mapToParent(QPoint(x_diff, y_diff)))
            print(self.pos())
            return
        elif self.selected_edge == 'top':
            rect.adjust(0, y_diff, 0, 0)

        elif self.selected_edge == 'left':
            rect.adjust(x_diff, 0, 0, 0)

        elif self.selected_edge == 'bottom':
            rect.adjust(0, 0, 0, y_diff)

        elif self.selected_edge == 'right':
            rect.adjust(0, 0, (x_diff), 0)

        elif self.selected_edge == 'diag_bottom_right':
            rect.adjust(0, 0, (x_diff), (x_diff))

        elif self.selected_edge == 'diag_top_right':
            rect.adjust(0, -(x_diff), (x_diff), 0)

        elif self.selected_edge == 'diag_bottom_left':
            rect.adjust((x_diff), 0, 0, -(x_diff))

        elif self.selected_edge == 'diag_top_left':
            rect.adjust((x_diff), (x_diff), 0, 0)

        s = self.mapToParent(QPoint(int(rect.x()), int(rect.y())))
        d = rect.getCoords()
        self.setGeometry(s.x(), s.y(), int(d[2]-d[0]), int(d[3]-d[1]))
        print(self.pos())

class WidMain(QWidget):

    def __init__(self):
        super().__init__()
        self.setStyleSheet("background: transparent;")
        self.setAutoFillBackground(True)
        self.movewid = MoveWidget()
        self.movewid.setParent(self)
      
        self.factor = 1.0
        self.cell_size = 300  # 初始单元格尺寸

    def wheelEvent(self, event):
        # 调整缩放因子,限制缩放范围避免过度缩放
        delta = event.angleDelta().y()
        self.factor *= 1.01 ** (delta / 15.0)
        self.factor = max(0.2, min(self.factor, 5.0))
        
        # 更新单元格尺寸
        self.cell_size = 300 * self.factor
        
        # 重新计算子部件的位置和尺寸,确保对齐网格
        child = self.movewid
        # 获取子部件在父坐标系中的位置,按网格对齐
        parent_pos = child.pos()
        grid_x = round(parent_pos.x() / self.cell_size) * self.cell_size
        grid_y = round(parent_pos.y() / self.cell_size) * self.cell_size
        # 设置子部件尺寸为当前单元格大小
        child.setGeometry(grid_x, grid_y, self.cell_size, self.cell_size)
        
        self.update()
        print("缩放因子", self.factor)
        print("当前单元格尺寸", self.cell_size)

    def paintEvent(self, a0) -> None:
        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)
        self.style().drawPrimitive(QStyle.PrimitiveElement.PE_Widget, opt, painter, self)

        pen = QPen(QColor("blue"))
        pen.setWidth(5)
        painter.setPen(pen)
        
        # 应用缩放变换绘制网格
        transform = QTransform()
        transform.scale(self.factor, self.factor)
        painter.setTransform(transform)

        # 绘制足够多的网格线,覆盖可视区域
        max_line = max(self.width(), self.height()) * 10
        for i in range(0, max_line, 300):
            painter.drawLine(i, 0, i, max_line)
        for i in range(0, max_line, 300):
            painter.drawLine(0, i, max_line, i)

        super().paintEvent(a0)

if __name__ == '__main__':
    app = QApplication([])
    w = WidMain()
    w.resize(800, 600)
    w.show()
    app.exec()

内容的提问来源于stack exchange,提问作者Валентин

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:52:03