Open jjennings955 opened 4 years ago
If I remember correctly the signals need setup to do this, I don't remember if it was a feature flag that needs set for it to trigger the changes. See https://github.com/frmdstryr/enamlx/blob/master/enamlx/qt/qt_graphics_view.py#L943
Haven't worked on this in a while...
See https://doc.qt.io/qt-5/qgraphicsitem.html#itemChange
Edit: I think there's a bug in https://github.com/frmdstryr/enamlx/blame/master/enamlx/qt/qt_graphics_view.py#L945, it should be if change ==
That does seem to be a bug, but that function is never getting called for me.
I also tried messing with the flags. I believe you need to do
self.widget.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
for that to be fired, but I tried that and it doesn't work.
I think the error is here: https://github.com/frmdstryr/enamlx/blame/master/enamlx/qt/qt_graphics_view.py#L935
Patching itemChange like that doesn't work.
I created a small example (pure PyQt5) below: The subclassed QGraphicsRectItem that implements itemChange works, but assigning to the method of an instance does not.
from PyQt5.QtGui import QBrush, QColor
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsRectItem, QMainWindow, QApplication
class ChangeableItem(QGraphicsRectItem):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setBrush(QBrush(QColor("green")))
self.setFlags(QGraphicsRectItem.ItemSendsGeometryChanges|QGraphicsRectItem.ItemIsMovable)
def itemChange(self, change, value):
if change == QGraphicsRectItem.ItemPositionHasChanged:
print("ChangeableItem", self.pos(), self.scenePos())
return QGraphicsRectItem.itemChange(self, change, value)
class PatchedChangeableItem(QGraphicsRectItem):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setBrush(QBrush(QColor("red")))
self.setFlags(QGraphicsRectItem.ItemSendsGeometryChanges|QGraphicsRectItem.ItemIsMovable)
app = QApplication([])
window = QMainWindow()
scene = QGraphicsScene(window)
view = QGraphicsView(scene)
view.setGeometry(0, 0, 500, 500)
scene.setSceneRect(0, 0, 500, 500)
rect_good = ChangeableItem(0, 0, 100, 100)
rect_bad = PatchedChangeableItem(100, 0, 100, 100)
def patchItemChange(self, change, value):
if change == QGraphicsRectItem.ItemPositionHasChanged:
print("PatchedChangeableItem", self.pos(), self.scenePos())
return QGraphicsRectItem.itemChange(self, change, value)
rect_bad.itemChange = patchItemChange
scene.addItem(rect_good)
scene.addItem(rect_bad)
window.setCentralWidget(view)
window.resize(800, 800)
window.show()
app.exec_()
I'm not sure exactly how/where to implement it, but I think rather than hooks, you can just implement the hooked version as the default, and use QGraphicsItem.setFlag to control which itemChange events are responded to?
Thanks for looking into it, it sounds like each widget needs to be subclassed and the flag explicitly enabled.
I have something mostly working. There were some other weird gotchas in there. I'll try to post it here later.
My implementation "works" but it always segfaults. It seems to be something to do with the QT event loop. Do you have any idea what might cause this? Here's the backtrace from running it with gdb. It happens if I just move my mouse around a bunch.
Seems pretty far removed from the enaml/enamlx stuff, but I'm stumped and really have no idea how to debug it.
#1 0x000055555569b7e2 in _PyDict_LoadGlobal (globals=0x7fffec1905f0, builtins=0x7ffff7fc7910, key=0x7ffff7f4bf70)
at /tmp/build/80754af9/python_1578510683607/work/Objects/dictobject.c:1430
#2 0x0000555555714da9 in _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:2114
#3 0x000055555566f73b in function_code_fastcall (globals=<optimized out>, nargs=2, args=<optimized out>, co=0x7fffec195780)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:283
#4 0x000055555566f73b in _PyFunction_FastCallDict (func=<optimized out>, args=0x7fffffffbb00, nargs=2, kwargs=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:322
#5 0x000055555568a943 in _PyObject_Call_Prepend (callable=0x7fffec164710, obj=<optimized out>, args=0x7fffe0045f90, kwargs=0x0)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:908
#6 0x000055555567db9e in PyObject_Call (callable=0x7ffff5bd10a0, args=<optimized out>, kwargs=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:245
#7 0x00007ffff1cef481 in call_method (va=0x7fffffffbba8, fmt=0x7fffefa4a5c4 "D", method=0x7ffff5bd10a0) at siplib.c:2264
#8 0x00007ffff1cef481 in sip_api_call_procedure_method (gil_state=PyGILState_UNLOCKED, error_handler=0x7ffff51979e0 <sipVEH_QtCore_PyQt5(_sipSimpleWrapper*, PyGILState_STATE)>, py_self=0x7fffec0db910, method=0x7ffff5bd10a0, fmt=0x7fffefa4a5c4 "D") at siplib.c:2286
#9 0x00007fffef8fbe9d in sipVH_QtWidgets_12(PyGILState_STATE, void (*)(_sipSimpleWrapper*, PyGILState_STATE), _sipSimpleWrapper*, _object*, QMouseEvent*) () at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/QtWidgets.abi3.so
#10 0x00007fffef9ea20b in sipQGraphicsView::mouseMoveEvent(QMouseEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/QtWidgets.abi3.so
#11 0x00007fffef006550 in QWidget::event(QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#12 0x00007fffef0ae5ee in QFrame::event(QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#13 0x00007fffef2e606b in QGraphicsView::viewportEvent(QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#14 0x00007fffef9ec113 in sipQGraphicsView::viewportEvent(QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/QtWidgets.abi3.so
#15 0x00007ffff4b7c68d in QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*, QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Core.so.5
#16 0x00007fffeefc79d2 in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#17 0x00007fffeefcf5e8 in QApplication::notify(QObject*, QEvent*) ()
---Type <return> to continue, or q <return> to quit---
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#18 0x00007fffefa3115e in sipQApplication::notify(QObject*, QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/QtWidgets.abi3.so
#19 0x00007ffff4b7c8f8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Core.so.5
#20 0x00007fffeefcdeca in QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool, bool) () at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#21 0x00007fffef01fcb1 in QWidgetWindow::handleMouseEvent(QMouseEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#22 0x00007fffef0228fb in QWidgetWindow::event(QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#23 0x00007fffeefc79fc in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#24 0x00007fffeefce980 in QApplication::notify(QObject*, QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
#25 0x00007fffefa3115e in sipQApplication::notify(QObject*, QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/QtWidgets.abi3.so
#26 0x00007ffff4b7c8f8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Core.so.5
#27 0x00007ffff0ac5fc8 in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Gui.so.5
#28 0x00007ffff0ac74a5 in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Gui.so.5
#29 0x00007ffff0aa3e3b in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Gui.so.5
#30 0x00007fffee8cd08a in xcbSourceDispatch(_GSource*, int (*)(void*), void*) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/plugins/platforms/../../lib/libQt5XcbQpa.so.5
#31 0x00007ffff21c0417 in g_main_context_dispatch () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#32 0x00007ffff21c0650 in () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#33 0x00007ffff21c06dc in g_main_context_iteration () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#34 0x00007ffff4bd53ec in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Core.so.5
#35 0x00007ffff4b7b312 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) ()
---Type <return> to continue, or q <return> to quit---
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Core.so.5
#36 0x00007ffff4b84240 in QCoreApplication::exec() ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/Qt/lib/libQt5Core.so.5
#37 0x00007fffef8adf50 in meth_QApplication_exec_ ()
at /home/jason/miniconda3/envs/node_enaml/lib/python3.7/site-packages/PyQt5/QtWidgets.abi3.so
#38 0x00005555556b5090 in _PyMethodDef_RawFastCallKeywords (method=0x7fffefd98340 <methods_QApplication+480>, self=0x7ffff17df4b0, args=0x7fffe0033a98, nargs=<optimized out>, kwnames=<optimized out>) at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:698
#39 0x00005555556b5231 in _PyCFunction_FastCallKeywords (func=0x7fffe001bfa0, args=<optimized out>, nargs=<optimized out>, kwnames=<optimized out>) at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:734
#40 0x0000555555719a5d in call_function (kwnames=0x0, oparg=0, pp_stack=<synthetic pointer>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:4568
#41 0x0000555555719a5d in _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3093
#42 0x00005555556b468b in function_code_fastcall (globals=<optimized out>, nargs=1, args=<optimized out>, co=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:283
#43 0x00005555556b468b in _PyFunction_FastCallKeywords (func=<optimized out>, stack=0x555555aaab90, nargs=1, kwnames=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:408
#44 0x0000555555715260 in call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:4616
#45 0x0000555555715260 in _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3110
#46 0x00005555556b468b in function_code_fastcall (globals=<optimized out>, nargs=0, args=<optimized out>, co=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:283
#47 0x00005555556b468b in _PyFunction_FastCallKeywords (func=<optimized out>, stack=0x7ffff7e59d60, nargs=0, kwnames=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:408
#48 0x0000555555714fd6 in call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:4616
#49 0x0000555555714fd6 in _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3124
#50 0x000055555566e6f9 in _PyEval_EvalCodeWithName (_co=0x7ffff6407f60, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0, kwargs=0x0, kwcount=0, kwstep=2, defs=0x0, defcount=0, kwdefs=0x0, closure=0x0, name=0x0, qualname=0x0) at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3930
#51 0x000055555566f5f4 in PyEval_EvalCodeEx (_co=<optimized out>, globals=<optimized out>, locals=<optimized out>, args=<optimized out>,---Type <return> to continue, or q <return> to quit---
argcount=<optimized out>, kws=<optimized out>, kwcount=0, defs=0x0, defcount=0, kwdefs=0x0, closure=0x0)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3959
#52 0x000055555566f61c in PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:524
#53 0x00005555557248bd in builtin_exec_impl.isra.12 (locals=0x7ffff7f40f00, globals=0x7ffff7f40f00, source=0x7ffff6407f60)
at /tmp/build/80754af9/python_1578510683607/work/Python/bltinmodule.c:1079
#54 0x00005555557248bd in builtin_exec (module=<optimized out>, args=<optimized out>, nargs=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Python/clinic/bltinmodule.c.h:283
#55 0x00005555556b4f99 in _PyMethodDef_RawFastCallKeywords (method=0x5555558702e0 <builtin_methods+480>, self=0x7ffff7fc0d10, args=0x555555998560, nargs=<optimized out>, kwnames=<optimized out>) at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:655
#56 0x00005555556b5231 in _PyCFunction_FastCallKeywords (func=0x7ffff7fc7e10, args=<optimized out>, nargs=<optimized out>, kwnames=<optimized out>) at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:734
#57 0x0000555555719324 in call_function (kwnames=0x0, oparg=2, pp_stack=<synthetic pointer>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:4568
#58 0x0000555555719324 in _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3124
#59 0x000055555566e6f9 in _PyEval_EvalCodeWithName (_co=0x7ffff63dd780, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0, kwargs=0x55555593df10, kwcount=0, kwstep=1, defs=0x7ffff6409e48, defcount=5, kwdefs=0x0, closure=0x0, name=0x7ffff63f2eb0, qualname=0x7ffff63f2eb0) at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3930
#60 0x00005555556b48b5 in _PyFunction_FastCallKeywords (func=<optimized out>, stack=0x55555593dee8, nargs=5, kwnames=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:433
#61 0x0000555555714fd6 in call_function (kwnames=0x0, oparg=<optimized out>, pp_stack=<synthetic pointer>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:4616
#62 0x0000555555714fd6 in _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3124
#63 0x000055555566e6f9 in _PyEval_EvalCodeWithName (_co=0x7ffff63d1660, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0, kwargs=0x0, kwcount=0, kwstep=2, defs=0x7ffff7ec1fa8, defcount=1, kwdefs=0x0, closure=0x0, name=0x7ffff63f3710, qualname=0x7ffff63f3710) at /tmp/build/80754af9/python_1578510683607/work/Python/ceval.c:3930
#64 0x000055555566f805 in _PyFunction_FastCallDict (func=<optimized out>, args=0x7ffff64383d8, nargs=2, kwargs=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Objects/call.c:376
#65 0x0000555555769837 in pymain_run_module (modname=<optimized out>, set_argv0=1)
at /tmp/build/80754af9/python_1578510683607/work/Modules/main.c:327
#66 0x000055555577bfcb in pymain_run_python (pymain=0x7fffffffd740)
---Type <return> to continue, or q <return> to quit---
at /tmp/build/80754af9/python_1578510683607/work/Modules/main.c:2871
#67 0x000055555577bfcb in pymain_main (pymain=0x7fffffffd740) at /tmp/build/80754af9/python_1578510683607/work/Modules/main.c:3414
#68 0x000055555577c0bc in _Py_UnixMain (argc=<optimized out>, argv=<optimized out>)
at /tmp/build/80754af9/python_1578510683607/work/Modules/main.c:3449
#69 0x00007ffff77e6b97 in __libc_start_main (main=
0x5555556500a0 <main>, argc=4, argv=0x7fffffffd898, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffd888) at ../csu/libc-start.c:310
#70 0x0000555555724990 in _start () at ../sysdeps/x86_64/elf/start.S:103
Without any code its hard to tell but its most likely trying to load a ref that was already destroyed.
I'm not sure if this is intended/known or not, but when items in a GraphicsView are set to movable, the position changes coming from mouse interactions don't get reflected/propagated through things that may be bound to item.position.
How can I achieve this?
Example code below