Results 1 to 2 of 2

Thread: QGraphicsView viewport and QGraphicsScene/View.SceneRect() are not equal after resize

  1. #1
    Join Date
    Jan 2016
    Posts
    2
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default QGraphicsView viewport and QGraphicsScene/View.SceneRect() are not equal after resize

    Hello all,

    I am implementing a custom slider widget for a client that requires a very large dynamic range. I decided to use QGraphicsView to implement the slider's axis and two cursors because of the built-in mouse handling, and the built-in ability to convert between pixel units (View) and a separate, more appropriate coordinate system (Scene). For reasons described after the code, I have to manually keep a separate mapping of the cursor's position in the Scene to the value sent to the rest of the application, and vice versa. In addition to moving the cursors via mouseEvents, the application can set the values the cursors should take, which may be in between pixel values, so a float-to-float mapping is appropriate; the View can automatically handle by rounding.

    My mapping from Scene to "user values" assumes two invariants:
    • One pixel moved in View coordinates moves one unit in Scene coordinates.
    • The center of the view is always the origin of the scene, or as close as possible in a viewport with an even number of pixel dimensions.


    Unfortunately, I'm am currently unable to satisfy the second condition; during resizeEvents, although I manage to set the sceneRects of both the Scene and View appropriately, and center the View on the origin, the viewport and the Scene and View SceneRects go out of sync.

    This test application with two axes centered at the origin demonstrates the behavior. Try resizing the application and seeing how the axis positions move relative to the viewport. If one uncomments the assert on line 62, they will soon get an assertion failure; it is during those resizeEvents that the viewed scene and the scene origin become misaligned. Why does this occur, and is there a method to correct it so that the viewport is always resized such that the scene origin is centered?

    Qt Code:
    1. from PyQt5 import QtGui, QtCore, QtWidgets
    2.  
    3. class TestAxis(QtWidgets.QGraphicsWidget):
    4. def __init__(self):
    5. QtWidgets.QGraphicsWidget.__init__(self)
    6. self.boundingRectCopy = QtCore.QRectF()
    7.  
    8. def boundingRect(self):
    9. # Axis cannot call sceneRect() to get scene size in
    10. # boundingRect() b/c infinite recursion.
    11. return self.boundingRectCopy
    12.  
    13.  
    14. class TestAxisX(TestAxis):
    15. def __init__(self):
    16. TestAxis.__init__(self)
    17.  
    18. def paint(self, painter, op, widget):
    19. sceneRect = self.scene().sceneRect()
    20. dispMin = sceneRect.left()
    21. dispMax = sceneRect.right()
    22. painter.drawLine(dispMin, 0, dispMax, 0)
    23.  
    24.  
    25. class TestAxisY(QtWidgets.QGraphicsWidget):
    26. def __init__(self):
    27. TestAxis.__init__(self)
    28.  
    29. def paint(self, painter, op, widget):
    30. sceneRect = self.scene().sceneRect()
    31. dispMin = sceneRect.top()
    32. dispMax = sceneRect.bottom()
    33. painter.drawLine(0, dispMin, 0,dispMax)
    34.  
    35.  
    36. class TestView(QtWidgets.QGraphicsView):
    37. def __init__(self, scene, axisX, axisY):
    38. QtWidgets.QGraphicsView.__init__(self, scene)
    39. self.axisX = axisX
    40. self.axisY = axisY
    41. self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
    42. self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
    43.  
    44. def sceneRectFromViewport(self):
    45. viewportOrigin = self.mapToScene(QtCore.QPoint(0,0))
    46. viewportSize = self.mapToScene(QtCore.QPoint(self.viewport().rect().width(), \
    47. self.viewport().height()))
    48. return QtCore.QRectF(viewportOrigin, viewportSize)
    49.  
    50. # Force the scene's boundingRect to match the view/ensure axis updated.
    51. def resizeEvent(self, ev):
    52. QtWidgets.QGraphicsView.resizeEvent(self, ev)
    53. self.centerOn(0, 0) # Resizes to make the viewed scene smaller will
    54. # be off-center without this.
    55. sceneRectFromViewport = self.sceneRectFromViewport()
    56.  
    57. # View and Scene SceneRects are coupled until one or the other is
    58. # manually set. We want them coupled, but without the default rules
    59. # that Scene uses to set its SceneRect size.
    60. self.setSceneRect(sceneRectFromViewport)
    61. self.scene().setSceneRect(sceneRectFromViewport)
    62. # assert self.sceneRectFromViewport() == self.scene().sceneRect(), "viewport {0}, scene {1}".format(self.sceneRectFromViewport(), self.scene().sceneRect())
    63. self.axisX.boundingRectCopy = self.scene().sceneRect()
    64. self.axisY.boundingRectCopy = self.scene().sceneRect()
    65. self.axisX.prepareGeometryChange()
    66. self.axisY.prepareGeometryChange()
    67.  
    68.  
    69. if __name__ == "__main__":
    70. app = QtWidgets.QApplication([])
    71. win = QtWidgets.QMainWindow()
    72. axisX = TestAxisX()
    73. axisY = TestAxisY()
    74. scene = QtWidgets.QGraphicsScene()
    75. scene.addItem(axisX)
    76. scene.addItem(axisY)
    77. view = TestView(scene, axisX, axisY)
    78. win.setCentralWidget(view)
    79. win.show()
    80. app.exec()
    To copy to clipboard, switch view to plain text mode 

    Some background: Initially, I was using a zoom transformation to get the required dynamic range of the sliders- 1 pixel movement can be an increment from 1e-14 to 1e14). The ItemIgnoresTransformations flag is set on for the cursors, so the X coordinate of the center was the value reported back to the rest of the application. However, QGraphicsView is not able to handle such a large dynamic range, and floating point errors start creeping in. MouseMoveEvents are not honored at all, or the cursors scene positions are updated, but their position within the view is moved only after the mouse has moved a certain number of pixels. So unfortunately, I now have to keep track of this information (scene position to "value sent to the application" mapping) myself using a separate Python class. If there is a better way to accomplish this than QGraphicsView, I will make a separate thread to discuss.

    Thanks in advance for any help! The widget is almost complete, and fixing this is hopefully the last bug preventing me from finishing!
    Last edited by cr1901; 17th January 2016 at 07:34. Reason: reformatted to look better

  2. #2
    Join Date
    Jan 2016
    Posts
    2
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: QGraphicsView viewport and QGraphicsScene/View.SceneRect() are not equal after re

    I'm trying to debug this myself; what is the correct way to get the matrix which transforms the scene to the view (QGraphicsScene doesn't seem to have a matrix member function or property?)? I need to figure out why the view, scene, and viewport do not coincide, and looking at the actual transformation used between the former two may help me in figuring out why the transformation from scene to viewport gets out of sync.

Similar Threads

  1. Replies: 10
    Last Post: 8th April 2015, 19:38
  2. How to resize a QGraphicsScene to a QGraphicsView?
    By CassioTC in forum Qt Programming
    Replies: 2
    Last Post: 22nd March 2011, 10:03
  3. Replies: 0
    Last Post: 29th September 2009, 03:28
  4. How to enlarge sceneRect() to the viewport size
    By iw2nhl in forum Qt Programming
    Replies: 3
    Last Post: 27th August 2007, 17:39
  5. QGraphicsScene / QGraphicsView speed after resize
    By themolecule in forum Qt Programming
    Replies: 1
    Last Post: 22nd July 2007, 00:46

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.