[PyQt] Various problems with QGraphicsView

Matt Newell newellm at blur.com
Fri Jun 6 22:02:35 BST 2008


On Tuesday 03 June 2008 09:50:50 Luke Campagnola wrote:
> Hello again,
> I am trying to implement a subclass of QGraphicsView that allows the
> user to pan its contents by dragging with the middle mouse button and
> scale by dragging with the right button. for various reasons, I do not
> wish to use the built-in pan and zoom features in QGraphicsView. I
> have read several posts online from people trying to accomplish
> similar tasks but haven't really seen any satisfactory answers, and
> I've been banging my head against this problem for a long while now.
>
> Here is my basic approach: subclass QGraphicsView, reimplement all
> mouse event functions so that I can catch the ones I recognize,
> translate/scale the graphicsview, and pass on the remaining events.
>
> Here are the problems I have run into:
>
>  - If I catch ALL mouse events and never pass any on to the
> QGraphicsView handlers, everything works fine. If, however, I do pass
> some (but not all) events to the superclass, then I start to get some
> very erratic behavior--maybe 1 out of every 100 mouse events has the
> incorrect position, sometimes more. I've attached a program that
> demonstrates this--you can drag with the right mouse button with no
> trouble until the left mouse button (which is passed on to the
> superclass) is clicked. After clicking the left mouse button, dragging
> with the right mouse button should work most of the time but
> occasionally the scene will jump erratically.
>
>  - If, on the other hand, I pass ALL events to the superclass, then
> every once in a while the mouseMoveEvent will start getting called
> repeatedly in the absence of any events, usually tying up the CPU in
> the process.
>
>  - self.translate() does not work (seemingly under any
> circumstances?). This has been discussed a few times before, and seems
> to be caused by the graphicsView wanting to automatically center the
> scene contents after translating them. My workaround looks like this
> (working example attached):
>      - in GraphicsView.__init__(), I have:
>         self.setTransformationAnchor(QtGui.QGraphicsView.NoAnchor)
>         self.setSceneRect(QtCore.QRectF(-1e100, -1e100, 1e100, 1e100))
>      - Instead of using self.translate(x, y), I use:
>          m = self.matrix()
>          m.translate(x, y)
>          self.setMatrix(m)
>  I don't know exactly all of these together produce a workable
> solution, but it seems like a lot of work to accomplish such a simple
> task.
>
>  - It is difficult to get full control of the viewport, presumably
> because QAbstractScrollArea (or possibly QGraphicsView?) likes to move
> the scene around without telling me (particularly when resizing the
> window). My workaround to this has been to reset any unexpected
> transformations like this:
>    self.resetMatrix()
>    center = self.mapToScene(self.width()/2., self.height()/2.)
>    m = QtGui.QMatrix()
>    m.translate(center.x(), center.y())
>    self.setMatrix(m)
>  I have left this out of the attached example, and as a result I know
> no reliable way of knowing where exactly my graphics are drawn within
> the widget.
>
>  - It appears that the event object that gets passed to the
> mouseEvent functions is being reused. This caused an unexpected (but
> easily fixed) problem for me: In order to allow scene panning/scaling,
> I need to record the event.pos() for every mouse event so that I can
> compare the previous event position to the current event position.
> Since the event object is reused, however, I find that the position I
> stored as the "previous position" has already been updated to the
> current position. For example:
>  ## Does not work
>  self.lastMousePosition = event.pos()
>  ## Workaround
>  self.lastMousePosition = QPoint(event.pos().x(), event.pos().y())
>
>
> In summary, my questions:
> 0. Why might I be having so much difficulty handling mouse events?
> 1. Is there a better / recommended way to accomplish the type of user
> interaction I'm after?
> 2. Is there some unambiguous way to set the transformation matrix used
> by QGraphicsView that will not be interfered with?
> 3. Is there a simple way to make translate() work?
>
> I'd love to hear any workable solutions other people have found.. I've
> been tempted to just go back to using QPainter, but the features in
> QGraphicsView are just too good to pass up :)
> Thanks!
> Luke

1.   Translate works the exact same as what you are doing with
>          m = self.matrix()
>          m.translate(x, y)
>          self.setMatrix(m)
the actual code is
void QGraphicsView::translate(qreal dx, qreal dy)
{
    Q_D(QGraphicsView);
    QTransform matrix = d->matrix;
    matrix.translate(dx, dy);
    setTransform(matrix);
}

and setMatrix simply calls setTransform.
      Setting NoAnchor should keep it from moving it behind your back whether 
you call setTransform, setMatrix, or translate.

2. For some reason QGraphicsView "replays" mouse events.  One of the places 
this is done is inside ::setTransform(called by setMatrix, translate, etc.).  
This is what is causing old mouse events to be delivered, and causes an 
infinite loops in some cases as mouseMoveEvent -> translate -> 
postEvent(previousMouseEvent) -> mouseMoveEvent...
You can avoid this by calling QGraphicsView::setInteractive(False).  If you 
want the features of interactive, you can add 

def setMatrixNoMouseReplay(self, matrix):
	self.setInteractive(False)
	self.setMatrix(matrix)
	self.setInteractive(True)

There are currently no side-affects caused by calling setInteractive.

Matt


More information about the PyQt mailing list