receiver: fix and improve diversion load/save functions
This commit is contained in:
parent
1379da70a8
commit
0b5c263799
|
@ -33,6 +33,8 @@ from Xlib import X
|
||||||
from Xlib.display import Display
|
from Xlib.display import Display
|
||||||
from Xlib.ext import record
|
from Xlib.ext import record
|
||||||
from Xlib.protocol import rq
|
from Xlib.protocol import rq
|
||||||
|
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
|
from yaml import safe_load_all as _yaml_safe_load_all
|
||||||
|
|
||||||
from .common import unpack as _unpack
|
from .common import unpack as _unpack
|
||||||
|
@ -618,6 +620,51 @@ _file_path = _path.join(_XDG_CONFIG_HOME, 'solaar', 'rules.yaml')
|
||||||
rules = built_in_rules
|
rules = built_in_rules
|
||||||
|
|
||||||
|
|
||||||
|
def _save_config_rule_file(file_name=_file_path):
|
||||||
|
# This is a trick to show str/float/int lists in-line (inspired by https://stackoverflow.com/a/14001707)
|
||||||
|
class inline_list(list):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def blockseq_rep(dumper, data):
|
||||||
|
return dumper.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=True)
|
||||||
|
|
||||||
|
_yaml_add_representer(inline_list, blockseq_rep)
|
||||||
|
|
||||||
|
def convert(elem):
|
||||||
|
if isinstance(elem, list):
|
||||||
|
if len(elem) == 1 and isinstance(elem[0], (int, str, float)):
|
||||||
|
# All diversion classes that expect a list of scalars also support a single scalar without a list
|
||||||
|
return elem[0]
|
||||||
|
if all(isinstance(c, (int, str, float)) for c in elem):
|
||||||
|
return inline_list([convert(c) for c in elem])
|
||||||
|
return [convert(c) for c in elem]
|
||||||
|
if isinstance(elem, dict):
|
||||||
|
return {k: convert(v) for k, v in elem.items()}
|
||||||
|
return elem
|
||||||
|
|
||||||
|
# YAML format settings
|
||||||
|
dump_settings = {
|
||||||
|
'encoding': 'utf-8',
|
||||||
|
'explicit_start': True,
|
||||||
|
'explicit_end': True,
|
||||||
|
'default_flow_style': False
|
||||||
|
# 'version': (1, 3), # it would be printed for every rule
|
||||||
|
}
|
||||||
|
# Save only user-defined rules
|
||||||
|
rules_to_save = sum([r.data()['Rule'] for r in rules.components if r.source == file_name], [])
|
||||||
|
if rules_to_save:
|
||||||
|
if _log.isEnabledFor(_INFO):
|
||||||
|
_log.info('saving %d rule(s) to %s', len(rules_to_save), file_name)
|
||||||
|
try:
|
||||||
|
with open(file_name, 'w') as f:
|
||||||
|
f.write('%YAML 1.3\n') # Write version manually
|
||||||
|
_yaml_dump_all(convert([r['Rule'] for r in rules_to_save]), f, **dump_settings)
|
||||||
|
except Exception as e:
|
||||||
|
_log.error('failed to save to %s\n%s', file_name, e)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _load_config_rule_file():
|
def _load_config_rule_file():
|
||||||
global rules
|
global rules
|
||||||
loaded_rules = []
|
loaded_rules = []
|
||||||
|
@ -634,8 +681,7 @@ def _load_config_rule_file():
|
||||||
_log.info('loaded %d rules from %s', len(loaded_rules), config_file.name)
|
_log.info('loaded %d rules from %s', len(loaded_rules), config_file.name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_log.error('failed to load from %s\n%s', _file_path, e)
|
_log.error('failed to load from %s\n%s', _file_path, e)
|
||||||
loaded_rules.append(built_in_rules)
|
rules = Rule([Rule(loaded_rules, source=_file_path), built_in_rules])
|
||||||
rules = Rule(loaded_rules)
|
|
||||||
|
|
||||||
|
|
||||||
_load_config_rule_file()
|
_load_config_rule_file()
|
||||||
|
|
|
@ -21,7 +21,6 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from contextlib import contextmanager as contextlib_contextmanager
|
from contextlib import contextmanager as contextlib_contextmanager
|
||||||
from logging import INFO as _INFO
|
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from shlex import quote as shlex_quote
|
from shlex import quote as shlex_quote
|
||||||
|
|
||||||
|
@ -32,7 +31,6 @@ from logitech_receiver import diversion as _DIV
|
||||||
from logitech_receiver.special_keys import CONTROL as _CONTROL
|
from logitech_receiver.special_keys import CONTROL as _CONTROL
|
||||||
from pynput import mouse as _mouse
|
from pynput import mouse as _mouse
|
||||||
from solaar.i18n import _
|
from solaar.i18n import _
|
||||||
from yaml import dump as _yaml_dump
|
|
||||||
|
|
||||||
_log = getLogger(__name__)
|
_log = getLogger(__name__)
|
||||||
del getLogger
|
del getLogger
|
||||||
|
@ -154,7 +152,7 @@ class DiversionDialog:
|
||||||
if response == Gtk.ResponseType.NO:
|
if response == Gtk.ResponseType.NO:
|
||||||
w.hide()
|
w.hide()
|
||||||
elif response == Gtk.ResponseType.YES:
|
elif response == Gtk.ResponseType.YES:
|
||||||
self._save_yaml_file(_DIV._file_path)
|
self._save_yaml_file()
|
||||||
w.hide()
|
w.hide()
|
||||||
else:
|
else:
|
||||||
# don't close
|
# don't close
|
||||||
|
@ -172,21 +170,11 @@ class DiversionDialog:
|
||||||
self.view.set_model(self.model)
|
self.view.set_model(self.model)
|
||||||
self.view.expand_all()
|
self.view.expand_all()
|
||||||
|
|
||||||
def _save_yaml_file(self, file_name):
|
def _save_yaml_file(self):
|
||||||
rules = [r.data()['Rule'] for r in _DIV.rules.components if r.source == file_name]
|
if _DIV._save_config_rule_file():
|
||||||
if len(rules) == 1:
|
self.dirty = False
|
||||||
rules = rules[0]
|
self.save_btn.set_sensitive(False)
|
||||||
if rules:
|
self.discard_btn.set_sensitive(False)
|
||||||
if _log.isEnabledFor(_INFO):
|
|
||||||
_log.info('saving %d rule(s) to %s', len(rules), file_name)
|
|
||||||
try:
|
|
||||||
with open(file_name, 'w') as f:
|
|
||||||
_yaml_dump(rules, f, default_flow_style=False)
|
|
||||||
self.dirty = False
|
|
||||||
self.save_btn.set_sensitive(False)
|
|
||||||
self.discard_btn.set_sensitive(False)
|
|
||||||
except Exception as e:
|
|
||||||
_log.error('failed to save to %s\n%s', file_name, e)
|
|
||||||
|
|
||||||
def _create_top_panel(self):
|
def _create_top_panel(self):
|
||||||
sw = Gtk.ScrolledWindow()
|
sw = Gtk.ScrolledWindow()
|
||||||
|
@ -205,7 +193,7 @@ class DiversionDialog:
|
||||||
vbox = Gtk.VBox(spacing=20)
|
vbox = Gtk.VBox(spacing=20)
|
||||||
self.save_btn = Gtk.Button(_('Save changes'), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, sensitive=False)
|
self.save_btn = Gtk.Button(_('Save changes'), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, sensitive=False)
|
||||||
self.discard_btn = Gtk.Button(_('Discard changes'), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, sensitive=False)
|
self.discard_btn = Gtk.Button(_('Discard changes'), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, sensitive=False)
|
||||||
self.save_btn.connect('clicked', lambda *_args: self._save_yaml_file(_DIV._file_path))
|
self.save_btn.connect('clicked', lambda *_args: self._save_yaml_file())
|
||||||
self.discard_btn.connect('clicked', lambda *_args: self._reload_yaml_file())
|
self.discard_btn.connect('clicked', lambda *_args: self._reload_yaml_file())
|
||||||
vbox.pack_start(self.save_btn, False, False, 0)
|
vbox.pack_start(self.save_btn, False, False, 0)
|
||||||
vbox.pack_start(self.discard_btn, False, False, 0)
|
vbox.pack_start(self.discard_btn, False, False, 0)
|
||||||
|
|
Loading…
Reference in New Issue