Merge pull request #61 from pikers/faster_highlighting
Faster row highlightingkivy_mainline_and_py3.8
commit
b0d4d4b2f8
|
@ -269,9 +269,10 @@ async def get_cached_feed(
|
|||
"""
|
||||
# check if a cached client is in the local actor's statespace
|
||||
ss = tractor.current_actor().statespace
|
||||
feeds = ss['feeds']
|
||||
feeds = ss.setdefault('feeds', {'_lock': trio.Lock()})
|
||||
lock = feeds['_lock']
|
||||
feed_stack = ss['feed_stacks'][brokername]
|
||||
feed_stacks = ss.setdefault('feed_stacks', {})
|
||||
feed_stack = feed_stacks.setdefault(brokername, contextlib.AsyncExitStack())
|
||||
async with lock:
|
||||
try:
|
||||
feed = feeds[brokername]
|
||||
|
@ -305,18 +306,15 @@ async def start_quote_stream(
|
|||
Since most brokers seems to support batch quote requests we
|
||||
limit to one task per process for now.
|
||||
"""
|
||||
|
||||
actor = tractor.current_actor()
|
||||
# set log level after fork
|
||||
get_console_log(actor.loglevel)
|
||||
# pull global vars from local actor
|
||||
ss = actor.statespace
|
||||
# broker2symbolsubs = ss.setdefault('broker2symbolsubs', {})
|
||||
ss.setdefault('feeds', {'_lock': trio.Lock()})
|
||||
feed_stacks = ss.setdefault('feed_stacks', {})
|
||||
symbols = list(symbols)
|
||||
log.info(
|
||||
f"{chan.uid} subscribed to {broker} for symbols {symbols}")
|
||||
feed_stack = feed_stacks.setdefault(broker, contextlib.AsyncExitStack())
|
||||
# another actor task may have already created it
|
||||
feed = await get_cached_feed(broker)
|
||||
symbols2chans = feed.subscriptions[feed_type]
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
"""Hoverable Behaviour (changing when the mouse is on the widget by O. Poyen.
|
||||
License: LGPL
|
||||
"""
|
||||
__author__ = 'Olivier Poyen'
|
||||
|
||||
|
||||
from kivy.properties import BooleanProperty, ObjectProperty
|
||||
from kivy.factory import Factory
|
||||
from kivy.core.window import Window
|
||||
|
||||
|
||||
class HoverBehavior(object):
|
||||
"""Hover behavior.
|
||||
|
||||
:Events:
|
||||
`on_enter`
|
||||
Fired when mouse enter the bbox of the widget.
|
||||
`on_leave`
|
||||
Fired when the mouse exit the widget.
|
||||
"""
|
||||
hovered = BooleanProperty(False)
|
||||
# Contains the last relevant point received by the Hoverable. This can
|
||||
# be used in `on_enter` or `on_leave` in order to know where was dispatched
|
||||
# the event.
|
||||
border_point = ObjectProperty(None)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.register_event_type('on_enter')
|
||||
self.register_event_type('on_leave')
|
||||
Window.bind(mouse_pos=self.on_mouse_pos)
|
||||
super(HoverBehavior, self).__init__(**kwargs)
|
||||
|
||||
def on_mouse_pos(self, *args):
|
||||
# don't proceed if I'm not displayed <=> If have no parent
|
||||
if not self.get_root_window():
|
||||
return
|
||||
pos = args[1]
|
||||
# Next line to_widget allow to compensate for relative layout
|
||||
inside = self.collide_point(*self.to_widget(*pos))
|
||||
if self.hovered == inside:
|
||||
# We have already done what was needed
|
||||
return
|
||||
self.border_point = pos
|
||||
self.hovered = inside
|
||||
if inside:
|
||||
self.dispatch('on_enter')
|
||||
else:
|
||||
self.dispatch('on_leave')
|
||||
|
||||
# implement these in the widget impl
|
||||
|
||||
def on_enter(self):
|
||||
pass
|
||||
|
||||
def on_leave(self):
|
||||
pass
|
||||
|
||||
|
||||
# register for global use via kivy.factory.Factory
|
||||
Factory.register('HoverBehavior', HoverBehavior)
|
|
@ -0,0 +1,154 @@
|
|||
"""Mouse over behaviour.
|
||||
|
||||
Based on initial LGPL work by O. Poyen. here:
|
||||
https://gist.github.com/opqopq/15c707dc4cffc2b6455f
|
||||
"""
|
||||
import time
|
||||
from functools import wraps
|
||||
from collections import deque
|
||||
|
||||
from kivy.properties import BooleanProperty, ObjectProperty
|
||||
from kivy.core.window import Window
|
||||
from kivy.clock import Clock
|
||||
|
||||
|
||||
from ...log import get_logger
|
||||
|
||||
|
||||
log = get_logger('kivy')
|
||||
|
||||
|
||||
# XXX: copied from 1.10.1 since the async branch isn't ported yet.
|
||||
def triggered(timeout=0, interval=False):
|
||||
'''Decorator that will trigger the call of the function at the specified
|
||||
timeout, through the method :meth:`CyClockBase.create_trigger`. Subsequent
|
||||
calls to the decorated function (while the timeout is active) are ignored.
|
||||
It can be helpful when an expensive funcion (i.e. call to a server) can be
|
||||
triggered by different methods. Setting a proper timeout will delay the
|
||||
calling and only one of them wil be triggered.
|
||||
@triggered(timeout, interval=False)
|
||||
def callback(id):
|
||||
print('The callback has been called with id=%d' % id)
|
||||
>> callback(id=1)
|
||||
>> callback(id=2)
|
||||
The callback has been called with id=2
|
||||
The decorated callback can also be unscheduled using:
|
||||
>> callback.cancel()
|
||||
.. versionadded:: 1.10.1
|
||||
'''
|
||||
|
||||
def wrapper_triggered(func):
|
||||
|
||||
_args = []
|
||||
_kwargs = {}
|
||||
|
||||
def cb_function(dt):
|
||||
func(*tuple(_args), **_kwargs)
|
||||
|
||||
cb_trigger = Clock.create_trigger(
|
||||
cb_function,
|
||||
timeout=timeout,
|
||||
interval=interval)
|
||||
|
||||
@wraps(func)
|
||||
def trigger_function(*args, **kwargs):
|
||||
_args[:] = []
|
||||
_args.extend(list(args))
|
||||
_kwargs.clear()
|
||||
_kwargs.update(kwargs)
|
||||
cb_trigger()
|
||||
|
||||
def trigger_cancel():
|
||||
cb_trigger.cancel()
|
||||
|
||||
setattr(trigger_function, 'cancel', trigger_cancel)
|
||||
|
||||
return trigger_function
|
||||
|
||||
return wrapper_triggered
|
||||
|
||||
|
||||
class MouseOverBehavior(object):
|
||||
"""Mouse over behavior.
|
||||
|
||||
:Events:
|
||||
`on_enter`
|
||||
Fired when mouse enter the bbox of the widget.
|
||||
`on_leave`
|
||||
Fired when the mouse exit the widget.
|
||||
"""
|
||||
hovered = BooleanProperty(False)
|
||||
# Contains the last relevant point received by the Hoverable. This can
|
||||
# be used in `on_enter` or `on_leave` in order to know where was dispatched
|
||||
# the event.
|
||||
border_point = ObjectProperty(None)
|
||||
_widgets = deque()
|
||||
_last_hovered = None
|
||||
_last_time = time.time()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.register_event_type('on_enter')
|
||||
self.register_event_type('on_leave')
|
||||
MouseOverBehavior._widgets.append(self)
|
||||
super().__init__(**kwargs)
|
||||
Window.bind(mouse_pos=self._on_mouse_pos)
|
||||
|
||||
@classmethod
|
||||
# try throttling to 1ms latency (doesn't seem to work
|
||||
# best I can get is 0.01...)
|
||||
@triggered(timeout=0.001, interval=False)
|
||||
def _on_mouse_pos(cls, *args):
|
||||
log.debug(f"{cls} time since last call: {time.time() - cls._last_time}")
|
||||
cls._last_time = time.time()
|
||||
# XXX: how to still do this at the class level?
|
||||
# don't proceed if I'm not displayed <=> If have no parent
|
||||
# if not self.get_root_window():
|
||||
# return
|
||||
|
||||
pos = args[1]
|
||||
# Next line to_widget allow to compensate for relative layout
|
||||
for widget in cls._widgets.copy():
|
||||
w_coords = widget.to_widget(*pos)
|
||||
inside = widget.collide_point(*w_coords)
|
||||
if inside and widget.hovered:
|
||||
return
|
||||
elif inside:
|
||||
# un-highlight the last highlighted
|
||||
last_hovered = cls._last_hovered
|
||||
if last_hovered:
|
||||
last_hovered.dispatch('on_leave')
|
||||
last_hovered.hovered = False
|
||||
|
||||
# stick last highlighted at the front of the stack
|
||||
# resulting in LIFO iteration for efficiency
|
||||
cls._widgets.remove(widget)
|
||||
cls._widgets.appendleft(widget)
|
||||
cls._last_hovered = widget
|
||||
|
||||
# highlight new widget
|
||||
widget.border_point = pos
|
||||
widget.hovered = True
|
||||
widget.dispatch('on_enter')
|
||||
return
|
||||
|
||||
# implement these in the widget impl
|
||||
|
||||
@classmethod
|
||||
def on_enter(cls):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def on_leave(cls):
|
||||
pass
|
||||
|
||||
|
||||
def new_mouse_over_group():
|
||||
"""Return a new *mouse over group*, a class that can be mixed
|
||||
in to a group of widgets which can be mutex highlighted based
|
||||
on the mouse position.
|
||||
"""
|
||||
return type(
|
||||
'MouseOverBehavior',
|
||||
(MouseOverBehavior,),
|
||||
{},
|
||||
)
|
|
@ -23,8 +23,10 @@ from async_generator import aclosing
|
|||
|
||||
from ..log import get_logger
|
||||
from .pager import PagerView
|
||||
from .kivy.hoverable import HoverBehavior
|
||||
from .kivy.mouse_over import new_mouse_over_group
|
||||
|
||||
|
||||
HoverBehavior = new_mouse_over_group()
|
||||
log = get_logger('monitor')
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue