【问题标题】:Puzzling behavior of QGraphicsView.fitInViewQGraphicsView.fitInView 的令人费解的行为
【发布时间】:2020-10-30 14:30:56
【问题描述】:

我正在尝试制作一个带有两个 QGraphicsViews 的小部件来并排比较两个图像。当用户放大其中一个视图时,另一个应该放大相同的区域。我的 zoomIn 函数发送一个带有可见场景矩形的信号,另一个视图中的一个插槽捕获此信号并调用 QGraphicsView.fitInView(rectangle) 以使相同的图像区域可见。 令我惊讶的是,fitInView 似乎将矩形 (x, y, width, height) 视为 (x, y, x + width, y + height)。 这是一个错误还是我错过了什么?

这是我的代码示例:

QGraphicsView 子类

import logging
from PyQt5 import (
    QtWidgets as qw,
    QtCore as qc,
    QtGui as qg
)

class Display(qw.QGraphicsView):
    sigViewportAreaChanged = qc.pyqtSignal(qc.QRectF)

    def __init__(self, *args, **kwargs):
        super(Display, self).__init__(*args, **kwargs)
        ...
        self.zoomInAction = qw.QAction('Zoom in')
        self.zoomInAction.triggered.connect(self.zoomIn)

    @qc.pyqtSlot()
    def zoomIn(self):
        self.scale(1.2, 1.2)
        pos = self.viewport().pos()
        size = self.viewport().size()
        logging.debug(f'{self.objectName()}: viewport:: pos: {pos}, size: {size}')
        pos = self.mapToScene(pos)
        size = self.mapToScene(size.width(), size.height())
        logging.debug(f'{self.objectName()}: scene:: pos: {pos}, size: {size}')
        view_area = qc.QRectF(pos.x(), pos.y(), size.x(), size.y())
        logging.debug(
            f'Emitting changed viewport area, {view_area},\n{mat}')
        self.sigViewportAreaChanged.emit(view_area)

    def setViewportRect(self, rect: qc.QRectF) -> None:
        logging.debug(f'{self.objectName()} <- {self.sender().objectName()}\nFit view to {rect}')
        pos = self.viewport().pos()
        size = self.viewport().size()
        logging.debug(f'{self.objectName()}: before fitting area: viewport:: pos: {pos}, size: {size}')
        pos = self.mapToScene(pos)
        size = self.mapToScene(size.width(), size.height())
        logging.debug(f'{self.objectName()}: before fitting area: scene:: pos: {pos}, size: {size}')
        # self.fitInView(rect)  # Incorrectly sets the visible area
        self.fitInView(rect.x(), rect.y(), rect.width() - rect.x(), rect.height() - rect.y())  # this works
        pos = self.viewport().pos()
        size = self.viewport().size()
        logging.debug(f'{self.objectName()}: after fitting area: viewport:: pos: {pos}, size: {size}')
        pos = self.mapToScene(pos)
        size = self.mapToScene(size.width(), size.height())
        logging.debug(f'{self.objectName()}: after fitting area: scene:: pos: {pos}, size: {size}')  # this suggests that the new viewport is (x, y, x+width, y+height) instead of (x, y, width, height)

容器小部件:

class ReviewWidget(qw.QWidget):
    """A widget with two panes for comparing images"""

    def __init__(self, *args, **kwargs):
        super(ReviewWidget, self).__init__(*args, **kwargs)
        layout = qw.QVBoxLayout()
        self.before = Display()
        self.before.setObjectName('Left')
        self.after = Display()
        self.after.setObjectName('Right')
        self.before.setHorizontalScrollBarPolicy(qc.Qt.ScrollBarAlwaysOn)
        self.before.setVerticalScrollBarPolicy(qc.Qt.ScrollBarAlwaysOn)
        self.after.setHorizontalScrollBarPolicy(qc.Qt.ScrollBarAlwaysOn)
        self.after.setVerticalScrollBarPolicy(qc.Qt.ScrollBarAlwaysOn)

        panes_layout = qw.QHBoxLayout()
        panes_layout.addWidget(self.before)
        panes_layout.addWidget(self.after)
        layout.addLayout(panes_layout)
        self.setLayout(panes_layout)
        self.make_actions()

    def tieViews(self, tie):
        if tie:
            self.before.sigViewportAreaChanged.connect(self.after.setViewportRect)
            self.after.sigViewportAreaChanged.connect(self.before.setViewportRect)
       else:
            self.before.disconnect(self.before.sigViewportAreaChanged)
            self.after.disconnect(self.after.sigViewportAreaChanged)

    def make_actions(self):
        self.tieViewsAction = qw.QAction('Zoom views together')
        self.tieViewsAction.setCheckable(True)
        self.tieViewsAction.triggered.connect(self.tieViews)

【问题讨论】:

    标签: python pyqt pyqt5 qgraphicsview


    【解决方案1】:

    我相信您计算size 的方式是错误的。您应该相对于pos 的转换值来计算它。既然知道宽度和高度的值,就可以根据pos的场景值计算view_area的右下角。

    viewport_pos = self.viewport().pos()
    scene_pos = self.mapToScene(viewport_pos)
    
    width = self.viewport().size().width()
    height = self.viewport().size().height()
    
    lower_right_corner = QPointF(width - scene_pos.x(), height() - scene_pos.y())
    

    如果没有减法,您会假设 view_area 的起源是 (0, 0),而实际上它是 pos

    【讨论】:

    • 我认为宽度和高度应该与原点无关,因为它们测量的是空间间隔(即,线的长度不应根据坐标系的原点而改变)。现在我知道尺寸上的mapToScene 将其视为一个点并翻译它以更改坐标。
    【解决方案2】:

    好的,我在阅读@Roney Gomes 的回答后发现了我的错误。

    为了将尺寸缩放到场景坐标系,我将尺寸作为一个点传递给mapToScene

    但是点和大小在概念上是不同的,虽然大小不依赖于坐标系的原点,但点是。 mapToScene,将其参数视为一个点,正在翻译它以反映新的坐标系。

    因此,我需要减去新坐标系的原点(实际上是视口的左上角)以恢复大小。

    更简单的解决方案是直接映射视口矩形:

    rect = self.mapToScene(self.viewport().rect())  
    rect = rect.boundingRect() # mapToScene(rect) returns QPolygonF
    self.sigViewportAreaChanged.emit(rect)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-02
      • 2020-06-19
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 2011-03-27
      • 2010-11-15
      相关资源
      最近更新 更多