napari-plot-profile icon indicating copy to clipboard operation
napari-plot-profile copied to clipboard

WIP use mouse_drag_callbacks instead of timer

Open haesleinhuepf opened this issue 4 years ago • 3 comments
trafficstars

This will replace the QTimer with a callback sent by the shapes layer...

haesleinhuepf avatar Aug 25 '21 19:08 haesleinhuepf

This doesn't work at the moment, because the callback doesn't pass "self" as parameter...

Error log:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\users\rober\miniconda3\envs\bio1\lib\site-packages\vispy\app\backends\_qt.py in mousePressEvent(self=<vispy.app.backends._qt.CanvasBackendDesktop object>, ev=<PyQt5.QtGui.QMouseEvent object>)
    454         if self._vispy_canvas is None:
    455             return
--> 456         self._vispy_mouse_press(
        self._vispy_mouse_press = <bound method BaseCanvasBackend._vispy_mouse_press of <vispy.app.backends._qt.CanvasBackendDesktop object at 0x000002C531FD5550>>
        global native = undefined
        ev = <PyQt5.QtGui.QMouseEvent object at 0x000002C55BAC7CA0>
        global pos = undefined
        ev.pos.x = undefined
        ev.pos.y = undefined
        global button = undefined
        global BUTTONMAP.get = <built-in method get of dict object at 0x000002C52A20DE80>
        ev.button = <built-in method button of QMouseEvent object at 0x000002C55BAC7CA0>
        global modifiers = undefined
        self._modifiers = <bound method QtBaseCanvasBackend._modifiers of <vispy.app.backends._qt.CanvasBackendDesktop object at 0x000002C531FD5550>>
    457             native=ev,
    458             pos=(ev.pos().x(), ev.pos().y()),

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\vispy\app\base.py in _vispy_mouse_press(self=<vispy.app.backends._qt.CanvasBackendDesktop object>, **kwargs={'button': 1, 'buttons': [], 'last_event': <MouseEvent blocked=False button=None buttons=[]...urces=[] time=1629919884.9780061 type=mouse_move>, 'last_mouse_press': None, 'modifiers': (), 'native': <PyQt5.QtGui.QMouseEvent object>, 'pos': (71, 341), 'press_event': None})
    179         # default method for delivering mouse press events to the canvas
    180         kwargs.update(self._vispy_mouse_data)
--> 181         ev = self._vispy_canvas.events.mouse_press(**kwargs)
        ev = undefined
        self._vispy_canvas.events.mouse_press = <vispy.util.event.EventEmitter object at 0x000002C531FD1E80>
        kwargs = {'native': <PyQt5.QtGui.QMouseEvent object at 0x000002C55BAC7CA0>, 'pos': (71, 341), 'button': 1, 'modifiers': (), 'buttons': [], 'press_event': None, 'last_event': <MouseEvent blocked=False button=None buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002C55BAC7CA0> pos=[ 71 341] press_event=None source=None sources=[] time=1629919884.9780061 type=mouse_move>, 'last_mouse_press': None}
    182         if self._vispy_mouse_data['press_event'] is None:
    183             self._vispy_mouse_data['press_event'] = ev

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\vispy\util\event.py in __call__(self=<vispy.util.event.EventEmitter object>, *args=(), **kwargs={'button': 1, 'buttons': [], 'last_event': <MouseEvent blocked=False button=None buttons=[]...urces=[] time=1629919884.9780061 type=mouse_move>, 'last_mouse_press': None, 'modifiers': (), 'native': <PyQt5.QtGui.QMouseEvent object>, 'pos': (71, 341), 'press_event': None})
    451                     raise RuntimeError('EventEmitter loop detected!')
    452
--> 453                 self._invoke_callback(cb, event)
        self._invoke_callback = <bound method EventEmitter._invoke_callback of <vispy.util.event.EventEmitter object at 0x000002C531FD1E80>>
        cb = <bound method QtViewer.on_mouse_press of <napari._qt.qt_viewer.QtViewer object at 0x000002C52E8381F0>>
        event = <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002C55BAC7CA0> pos=[ 71 341] press_event=None source=None sources=[] time=1629919884.9996626 type=mouse_press>
    454                 if event.blocked:
    455                     break

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\vispy\util\event.py in _invoke_callback(self=<vispy.util.event.EventEmitter object>, cb=<bound method QtViewer.on_mouse_press of <napari._qt.qt_viewer.QtViewer object>>, event=<MouseEvent blocked=False button=1 buttons=[] de...rces=[] time=1629919884.9996626 type=mouse_press>)
    469             cb(event)
    470         except Exception:
--> 471             _handle_exception(self.ignore_callback_errors,
        global _handle_exception = <function _handle_exception at 0x000002C526D14550>
        self.ignore_callback_errors = False
        self.print_callback_errors = 'reminders'
        self = <vispy.util.event.EventEmitter object at 0x000002C531FD1E80>
        global cb_event = undefined
        cb = <bound method QtViewer.on_mouse_press of <napari._qt.qt_viewer.QtViewer object at 0x000002C52E8381F0>>
        event = <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002C55BAC7CA0> pos=[ 71 341] press_event=None source=None sources=[] time=1629919884.9996626 type=mouse_press>
    472                               self.print_callback_errors,
    473                               self, cb_event=(cb, event))

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\vispy\util\event.py in _invoke_callback(self=<vispy.util.event.EventEmitter object>, cb=<bound method QtViewer.on_mouse_press of <napari._qt.qt_viewer.QtViewer object>>, event=<MouseEvent blocked=False button=1 buttons=[] de...rces=[] time=1629919884.9996626 type=mouse_press>)
    467     def _invoke_callback(self, cb, event):
    468         try:
--> 469             cb(event)
        cb = <bound method QtViewer.on_mouse_press of <napari._qt.qt_viewer.QtViewer object at 0x000002C52E8381F0>>
        event = <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002C55BAC7CA0> pos=[ 71 341] press_event=None source=None sources=[] time=1629919884.9996626 type=mouse_press>
    470         except Exception:
    471             _handle_exception(self.ignore_callback_errors,

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\_qt\qt_viewer.py in on_mouse_press(self=<napari._qt.qt_viewer.QtViewer object>, event=<MouseEvent blocked=False button=1 buttons=[] de...rces=[] time=1629919884.9996626 type=mouse_press>)
    890             The vispy event that triggered this method.
    891         """
--> 892         self._process_mouse_event(mouse_press_callbacks, event)
        self._process_mouse_event = <bound method QtViewer._process_mouse_event of <napari._qt.qt_viewer.QtViewer object at 0x000002C52E8381F0>>
        global mouse_press_callbacks = <function mouse_press_callbacks at 0x000002C52D9AC550>
        event = <MouseEvent blocked=False button=1 buttons=[] delta=[0. 0.] handled=False is_dragging=False last_event=MouseEvent modifiers=() native=<PyQt5.QtGui.QMouseEvent object at 0x000002C55BAC7CA0> pos=[ 71 341] press_event=None source=None sources=[] time=1629919884.9996626 type=mouse_press>
    893
    894     def on_mouse_move(self, event):

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\_qt\qt_viewer.py in _process_mouse_event(self=<napari._qt.qt_viewer.QtViewer object>, mouse_callbacks=<function mouse_press_callbacks>, event=<ReadOnlyWrapper at 0x2c55eeaeac0 for MouseEvent>)
    849         layer = self.viewer.layers.selection.active
    850         if layer is not None:
--> 851             mouse_callbacks(layer, event)
        mouse_callbacks = <function mouse_press_callbacks at 0x000002C52D9AC550>
        layer = <Shapes layer 'Shapes' at 0x2c55bacdeb0>
        event = <ReadOnlyWrapper at 0x2c55eeaeac0 for MouseEvent at 0x2c55eee5d30>
    852
    853     def on_mouse_wheel(self, event):

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\utils\interactions.py in mouse_press_callbacks(obj=<Shapes layer 'Shapes'>, event=<ReadOnlyWrapper at 0x2c55eeaeac0 for MouseEvent>)
    146         if inspect.isgenerator(gen):
    147             try:
--> 148                 next(gen)
        global next = undefined
        gen = <generator object add_line at 0x000002C55EEAD040>
    149                 # now store iterated genenerator
    150                 obj._mouse_drag_gen[mouse_drag_func] = gen

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\layers\shapes\_shapes_mouse_bindings.py in add_line(layer=<Shapes layer 'Shapes'>, event=<ReadOnlyWrapper at 0x2c55eeaeac0 for MouseEvent>)
     96     corner = np.array(coordinates)
     97     data = np.array([corner, corner + full_size])
---> 98     yield from _add_line_rectangle_ellipse(
        global _add_line_rectangle_ellipse = <function _add_line_rectangle_ellipse at 0x000002C52DFF4B80>
        layer = <Shapes layer 'Shapes' at 0x2c55bacdeb0>
        event = <ReadOnlyWrapper at 0x2c55eeaeac0 for MouseEvent at 0x2c55eee5d30>
        data = <class 'numpy.ndarray'> (2, 2) float64
        global shape_type = undefined
     99         layer, event, data=data, shape_type='line'
    100     )

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\layers\shapes\_shapes_mouse_bindings.py in _add_line_rectangle_ellipse(layer=<Shapes layer 'Shapes'>, event=<ReadOnlyWrapper at 0x2c55eeaeac0 for MouseEvent>, data=<class 'numpy.ndarray'> (2, 2) float64, shape_type='line')
    143     # on press
    144     # Start drawing rectangle / ellipse / line
--> 145     layer.add(data, shape_type=shape_type)
        layer.add = <bound method Shapes.add of <Shapes layer 'Shapes' at 0x2c55bacdeb0>>
        data = <class 'numpy.ndarray'> (2, 2) float64
        shape_type = 'line'
    146     layer.selected_data = {layer.nshapes - 1}
    147     layer._value = (layer.nshapes - 1, 4)

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\layers\shapes\shapes.py in add(self=<Shapes layer 'Shapes'>, data=<class 'numpy.ndarray'> (2, 2) float64, shape_type='line', edge_width=1, edge_color=<class 'numpy.ndarray'> (1, 4) float32, face_color=<class 'numpy.ndarray'> (1, 4) float32, z_index=0)
   1995                 z_index=z_index,
   1996             )
-> 1997             self.events.data(value=self.data)
        self.events.data = <napari.utils.events.event.EventEmitter object at 0x000002C55EE38460>
        global value = undefined
        self.data = [<class 'numpy.ndarray'> (2, 2) float64]
   1998
   1999     def _init_shapes(

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\utils\events\event.py in __call__(self=<napari.utils.events.event.EventEmitter object>, *args=(), **kwargs={'value': [<class 'numpy.ndarray'> (2, 2) float64]})
    573                     continue
    574
--> 575                 self._invoke_callback(cb, event)
        self._invoke_callback = <bound method EventEmitter._invoke_callback of <napari.utils.events.event.EventEmitter object at 0x000002C55EE38460>>
        cb = <bound method PlotProfile._data_changed_event of <napari_plot_profile._dock_widget.PlotProfile object at 0x000002C55AC1A820>>
        event = <Event blocked=False handled=False native=None source=None sources=[] type='data'>
    576                 if event.blocked:
    577                     break

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\utils\events\event.py in _invoke_callback(self=<napari.utils.events.event.EventEmitter object>, cb=<bound method PlotProfile._data_changed_event of...ri_plot_profile._dock_widget.PlotProfile object>>, event=<Event blocked=False handled=False native=None source=None sources=[] type='data'>)
    605                 self.disconnect(cb)
    606                 return
--> 607             _handle_exception(
        global _handle_exception = <function _handle_exception at 0x000002C526D14550>
        self.ignore_callback_errors = False
        self.print_callback_errors = 'reminders'
        self = <napari.utils.events.event.EventEmitter object at 0x000002C55EE38460>
        global cb_event = undefined
        cb = <bound method PlotProfile._data_changed_event of <napari_plot_profile._dock_widget.PlotProfile object at 0x000002C55AC1A820>>
        event = <Event blocked=False handled=False native=None source=None sources=[] type='data'>
    608                 self.ignore_callback_errors,
    609                 self.print_callback_errors,

c:\users\rober\miniconda3\envs\bio1\lib\site-packages\napari\utils\events\event.py in _invoke_callback(self=<napari.utils.events.event.EventEmitter object>, cb=<bound method PlotProfile._data_changed_event of...ri_plot_profile._dock_widget.PlotProfile object>>, event=<Event blocked=False handled=False native=None source=None sources=[] type='data'>)
    594     def _invoke_callback(self, cb: Callback, event: Event):
    595         try:
--> 596             cb(event)
        cb = <bound method PlotProfile._data_changed_event of <napari_plot_profile._dock_widget.PlotProfile object at 0x000002C55AC1A820>>
        event = <Event blocked=False handled=False native=None source=None sources=[] type='data'>
    597         except Exception as e:
    598             # dead Qt object with living python pointer. not importing Qt

TypeError: _data_changed_event() missing 1 required positional argument: 'event'

haesleinhuepf avatar Aug 25 '21 19:08 haesleinhuepf

Not sure if it's the QTimer or what, but the live refresh seems quite slow on my end. The updating is slow and my line adjustment is laggy, like it stalls and skips, rather than fluid. If I close the plugin window manipulating the line is buttery smooth again. Alas, I don't have an older version of the plugin to compare.

psobolewskiPhD avatar Aug 26 '21 16:08 psobolewskiPhD

Hi @psobolewskiPhD, it will become smoother as soon as this PR gets merged. Therefore I need to make it work with lines and paths 😉

haesleinhuepf avatar Aug 27 '21 06:08 haesleinhuepf