logitech_receiver: add UnsortedNamedInts

This commit is contained in:
Vinícius 2022-01-17 13:46:54 -03:00 committed by Peter F. Patel-Schneider
parent b36b070feb
commit 29ff5e5924
3 changed files with 55 additions and 25 deletions

View File

@ -99,7 +99,7 @@ class NamedInts:
if the value already exists in the set (int or string), ValueError will be if the value already exists in the set (int or string), ValueError will be
raised. raised.
""" """
__slots__ = ('__dict__', '_values', '_indexed', '_fallback') __slots__ = ('__dict__', '_values', '_indexed', '_fallback', '_is_sorted')
def __init__(self, **kwargs): def __init__(self, **kwargs):
def _readable_name(n): def _readable_name(n):
@ -110,7 +110,9 @@ class NamedInts:
# print (repr(kwargs)) # print (repr(kwargs))
values = {k: NamedInt(v, _readable_name(k)) for (k, v) in kwargs.items()} values = {k: NamedInt(v, _readable_name(k)) for (k, v) in kwargs.items()}
self.__dict__ = values self.__dict__ = values
self._values = sorted(list(values.values())) self._is_sorted = False
self._values = list(values.values())
self._sort_values()
self._indexed = {int(v): v for v in self._values} self._indexed = {int(v): v for v in self._values}
# assert len(values) == len(self._indexed) # assert len(values) == len(self._indexed)
# "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed) # "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed)
@ -137,14 +139,20 @@ class NamedInts:
if unknown_bits: if unknown_bits:
yield 'unknown:%06X' % unknown_bits yield 'unknown:%06X' % unknown_bits
def _sort_values(self):
self._values = sorted(self._values)
self._is_sorted = True
def __getitem__(self, index): def __getitem__(self, index):
if isinstance(index, int): if isinstance(index, int):
if index in self._indexed: if index in self._indexed:
return self._indexed[int(index)] return self._indexed[int(index)]
if self._fallback and isinstance(index, int): if self._fallback:
value = NamedInt(index, self._fallback(index)) value = NamedInt(index, self._fallback(index))
self._indexed[index] = value self._indexed[index] = value
self._values = sorted(self._values + [value]) self._values.append(value)
self._is_sorted = False
self._sort_values()
return value return value
elif is_string(index): elif is_string(index):
@ -153,21 +161,24 @@ class NamedInts:
return (next((x for x in self._values if str(x) == index), None)) return (next((x for x in self._values if str(x) == index), None))
elif isinstance(index, slice): elif isinstance(index, slice):
values = self._values if self._is_sorted else sorted(self._values)
if index.start is None and index.stop is None: if index.start is None and index.stop is None:
return self._values[:] return values[:]
v_start = int(self._values[0]) if index.start is None else int(index.start) v_start = int(values[0]) if index.start is None else int(index.start)
v_stop = (self._values[-1] + 1) if index.stop is None else int(index.stop) v_stop = (values[-1] + 1) if index.stop is None else int(index.stop)
if v_start > v_stop or v_start > self._values[-1] or v_stop <= self._values[0]: if v_start > v_stop or v_start > values[-1] or v_stop <= values[0]:
return [] return []
if v_start <= self._values[0] and v_stop > self._values[-1]: if v_start <= values[0] and v_stop > values[-1]:
return self._values[:] return values[:]
start_index = 0 start_index = 0
stop_index = len(self._values) stop_index = len(values)
for i, value in enumerate(self._values):
for i, value in enumerate(values):
if value < v_start: if value < v_start:
start_index = i + 1 start_index = i + 1
elif index.stop is None: elif index.stop is None:
@ -176,7 +187,7 @@ class NamedInts:
stop_index = i stop_index = i
break break
return self._values[start_index:stop_index] return values[start_index:stop_index]
def __setitem__(self, index, name): def __setitem__(self, index, name):
assert isinstance(index, int), type(index) assert isinstance(index, int), type(index)
@ -193,7 +204,9 @@ class NamedInts:
if int(value) in self._indexed: if int(value) in self._indexed:
raise ValueError('%d (%s) already known' % (int(value), value)) raise ValueError('%d (%s) already known' % (int(value), value))
self._values = sorted(self._values + [value]) self._values.append(value)
self._is_sorted = False
self._sort_values()
self.__dict__[str(value)] = value self.__dict__[str(value)] = value
self._indexed[int(value)] = value self._indexed[int(value)] = value
@ -216,6 +229,15 @@ class NamedInts:
return NamedInts(**self.__dict__, **other.__dict__) return NamedInts(**self.__dict__, **other.__dict__)
class UnsortedNamedInts(NamedInts):
def _sort_values(self):
pass
def __or__(self, other):
c = UnsortedNamedInts if isinstance(other, UnsortedNamedInts) else NamedInts
return c(**self.__dict__, **other.__dict__)
def strhex(x): def strhex(x):
assert x is not None assert x is not None
"""Produce a hex-string representation of a sequence of bytes.""" """Produce a hex-string representation of a sequence of bytes."""

View File

@ -31,6 +31,7 @@ from .common import FirmwareInfo as _FirmwareInfo
from .common import KwException as _KwException from .common import KwException as _KwException
from .common import NamedInt as _NamedInt from .common import NamedInt as _NamedInt
from .common import NamedInts as _NamedInts from .common import NamedInts as _NamedInts
from .common import UnsortedNamedInts as _UnsortedNamedInts
from .common import bytes2int as _bytes2int from .common import bytes2int as _bytes2int
from .common import pack as _pack from .common import pack as _pack
from .common import unpack as _unpack from .common import unpack as _unpack
@ -438,18 +439,18 @@ class ReprogrammableKeyV4(ReprogrammableKey):
return _NamedInt(self._mapped_to, task) return _NamedInt(self._mapped_to, task)
@property @property
def remappable_to(self) -> List[_NamedInt]: def remappable_to(self) -> _NamedInts:
self._device.keys._ensure_all_keys_queried() self._device.keys._ensure_all_keys_queried()
ret = [] ret = _UnsortedNamedInts()
if self.group_mask != []: # only keys with a non-zero gmask are remappable if self.group_mask != []: # only keys with a non-zero gmask are remappable
ret = [self.default_task] # it should always be possible to map the key to itself ret[self.default_task] = self.default_task # it should always be possible to map the key to itself
for g in self.group_mask: for g in self.group_mask:
g = special_keys.CID_GROUP[str(g)] g = special_keys.CID_GROUP[str(g)]
for tgt_cid in self._device.keys.group_cids[g]: for tgt_cid in self._device.keys.group_cids[g]:
tgt_task = str(special_keys.TASK[self._device.keys.cid_to_tid[tgt_cid]]) tgt_task = str(special_keys.TASK[self._device.keys.cid_to_tid[tgt_cid]])
tgt_task = _NamedInt(tgt_cid, tgt_task) tgt_task = _NamedInt(tgt_cid, tgt_task)
if tgt_task != self.default_task: # don't put itself in twice if tgt_task != self.default_task: # don't put itself in twice
ret.append(tgt_task) ret[tgt_task] = tgt_task
return ret return ret
@property @property

View File

@ -23,7 +23,7 @@ from shlex import quote as shlex_quote
from gi.repository import Gdk, GObject, Gtk from gi.repository import Gdk, GObject, Gtk
from logitech_receiver import diversion as _DIV from logitech_receiver import diversion as _DIV
from logitech_receiver.common import NamedInt, NamedInts from logitech_receiver.common import NamedInt, UnsortedNamedInts
from logitech_receiver.diversion import XK_KEYS as _XK_KEYS from logitech_receiver.diversion import XK_KEYS as _XK_KEYS
from logitech_receiver.diversion import Key as _Key from logitech_receiver.diversion import Key as _Key
from logitech_receiver.diversion import buttons as _buttons from logitech_receiver.diversion import buttons as _buttons
@ -1655,10 +1655,16 @@ class SetUI(ActionUI):
Only one label per number is kept. Only one label per number is kept.
""" """
if isinstance(setting, type) and issubclass(setting, _Setting): if isinstance(setting, type) and issubclass(setting, _Setting):
return (getattr(setting, 'choices_universe', None) choices = UnsortedNamedInts()
or NamedInts()) | (getattr(setting, 'choices_extra', None) or NamedInts()) universe = getattr(setting, 'choices_universe', None)
if universe:
choices |= universe
extra = getattr(setting, 'choices_extra', None)
if extra:
choices |= extra
return choices
settings = cls.ALL_SETTINGS.get(setting, []) settings = cls.ALL_SETTINGS.get(setting, [])
choices = NamedInts() choices = UnsortedNamedInts()
for s in settings: for s in settings:
choices |= cls._all_choices(s) choices |= cls._all_choices(s)
return choices return choices
@ -1670,10 +1676,11 @@ class SetUI(ActionUI):
val_class = setting.validator_class if setting else None val_class = setting.validator_class if setting else None
kind = val_class.kind if val_class else None kind = val_class.kind if val_class else None
if kind in cls.MULTIPLE: if kind in cls.MULTIPLE:
keys = NamedInts() keys = UnsortedNamedInts()
for s in settings: for s in settings:
keys |= getattr(s, 'choices_universe' if kind == _SKIND.multiple_toggle else 'keys_universe', universe = getattr(s, 'choices_universe' if kind == _SKIND.multiple_toggle else 'keys_universe', None)
None) or NamedInts() if universe:
keys |= universe
# only one key per number is used # only one key per number is used
else: else:
keys = None keys = None