如何同时控制两个不同的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 <= _factor <= 100:
;
【讨论】:
以上是关于如何同时控制两个不同的QGraphicsView的主要内容,如果未能解决你的问题,请参考以下文章