I have a setup where two QGraphicView
s display a single QGraphicsScene
. One of these views is an overview the other the detail. Imagine something like:
The rectangle marking the current boundaries of the detailed view is part of the scene. It is the white rectangle in the upper view, which I will call in the text below as "bounding-box".
What I want is to be able to click in the overview- QGraphicsView
and drag the bounding-box around to trigger a scrolling of the detail- QGraphicsView
. Obviously, the bounding-box has to be only clickable in the overview- QGraphicsView
, otherwise I would never be able to do manipulations in the detail- QGraphicsView
, because the bounding-box covers the entire detail view.
So how can I make a QGraphicsItem
be selectable only from a single QGraphicsView
or, alternatively, how do I "insert" a QGraphicsItem
only into a single QGraphicsView
? Can I perhaps nest QGraphicsScenes
so that one is the copy of another plus some extra items?
Extending the answer of Trilarion, I was able to solve the problem, by installing a Eventfilter on the overview QgraphcisView
. On the Enter
event, the dragging is enabled, on the Leave
event the dragging is disabled.
from PySide import QtGui, QtCore
# special GraphicsRectItem that is aware of its position and does something if the position is changed
class MovableGraphicsRectItem(QtGui.QGraphicsRectItem):
def __init__(self, callback=None):
super(MovableGraphicsRectItem, self).__init__()
self.setFlags(QtGui.QGraphicsItem.ItemIsMovable | QtGui.QGraphicsItem.ItemSendsScenePositionChanges)
self.setCursor(QtCore.Qt.PointingHandCursor)
self.callback = callback
def itemChange(self, change, value):
if change == QtGui.QGraphicsItem.ItemPositionChange and self.callback:
self.callback(value)
return super(MovableGraphicsRectItem, self).itemChange(change, value)
def activate(self):
self.setFlags(QtGui.QGraphicsItem.ItemIsMovable | QtGui.QGraphicsItem.ItemSendsScenePositionChanges)
self.setCursor(QtCore.Qt.PointingHandCursor)
def deactivate(self):
self.setFlags(not QtGui.QGraphicsItem.ItemIsMovable | QtGui.QGraphicsItem.ItemSendsScenePositionChanges)
self.setCursor(QtCore.Qt.ArrowCursor)
class MouseInsideFilterObj(QtCore.QObject):#And this one
def __init__(self, enterCallback, leaveCallback):
QtCore.QObject.__init__(self)
self.enterCallback = enterCallback
self.leaveCallback = leaveCallback
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.Type.Enter:
self.enterCallback(obj)
if event.type() == QtCore.QEvent.Type.Leave:
self.leaveCallback(obj)
return True
class TestClass:
def __init__(self):
self.app = QtGui.QApplication([])
# the scene with some rectangles
self.scene = QtGui.QGraphicsScene()
self.scene.addRect(30, 30, 100, 50, pen=QtGui.QPen(QtCore.Qt.darkGreen))
self.scene.addRect(150, 0, 30, 80, pen=QtGui.QPen(QtCore.Qt.darkYellow))
self.scene.addRect(80, 80, 100, 20, pen=QtGui.QPen(QtCore.Qt.darkMagenta))
self.scene.addRect(200, 10, 30, 80, pen=QtGui.QPen(QtCore.Qt.darkRed))
self.window = QtGui.QWidget()
# put two graphicsviews into the window with different scaling for each
self.layout = QtGui.QVBoxLayout(self.window)
self.v1 = QtGui.QGraphicsView(self.scene)
self.v1.setFixedSize(500, 100)
self.v1.scale(0.5, 0.5)
self.v1.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.v1.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.layout.addWidget(self.v1)
self.v2 = QtGui.QGraphicsView(self.scene)
self.v2.setFixedSize(500, 500)
self.v2.scale(5, 5)
self.v2.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.v2.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.layout.addWidget(self.v2)
mouseFilter = MouseInsideFilterObj(self.enterGV, self.leaveGV)
self.v1.installEventFilter(mouseFilter)
# the tracker rectangle
self.tracker = MovableGraphicsRectItem(lambda pos: self.v2.setSceneRect(pos.x(), pos.y(), 100, 100))
self.tracker.setRect(0, 0, 100, 100)
self.v2.setSceneRect(0, 0, 100, 100)
self.tracker.setPen(QtGui.QPen(QtCore.Qt.darkCyan))
self.scene.addItem(self.tracker)
self.window.show()
self.app.exec_()
def leaveGV(self, gv):
if gv is self.v1:
self.tracker.deactivate()
def enterGV(self, gv):
if gv is self.v1:
self.tracker.activate()
TestClass()