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

How to control two different QGraphicsViews at the same time

我正在学习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,请问有什么办法吗?图片不同,但是放大,缩小,平移,我希望所有的QGraphicsviews有相同的控件。

我想同时控制两个不同的QGraphicsViews

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 创建的。

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

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

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

通过拦截ItemPositionHasChanged的变化,然后可以用新的位置发出信号,将两个场景中的两个item与它们的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)

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

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

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

其他问题:

  • 整个mapRect计算是不必要的:你可以通过self.graphicsView_1.transform().m11() * factor得到新的因子,它将current乘以给定的因子.见 QTransform docs;
  • 缩放应始终保持一致,并且由于 scale() 实际上应用于 current 缩放,结果是您 never 得到相同的值;尝试在 zoom() 的末尾添加 print(self.graphicsView_1.transform().m11()) ,你会发现经过几次缩放后 in/out 你永远无法回到精确的 1.0;
  • 使用if not 0.07 <= _factor <= 100:;