Skip to content
Snippets Groups Projects
Commit 2e7af957 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Fixed horrible delayed processing of mouse wheel and key events when

runnign under GTK/SSH/X11.
parent def5e656
No related branches found
No related tags found
No related merge requests found
......@@ -11,6 +11,7 @@
import logging
import fsl.fsleyes.profiles as profiles
import fsl.utils.async as async
log = logging.getLogger(__name__)
......@@ -76,7 +77,12 @@ class LightBoxViewProfile(profiles.Profile):
if wheel > 0: wheel = -1
elif wheel < 0: wheel = 1
self._viewPanel.getCanvas().topRow += wheel
# See comment in OrthoViewProfile._zoomModeMouseWheel
# about timeout
def update():
self._viewPanel.getCanvas().topRow += wheel
async.idle(update, timeout=0.1)
def _viewModeLeftMouseDrag(self, ev, canvas, mousePos, canvasPos):
......@@ -106,4 +112,10 @@ class LightBoxViewProfile(profiles.Profile):
if wheel > 0: wheel = 50
elif wheel < 0: wheel = -50
self._viewPanel.getSceneOptions().zoom += wheel
# see comment in OrthoViewProfile._zoomModeMouseWheel
# about timeout
def update():
self._viewPanel.getSceneOptions().zoom += wheel
async.idle(update, timeout=0.1)
......@@ -17,6 +17,7 @@ import numpy as np
import props
import fsl.data.image as fslimage
import fsl.data.strings as strings
import fsl.utils.async as async
import fsl.utils.dialog as fsldlg
import fsl.utils.status as status
import fsl.fsleyes.actions as actions
......@@ -868,10 +869,17 @@ class OrthoEditProfile(orthoviewprofile.OrthoViewProfile):
voxel = self.__getVoxelLocation(canvasPos)
if voxel is not None:
if voxel is None:
return
# See comment in OrthoViewProfile._zoomModeMouseWheel
# about timeout
def update():
self.__drawCursorAnnotation(canvas, voxel)
self.__refreshCanvases(ev, canvas)
async.idle(update, timeout=0.1)
def _deselModeLeftMouseDown(self, ev, canvas, mousePos, canvasPos):
"""Handles mouse down events in ``desel`` mode.
......@@ -1056,17 +1064,24 @@ class OrthoEditProfile(orthoviewprofile.OrthoViewProfile):
dataRange = opts.dataMax - opts.dataMin
step = 0.01 * dataRange
if wheel > 0: self.intensityThres += step
elif wheel < 0: self.intensityThres -= step
if wheel > 0: offset = step
elif wheel < 0: offset = -step
else: return
if self.__selecting:
voxel = self.__getVoxelLocation(canvasPos)
if voxel is not None:
self.__selintSelect(voxel, canvas)
self.__refreshCanvases(ev, canvas)
# See comment in OrthoViewProfile._zoomModeMouseWheel
# about timeout
def update():
self.intensityThres += offset
if self.__selecting:
voxel = self.__getVoxelLocation(canvasPos)
if voxel is not None:
self.__selintSelect(voxel, canvas)
self.__refreshCanvases(ev, canvas)
async.idle(update, timeout=0.1)
def _chradModeMouseWheel(self, ev, canvas, wheel, mousePos, canvasPos):
......@@ -1077,14 +1092,22 @@ class OrthoEditProfile(orthoviewprofile.OrthoViewProfile):
select-by-intensity is re-run at the current mouse location.
"""
if wheel > 0: self.searchRadius -= 5
elif wheel < 0: self.searchRadius += 5
if wheel > 0: offset = -5
elif wheel < 0: offset = 5
else: return
if self.__selecting:
voxel = self.__getVoxelLocation(canvasPos)
# See comment in OrthoViewProfile._zoomModeMouseWheel
# about timeout
def update():
self.searchRadius += offset
if self.__selecting:
voxel = self.__getVoxelLocation(canvasPos)
if voxel is not None:
self.__selintSelect(voxel, canvas)
self.__refreshCanvases(ev, canvas)
if voxel is not None:
self.__selintSelect(voxel, canvas)
self.__refreshCanvases(ev, canvas)
async.idle(update, timeout=0.1)
......@@ -16,6 +16,7 @@ import numpy as np
import fsl.fsleyes.profiles as profiles
import fsl.fsleyes.actions as actions
import fsl.utils.async as async
import fsl.data.image as fslimage
import fsl.data.constants as constants
......@@ -332,7 +333,11 @@ class OrthoViewProfile(profiles.Profile):
elif ch in ('+', '='): dirs[canvas.zax] = 1
elif ch in ('-', '_'): dirs[canvas.zax] = -1
self._displayCtx.location.xyz = self.__offsetLocation(*dirs)
def update():
self._displayCtx.location.xyz = self.__offsetLocation(*dirs)
# See comment in _zoomModeMouseWheel about timeout
async.idle(update, timeout=0.1)
#####################
......@@ -357,7 +362,11 @@ class OrthoViewProfile(profiles.Profile):
pos = self.__offsetLocation(*dirs)
self._displayCtx.location[canvas.zax] = pos[canvas.zax]
def update():
self._displayCtx.location[canvas.zax] = pos[canvas.zax]
# See comment in _zoomModeMouseWheel about timeout
async.idle(update, timeout=0.1)
####################
......@@ -378,7 +387,17 @@ class OrthoViewProfile(profiles.Profile):
"""
if wheel > 0: wheel = 50
elif wheel < 0: wheel = -50
canvas.zoom += wheel
# Over SSH/X11, mouse wheel events seem to get queued,
# and continue to get processed after the user has
# stopped spinning the mouse wheel, which is super
# frustrating. So we do the update asynchronously, and
# set a time out to drop the event, and prevent the
# horribleness from happening.
def update():
canvas.zoom += wheel
async.idle(update, timeout=0.1)
def _zoomModeChar(self, ev, canvas, key):
......@@ -500,7 +519,11 @@ class OrthoViewProfile(profiles.Profile):
elif key == wx.WXK_RIGHT: xoff = 2
else: return
canvas.panDisplayBy(xoff, yoff)
def update():
canvas.panDisplayBy(xoff, yoff)
# See comment in _zoomModeMouseWheel about timeout
async.idle(update, timeout=0.1)
#############
......
......@@ -31,6 +31,8 @@ task to run. It waits until all the threads have finished, and then runs
the task (via :func:`idle`).
"""
import time
import Queue
import logging
import threading
......@@ -125,12 +127,18 @@ def _wxIdleLoop(ev):
ev.Skip()
try: task, args, kwargs = _idleQueue.get_nowait()
except Queue.Empty: return
try:
task, schedtime, timeout, args, kwargs = _idleQueue.get_nowait()
except Queue.Empty:
return
name = getattr(task, '__name__', '<unknown>')
now = time.time()
elapsed = now - schedtime
name = getattr(task, '__name__', '<unknown>')
log.debug('Running function ({}) on wx idle loop'.format(name))
task(*args, **kwargs)
if timeout == 0 or (elapsed < timeout):
log.debug('Running function ({}) on wx idle loop'.format(name))
task(*args, **kwargs)
if _idleQueue.qsize() > 0:
ev.RequestMore()
......@@ -141,6 +149,12 @@ def idle(task, *args, **kwargs):
:arg task: The task to run.
:arg timeout: Optional. If provided, must be provided as a keyword
argument. Specifies a time out, in seconds. If this
amount of time passes before the function gets
scheduled to be called on the idle loop, the function
is not called, and is dropped from the queue.
All other arguments are passed through to the task function.
If a ``wx.App`` is not running, the task is called directly.
......@@ -149,6 +163,9 @@ def idle(task, *args, **kwargs):
global _idleRegistered
global _idleTasks
schedtime = time.time()
timeout = kwargs.pop('timeout', 0)
if _haveWX():
import wx
......@@ -159,7 +176,7 @@ def idle(task, *args, **kwargs):
name = getattr(task, '__name__', '<unknown>')
log.debug('Scheduling idle task ({}) on wx idle loop'.format(name))
_idleQueue.put_nowait((task, args, kwargs))
_idleQueue.put_nowait((task, schedtime, timeout, args, kwargs))
else:
log.debug('Running idle task directly')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment