[Moved] QGraphicsItem::itemChange not called
-
I can't get this to work. Code follows. Basically, I want itemChange() to be called when I scale a QGraphicsItem. The result of the code is (whats printed on the console):
itemChange PySide.QtGui.QGraphicsItem.GraphicsItemChange.ItemFlagsChange 2048
itemChange PySide.QtGui.QGraphicsItem.GraphicsItemChange.ItemSceneChange <PySide.QtGui.QGraphicsScene object at 0x118bb48>
Mouse pressed: scaling slotItem, should trigger itemChange()That is, itemChange is called a few times, but not when I scale the item (when the mouse is pressed.)
I am thinking about why itemChange() is documented in the same group as events, but not described as an event (the documentation calls it a "notification"), and not having the same parameters as events (not a QEvent, but (change, value)). My best quess is that it does not go through the event loop like events.
Baffled.
@from PySide.QtCore import *
from PySide.QtGui import *
import sys'''
This tests use of itemChanged() method of QGraphicItem
'''class SlotItem(QGraphicsEllipseItem):
SIZE = 40
def init(self, pos):
QGraphicsEllipseItem.init(self)
self.setRect(pos.x(), pos.y(), self.SIZE, self.SIZE)# Enable callback to itemChanged on scale() self.setFlags(QGraphicsItem.ItemSendsGeometryChanges) def itemChange(self, change, value): print "itemChange", change, value return super(SlotItem,self).itemChange(change, value) # <<<<< Must return the result !!! Edited
Subclass to scale on scroll wheel
class DiagramView(QGraphicsView):
def init(self, scene, *args):
QGraphicsView.init(self, scene, *args)self.scene = scene self.text = QGraphicsTextItem("Testing itemChanged() notification") self.text.setTextInteractionFlags(Qt.TextEditorInteraction) scene.addItem(self.text) self.slotItem = SlotItem(QPointF(30,30)) scene.addItem(self.slotItem) def mousePressEvent(self, event): ''' Start a drag''' print "Mouse pressed: scaling slotItem, should trigger itemChange()" self.slotItem.scale(2.0, 2.0)
class MainWindow(QMainWindow):
def init(self, *args):
QMainWindow.init(self, *args)
self.scene = QGraphicsScene()
self.view = DiagramView(self.scene)
self.view.setRenderHint(QPainter.Antialiasing)
self.setCentralWidget(self.view)def main(args):
app = QApplication(args)
mainWindow = MainWindow()
mainWindow.setGeometry(100, 100, 500, 80)
mainWindow.show()# Qt Main loop sys.exit(app.exec_())
if name == "main":
main(sys.argv)@ -
Nevermind, I found the bug in the above code and edited it so it now works.
But the code would better be:
@def itemChange(self, change, value):
print "itemChange", change, value
return value@The critical part of the documentation is "The default implementation does nothing, and returns value." More precisely it could say: simply returns the passed parameter having name value, unchanged.
There is no reason to call the base class implementation, but you MUST return a result having the same type as the parameter named value.
That is, itemChange() is a hook: it is called when Qt has a value for a change and gives you the opportunity to alter the value before Qt uses the value. If you don't return a value properly, the results are unpredictable. The default implementation is an idempotent hook: transmits the value parameter unchanged.
Note that itemChange(), like sceneEvent() and a few other methods in the list of protected methods of QGraphicsItem have non-void return value. Be careful to return a value that is not None. Apparently Qt does not do run-time error checking (or at least in the Python binding, a returned value of None passes any type checking that Qt does.)
The documentation for itemChange() says "adjustments can be made." This is poor technical writing because it uses the passive voice. It would be better stated: in your reimplementation, you can adjust the value, but you should always return a result having the same type as the parameter named value.
The code and documentation would also be better if longer names were used: itemChange(changeType, changeValue). As you can see in my discussion above, it is difficult for readers to distinguish between the parameter having name value, and the value of that parameter. In other words, the word "value" is too abstract and generic. "Return a value" is too easily confused with "return the parameter having name: value".