parent
5b1125cd11
commit
8a87b9b013
|
@ -27,7 +27,9 @@ from math import sqrt as _sqrt
|
|||
|
||||
import _thread
|
||||
import psutil
|
||||
import solaar.ui.window as _window
|
||||
|
||||
from solaar.ui.config_panel import change_setting as _change_setting
|
||||
from yaml import add_representer as _yaml_add_representer
|
||||
from yaml import dump_all as _yaml_dump_all
|
||||
from yaml import safe_load_all as _yaml_safe_load_all
|
||||
|
@ -686,6 +688,41 @@ class MouseClick(Action):
|
|||
return {'MouseClick': [self.button, self.count]}
|
||||
|
||||
|
||||
class Set(Action):
|
||||
def __init__(self, args):
|
||||
if not (isinstance(args, list) and len(args) > 2):
|
||||
_log.warn('rule Set argument not list with minimum length 3: %s', args)
|
||||
self.args = []
|
||||
else:
|
||||
self.args = args
|
||||
|
||||
def __str__(self):
|
||||
return 'Set: ' + ' '.join([str(a) for a in self.args])
|
||||
|
||||
def evaluate(self, feature, notification, device, status, last_result):
|
||||
if len(self.args) < 3:
|
||||
return None
|
||||
if _log.isEnabledFor(_INFO):
|
||||
_log.info('Set action: %s', self.args)
|
||||
dev = _window.find_device(self.args[0]) if self.args[0] is not None else device
|
||||
if dev is None:
|
||||
_log.error('Set action: device %s is not known', self.args[0])
|
||||
return None
|
||||
setting = next((s for s in dev.settings if s.name == self.args[1]), None)
|
||||
if setting is None:
|
||||
_log.error('Set action: setting %s is not the name of a setting for %s', self.args[1], dev.name)
|
||||
return None
|
||||
args = setting.acceptable(self.args[2:], setting.read())
|
||||
if args is None:
|
||||
_log.error('Set Action: invalid args %s for setting %s of %s', self.args[2:], self.args[1], self.args[0])
|
||||
return None
|
||||
_change_setting(dev, setting, args)
|
||||
return None
|
||||
|
||||
def data(self):
|
||||
return {'Set': self.args[:]}
|
||||
|
||||
|
||||
class Execute(Action):
|
||||
def __init__(self, args):
|
||||
if isinstance(args, str):
|
||||
|
@ -726,6 +763,7 @@ COMPONENTS = {
|
|||
'KeyPress': KeyPress,
|
||||
'MouseScroll': MouseScroll,
|
||||
'MouseClick': MouseClick,
|
||||
'Set': Set,
|
||||
'Execute': Execute,
|
||||
}
|
||||
|
||||
|
|
|
@ -172,6 +172,9 @@ class Setting:
|
|||
|
||||
return value
|
||||
|
||||
def acceptable(self, args, current):
|
||||
return self._validator.acceptable(args, current) if self._validator else None
|
||||
|
||||
def apply(self):
|
||||
assert hasattr(self, '_value')
|
||||
assert hasattr(self, '_device')
|
||||
|
@ -371,7 +374,7 @@ class LongSettings(Setting):
|
|||
return None
|
||||
return map
|
||||
|
||||
def write_item_value(self, item, value):
|
||||
def write_key_value(self, item, value):
|
||||
assert hasattr(self, '_value')
|
||||
assert hasattr(self, '_device')
|
||||
assert item is not None
|
||||
|
@ -689,7 +692,7 @@ class BooleanValidator:
|
|||
if new_value is None:
|
||||
new_value = False
|
||||
else:
|
||||
assert isinstance(new_value, bool)
|
||||
assert isinstance(new_value, bool), 'New value %s for boolean setting is not a boolean' % new_value
|
||||
|
||||
to_write = self.true_value if new_value else self.false_value
|
||||
|
||||
|
@ -720,6 +723,12 @@ class BooleanValidator:
|
|||
|
||||
return self.write_prefix_bytes + to_write
|
||||
|
||||
def acceptable(self, args, current):
|
||||
if len(args) != 1:
|
||||
return None
|
||||
val = [args[0]] if type(args[0]) == bool else [not current] if args[0] == '~' else None
|
||||
return val
|
||||
|
||||
|
||||
class BitFieldValidator:
|
||||
__slots__ = ('byte_count', 'options')
|
||||
|
@ -755,6 +764,15 @@ class BitFieldValidator:
|
|||
def all_options(self):
|
||||
return self.options
|
||||
|
||||
def acceptable(self, args, current):
|
||||
if len(args) != 2:
|
||||
return None
|
||||
key = next((key for key in self.options if key == args[0]), None)
|
||||
if key is None:
|
||||
return None
|
||||
val = args[1] if type(args[1]) == bool else not current[str(int(key))] if args[1] == '~' else None
|
||||
return None if val is None else [str(int(key)), val]
|
||||
|
||||
|
||||
class BitFieldWithOffsetAndMaskValidator:
|
||||
__slots__ = ('byte_count', 'options', '_option_from_key', '_mask_from_offset', '_option_from_offset_mask')
|
||||
|
@ -837,6 +855,15 @@ class BitFieldWithOffsetAndMaskValidator:
|
|||
def all_options(self):
|
||||
return [int(opt) if isinstance(opt, int) else opt.as_int() for opt in self.options]
|
||||
|
||||
def acceptable(self, args, current):
|
||||
if len(args) != 2:
|
||||
return None
|
||||
key = next((option.id for option in self.options if option.as_int() == args[0]), None)
|
||||
if key is None:
|
||||
return None
|
||||
val = args[1] if type(args[1]) == bool else not current[str(int(key))] if args[1] == '~' else None
|
||||
return None if val is None else [str(key), val]
|
||||
|
||||
|
||||
class ChoicesValidator:
|
||||
kind = KIND.choice
|
||||
|
@ -870,21 +897,31 @@ class ChoicesValidator:
|
|||
|
||||
def prepare_write(self, new_value, current_value=None):
|
||||
if new_value is None:
|
||||
choice = self.choices[:][0]
|
||||
value = self.choices[:][0]
|
||||
else:
|
||||
if isinstance(new_value, int):
|
||||
choice = self.choices[new_value]
|
||||
elif int(new_value) in self.choices:
|
||||
choice = self.choices[int(new_value)]
|
||||
elif new_value in self.choices:
|
||||
choice = self.choices[new_value]
|
||||
else:
|
||||
raise ValueError(new_value)
|
||||
|
||||
if choice is None:
|
||||
value = self.choice(new_value)
|
||||
if value is None:
|
||||
raise ValueError('invalid choice %r' % new_value)
|
||||
assert isinstance(choice, _NamedInt)
|
||||
return self._write_prefix_bytes + choice.bytes(self._byte_count)
|
||||
assert isinstance(value, _NamedInt)
|
||||
return self._write_prefix_bytes + value.bytes(self._byte_count)
|
||||
|
||||
def choice(self, value):
|
||||
if isinstance(value, int):
|
||||
return self.choices[value]
|
||||
try:
|
||||
int(value)
|
||||
if int(value) in self.choices:
|
||||
return self.choices[int(value)]
|
||||
except Exception:
|
||||
pass
|
||||
if value in self.choices:
|
||||
return self.choices[value]
|
||||
else:
|
||||
return None
|
||||
|
||||
def acceptable(self, args, current):
|
||||
choice = self.choice(args[0]) if len(args) == 1 else None
|
||||
return None if choice is None else [choice]
|
||||
|
||||
|
||||
class ChoicesMapValidator(ChoicesValidator):
|
||||
|
@ -949,6 +986,15 @@ class ChoicesMapValidator(ChoicesValidator):
|
|||
new_value = new_value | self.activate
|
||||
return self._write_prefix_bytes + new_value.to_bytes(self._byte_count, 'big')
|
||||
|
||||
def acceptable(self, args, current):
|
||||
if len(args) != 2:
|
||||
return None
|
||||
key, choices = next(((key, item) for key, item in self.choices.items() if key == args[0]), (None, None))
|
||||
if choices is None or args[1] not in choices:
|
||||
return None
|
||||
choice = next((item for item in choices if item == args[1]), None)
|
||||
return [str(int(key)), int(choice)] if choice is not None else None
|
||||
|
||||
|
||||
class RangeValidator:
|
||||
__slots__ = ('min_value', 'max_value', 'flag', '_byte_count', 'needs_current_value')
|
||||
|
@ -982,6 +1028,11 @@ class RangeValidator:
|
|||
raise ValueError('invalid choice %r' % new_value)
|
||||
return _int2bytes(new_value, self._byte_count)
|
||||
|
||||
def acceptable(self, args, current):
|
||||
arg = args[0]
|
||||
# None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args)
|
||||
return None if len(args) != 1 or type(arg) != int or arg < self.min_value or arg > self.max_value else args
|
||||
|
||||
|
||||
class MultipleRangeValidator:
|
||||
|
||||
|
@ -1052,6 +1103,9 @@ class MultipleRangeValidator:
|
|||
w += _int2bytes(v, sub_item.length)
|
||||
return w + b'\xFF'
|
||||
|
||||
def acceptable(self, args, current):
|
||||
pass # not implemented yet
|
||||
|
||||
|
||||
class ActionSettingRW:
|
||||
"""Special RW class for settings that turn on and off special processing when a key or button is depressed"""
|
||||
|
|
|
@ -211,7 +211,7 @@ def set(dev, setting, args):
|
|||
else:
|
||||
raise Exception("%s: key '%s' not in setting" % (setting.name, key))
|
||||
message = 'Setting %s key %s parameter %s to %r' % (setting.name, k, args.extra_subkey, item[args.extra_subkey])
|
||||
result = setting.write_item_value(int(k), item)
|
||||
result = setting.write_key_value(int(k), item)
|
||||
|
||||
else:
|
||||
raise Exception('NotImplemented')
|
||||
|
|
|
@ -37,18 +37,24 @@ def _read_async(setting, force_read, sbox, device_is_online, sensitive):
|
|||
_ui_async(_do_read, setting, force_read, sbox, device_is_online, sensitive)
|
||||
|
||||
|
||||
def _write_async(setting, value, sbox):
|
||||
failed, spinner, control = _get_failed_spinner_control(sbox)
|
||||
control.set_sensitive(False)
|
||||
failed.set_visible(False)
|
||||
spinner.set_visible(True)
|
||||
spinner.start()
|
||||
def _write_async(setting, value, sbox, sensitive=True, key=None):
|
||||
if sbox:
|
||||
failed, spinner, control = _get_failed_spinner_control(sbox)
|
||||
control.set_sensitive(False)
|
||||
failed.set_visible(False)
|
||||
spinner.set_visible(True)
|
||||
spinner.start()
|
||||
|
||||
def _do_write(s, v, sb):
|
||||
v = setting.write(v)
|
||||
GLib.idle_add(_update_setting_item, sb, v, True, priority=99)
|
||||
def _do_write(s, v, sb, key):
|
||||
if key is None:
|
||||
v = setting.write(v)
|
||||
else:
|
||||
v = setting.write_key_value(key, v)
|
||||
v = {key: v}
|
||||
if sb:
|
||||
GLib.idle_add(_update_setting_item, sb, v, True, sensitive, priority=99)
|
||||
|
||||
_ui_async(_do_write, setting, value, sbox)
|
||||
_ui_async(_do_write, setting, value, sbox, key)
|
||||
|
||||
|
||||
def _write_async_key_value(setting, key, value, sbox):
|
||||
|
@ -65,20 +71,6 @@ def _write_async_key_value(setting, key, value, sbox):
|
|||
_ui_async(_do_write_key_value, setting, key, value, sbox)
|
||||
|
||||
|
||||
def _write_async_item_value(setting, item, value, sbox):
|
||||
failed, spinner, control = _get_failed_spinner_control(sbox)
|
||||
control.set_sensitive(False)
|
||||
failed.set_visible(False)
|
||||
spinner.set_visible(True)
|
||||
spinner.start()
|
||||
|
||||
def _do_write_item_value(s, k, v, sb):
|
||||
v = setting.write_item_value(k, v)
|
||||
GLib.idle_add(_update_setting_item, sb, {k: v}, True, priority=99)
|
||||
|
||||
_ui_async(_do_write_item_value, setting, item, value, sbox)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
@ -257,7 +249,7 @@ def _create_multiple_range_control(setting, change):
|
|||
p = control
|
||||
for _i in range(7):
|
||||
p = p.get_parent()
|
||||
_write_async_item_value(setting, str(int(item)), setting._value[str(int(item))], p)
|
||||
_write_async_key_value(setting, str(int(item)), setting._value[str(int(item))], p)
|
||||
|
||||
def _changed(control, setting, item, sub_item):
|
||||
if control.get_sensitive():
|
||||
|
@ -385,9 +377,10 @@ def _change_click(button, arg):
|
|||
|
||||
|
||||
def _change_icon(allowed, icon):
|
||||
allowed = allowed if allowed in _allowables_icons else True
|
||||
icon.set_from_icon_name(_allowables_icons[allowed], Gtk.IconSize.LARGE_TOOLBAR)
|
||||
icon.set_tooltip_text(_allowables_tooltips[allowed])
|
||||
if allowed in _allowables_icons:
|
||||
icon._allowed = allowed
|
||||
icon.set_from_icon_name(_allowables_icons[allowed], Gtk.IconSize.LARGE_TOOLBAR)
|
||||
icon.set_tooltip_text(_allowables_tooltips[allowed])
|
||||
|
||||
|
||||
def _create_sbox(s, device):
|
||||
|
@ -478,7 +471,7 @@ def _update_setting_item(sbox, value, is_online=True, sensitive=True):
|
|||
control.set_sensitive(False)
|
||||
failed.set_visible(False)
|
||||
if isinstance(control, Gtk.Switch):
|
||||
control.set_active(value)
|
||||
control.set_state(value)
|
||||
elif isinstance(control, Gtk.ComboBoxText):
|
||||
control.set_active_id(str(int(value)))
|
||||
elif isinstance(control, Gtk.Scale):
|
||||
|
@ -527,6 +520,7 @@ def _update_setting_item(sbox, value, is_online=True, sensitive=True):
|
|||
else:
|
||||
raise Exception('NotImplemented')
|
||||
|
||||
sensitive = sbox._change_icon._allowed if sensitive is None else sensitive
|
||||
control.set_sensitive(sensitive is True)
|
||||
_change_icon(sensitive, sbox._change_icon)
|
||||
|
||||
|
@ -583,7 +577,6 @@ def update(device, is_online=None):
|
|||
else:
|
||||
sbox = _items[k] = _create_sbox(s, device)
|
||||
_box.pack_start(sbox, False, False, 0)
|
||||
|
||||
sensitive = device.persister.get_sensitivity(s.name) if device.persister else True
|
||||
_read_async(s, False, sbox, is_online, sensitive)
|
||||
|
||||
|
@ -606,3 +599,13 @@ def destroy():
|
|||
global _box
|
||||
_box = None
|
||||
_items.clear()
|
||||
|
||||
|
||||
def change_setting(device, setting, values):
|
||||
device = setting._device
|
||||
device_path = device.receiver.path if device.receiver else device.path
|
||||
if (device_path, device.number, setting.name) in _items:
|
||||
sbox = _items[(device_path, device.number, setting.name)]
|
||||
else:
|
||||
sbox = None
|
||||
_write_async(setting, values[-1], sbox, None, key=values[0] if len(values) > 1 else None)
|
||||
|
|
|
@ -926,3 +926,18 @@ def update_device(device, item, selected_device_id, need_popup, full=False):
|
|||
elif selected_device_id == (device.receiver.path if device.receiver else device.path, device.number):
|
||||
full_update = full or was_online != is_online
|
||||
_update_info_panel(device, full=full_update)
|
||||
|
||||
|
||||
def find_device(serial):
|
||||
assert serial, 'need serial number or unit ID to find a device'
|
||||
result = None
|
||||
|
||||
def check(_store, _treepath, row):
|
||||
nonlocal result
|
||||
device = _model.get_value(row, _COLUMN.DEVICE)
|
||||
if device and device.kind and (device.unitId == serial or device.serial == serial):
|
||||
result = device
|
||||
return True
|
||||
|
||||
_model.foreach(check)
|
||||
return result
|
||||
|
|
Loading…
Reference in New Issue