如何同时控制两个不同的QGraphicsView

Posted

技术标签:

【中文标题】如何同时控制两个不同的QGraphicsView【英文标题】:How to control two different QGraphicsViews at the same time 【发布时间】:2021-09-18 13:14:52 【问题描述】:

我正在学习QGraphicsView。

self._item = QGraphicsPixmapItem()
self._item.setFlags(QGraphicsPixmapItem.ItemIsFocusable | QGraphicsPixmapItem.ItemIsMovable)

self._scene = QGraphicsScene(self)

self.graphicsView_1.setScene(self._scene)

self._scene.addItem(self._item)

self._item.setPixmap(QPixmap("image_path"))

我知道上面的序列是输出图片的方式。

第二行“QGraphicsPixmapItem.ItemIsMovable”这是鼠标选项。

我知道这样输出图片会用鼠标移动图片。

self._item = QGraphicsPixmapItem()
self._item.setFlags(QGraphicsPixmapItem.ItemIsFocusable | QGraphicsPixmapItem.ItemIsMovable)

self._scene = QGraphicsScene(self)

self.graphicsView_1.setScene(self._scene)
self.graphicsView_2.setScene(self._scene)

self._scene.addItem(self._item)

self._item.setPixmap(QPixmap("image_path"))

然后我制作了另一个 QGraphicsView 并应用了与 1 中相同的场景。

在这种情况下,如果您在一个 QGraphicsView 中单击并拖动鼠标,则会同时应用两者。

但是,这样做的缺点是必须观看相同的场景。

我想控制多个屏幕,同时输出不同的图片到QGraphicsView,但是有办法吗?图片不一样,但是放大、缩小、平移,我希望所有的QGraphicsview都拥有相同的控件。

我想同时控制两个不同的QGraphicsView。

import sys

from PyQt5 import QtWidgets

from PyQt5.QtCore import QRectF
from PyQt5.QtCore import Qt

from PyQt5.QtGui import QPixmap
from PyQt5.QtGui import QPainter
from PyQt5.QtGui import QColor

from PyQt5.QtWidgets import QGraphicsPixmapItem
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QGraphicsScene

from ui.preview_test import Ui_MainWindow

class mainwindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.graphicsView_1.setCursor(Qt.OpenHandCursor)
        self.graphicsView_1.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.graphicsView_2.setCursor(Qt.OpenHandCursor)
        self.graphicsView_2.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.graphicsView_1.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.graphicsView_1.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.graphicsView_2.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.graphicsView_2.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.graphicsView_1.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing |
                            QPainter.SmoothPixmapTransform)
        self.graphicsView_2.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing |
                QPainter.SmoothPixmapTransform)                   
        self.graphicsView_1.setBackgroundBrush(Qt.black)
        self.graphicsView_2.setBackgroundBrush(Qt.black)

        self._item = QGraphicsPixmapItem()
        self._item.setFlags(QGraphicsPixmapItem.ItemIsFocusable |
                            QGraphicsPixmapItem.ItemIsMovable)
        
        self._scene = QGraphicsScene(self) 
        self.graphicsView_1.setScene(self._scene)
        self.graphicsView_2.setScene(self._scene)
        
        self._scene.addItem(self._item)
        
        self._delta = 0.1 

        self._item.setPixmap(QPixmap("C:/Users/wlxo0/Desktop/960x540 image.png"))
        

        self.show()

    def setBackground(self, color):
        if isinstance(color, QColor):
            self.graphicsView_1.setBackgroundBrush(color)
        elif isinstance(color, (str, Qt.GlobalColor)):
            color = QColor(color)
            if color.isValid():
                self.graphicsView_1.setBackgroundBrush(color)

    def mouseMoveEvent(self, QMouseEvent):
        self.mouse_x = QMouseEvent.x()
        self.mouse_y = QMouseEvent.y()

        print(f"X : self.frame_2.x() <= self.mouse_x <= self.frame_2.x() + self.frame_2.width()-1, Y : self.frame_2.y() <= self.mouse_y <= self.frame_2.y() + self.frame_2.height()-1")

    def wheelEvent(self, event):
        if event.angleDelta().y() > 0:
            self.zoomIn()
        else:
            self.zoomOut()

    def zoomIn(self):
        self.zoom(1 + self._delta)

    def zoomOut(self):
        self.zoom(1 - self._delta)

    def zoom(self, factor):
        _factor = self.graphicsView_1.transform().scale(
            factor, factor).mapRect(QRectF(0, 0, 1, 1)).width()
        _factor2 = self.graphicsView_2.transform().scale(
            factor, factor).mapRect(QRectF(0, 0, 1, 1)).width()
        if _factor < 0.07 or _factor > 100:
            return
        if _factor2 < 0.07 or _factor2 > 100:
            return
        
        self.graphicsView_1.scale(factor, factor)
        self.graphicsView_2.scale(factor, factor)

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = mainwindow()
    app.exec_()

Original_Code我正在编辑我在 github 上找到的代码。

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.graphicsView_1 = QtWidgets.QGraphicsView(self.centralwidget)
        self.graphicsView_1.setObjectName("graphicsView_1")
        self.horizontalLayout.addWidget(self.graphicsView_1)
        self.graphicsView_2 = QtWidgets.QGraphicsView(self.centralwidget)
        self.graphicsView_2.setObjectName("graphicsView_2")
        self.horizontalLayout.addWidget(self.graphicsView_2)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

UI 是使用 Qt Designer 创建的。

【问题讨论】:

您必须为每个视图使用不同的QGraphicsScene 实例。然后手动同步您想要匹配的部分,例如缩放和位置。 【参考方案1】:

不幸的是,只有部分答案,因为您想要实现的目标需要非常复杂的实现。

第一个要求是为每个视图使用一个独特的场景,因为如果你使用相同的场景,你显然拥有相同的内容。

然后,您可以使用 QGraphicsObject 作为像素图项的“容器”。基本 QGraphicsItems 不继承自 QObject(信号的基本要求),而 QGraphicsObject 继承自 QGraphicsItem 和 QObject。

通过拦截ItemPositionHasChanged的变化,就可以发出带有新位置的信号,并将两个场景中的两个项目与它们的setPos联系起来。

class PixmapItem(QtWidgets.QGraphicsObject):
    moved = QtCore.pyqtSignal(QtCore.QPointF)
    def __init__(self, parent=None):
        super().__init__(parent)
        self.pixmapItem = QtWidgets.QGraphicsPixmapItem(self)
        self.setFlags(
            self.ItemIsFocusable | 
            self.ItemIsMovable | 
            self.ItemSendsGeometryChanges)

    def setPixmap(self, pixmap):
        self.pixmapItem.setPixmap(pixmap)
        self.pixmapItem.setPos(-self.pixmapItem.boundingRect().center())

    def boundingRect(self):
        return self.childrenBoundingRect()

    def itemChange(self, change, value):
        if change == self.ItemPositionHasChanged:
            self.moved.emit(value)
        return super().itemChange(change, value)

    def paint(self, *args):
        pass


class mainwindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        # ...
        self.scene1 = QtWidgets.QGraphicsScene()
        self.graphicsView_1.setScene(self.scene1)
        self.item1 = PixmapItem()
        self.scene1.addItem(self.item1)
        self.scene2 = QtWidgets.QGraphicsScene()
        self.graphicsView_2.setScene(self.scene2)
        self.item2 = PixmapItem()
        self.scene2.addItem(self.item2)

        self.item1.setPixmap(QPixmap("image1.png"))
        self.item2.setPixmap(QPixmap("image2.png"))

        self.item1.moved.connect(self.item2.setPos)
        self.item2.moved.connect(self.item1.setPos)

如上所述,这会起作用,但还不够:

您正在主窗口上实现滚轮事件,这是有问题的:首先,您会从 任何 位置(除了处理滚轮事件的小部件和 接受它们而不将其传播给父级),然后,一旦图像被缩放,滚轮也会滚动内容,因为隐藏滚动条不会妨碍它们的功能; 隐藏滚动条不会阻止视图在需要时滚动场景,当图像大于视图并且可见场景矩形需要在移动项目后进行调整时,这可能会成为问题; 如果图像大小不同,则同样的问题最为重要,这可能会导致每次调整窗口大小时出现闪烁和其他定位问题;

对此没有简单的解决方案:提供此类功能肯定需要对图形视图进行子类化,以提供适合每个视图的自定义行为,以实现它们之间的同步。 p>

其他问题:

整个mapRect 计算是不必要的:您只需执行self.graphicsView_1.transform().m11() * factor 即可获得新因子,它将当前 缩放乘以给定因子。见QTransform docs; 缩放应该始终保持一致,并且由于scale() 实际上应用于当前 缩放,结果是您永远不会获得相同的值;尝试在zoom() 的末尾添加print(self.graphicsView_1.transform().m11()),您会发现经过几次放大/缩小后,您永远无法恢复到精确的1.0; 使用if not 0.07 &lt;= _factor &lt;= 100:;

【讨论】:

以上是关于如何同时控制两个不同的QGraphicsView的主要内容,如果未能解决你的问题,请参考以下文章

如何一键同时控制两个 UIScrollView?

我应该如何从一个控制器操作返回两个对象列表?

如何通过两个不同的子句同时查询两个字段的计数?

如何同时访问两个大小不同的数组? [关闭]

如何保存同时播放两个曲目的wav文件?在不同的体积

如何在两个不同的表中同时插入值