父QWidget滚轮缩放时子部件尺寸按比例适配的实现问题
问题描述
- 现有父QWidget通过
paintEvent方法绘制网格,内部包含子QWidget,子部件仅能在网格单元格间移动(移动相关方法已重写)。需实现以下功能:- 网格缩放:滚动鼠标滚轮时,屏幕可容纳的单元格数量对应增减,此功能已在
wheelEvent方法中实现。 - 子部件尺寸适配缩放:网格缩放时,子部件尺寸需随单元格大小按比例变化。目前该功能失效,尝试使用坐标变换但未生效。
- 网格缩放:滚动鼠标滚轮时,屏幕可容纳的单元格数量对应增减,此功能已在
- 特殊说明:未采用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,提问作者Валентин
相关产品推荐
相关产品推荐

