summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Gayot <olivier.gayot@sigexec.com>2023-02-11 17:24:57 +0100
committerOlivier Gayot <olivier.gayot@sigexec.com>2023-02-11 17:47:35 +0100
commit3cde5b6c7f4f9459bfed67f649582c136824a9cc (patch)
tree670c6a1d904e54d4436190d0d4d20519c5a7da83
parent13f3018846c8d9fd9fdaa4e3f5c9dcbf3b574fb6 (diff)
Use dataclasses instead of relying on untyped dicts
Signed-off-by: Olivier Gayot <olivier.gayot@sigexec.com>
-rwxr-xr-xmonitor-menu.py90
1 files changed, 66 insertions, 24 deletions
diff --git a/monitor-menu.py b/monitor-menu.py
index 42228ce..f8baa7a 100755
--- a/monitor-menu.py
+++ b/monitor-menu.py
@@ -1,10 +1,12 @@
#!/usr/bin/env python3
import argparse
+import dataclasses
import json
import logging
from os.path import expanduser
import subprocess
+from typing import Any
import dialog
@@ -21,10 +23,71 @@ class LogLevel:
raise ValueError("invalid value")
+@dataclasses.dataclass
+class Monitor:
+ output: str
+
+ xrandr_opts: list[str] = dataclasses.field(default_factory=list)
+ position: str | None = None
+ background: str | None = None
+ primary: bool = False
+
+ @classmethod
+ def from_json_dict(cls, json_dict: dict[str, Any]) -> "Monitor":
+ def convert_key_name(name: str) -> str:
+ return name.replace("-", "_")
+
+ return cls(**{convert_key_name(key): value for key, value in json_dict.items()})
+
+
+@dataclasses.dataclass
+class Profile:
+ name: str
+ monitors: list[Monitor] = dataclasses.field(default_factory=list)
+
+ xrandr_opts: list[str] = dataclasses.field(default_factory=list)
+
+ @classmethod
+ def from_json_dict(cls, json_dict: dict[str, Any]) -> "Profile":
+ def convert_key_name(name: str) -> str:
+ return name.replace("-", "_")
+
+ kwargs = {}
+ for key, value in json_dict.items():
+ key = convert_key_name(key)
+ if key == "monitors":
+ value = [Monitor.from_json_dict(item) for item in value]
+ kwargs[key] = value
+ return cls(**kwargs)
+
+ def apply(self):
+ # We build the command line starting from just "xrandr" and adding
+ # arguments.
+ xrandr_cmd = ['xrandr']
+ feh_cmd = ['feh', '--bg-fill']
+
+ try:
+ xrandr_cmd += self.xrandr_opts
+ except KeyError:
+ pass
+
+ for monitor in self.monitors:
+ xrandr_cmd.extend(monitor.xrandr_opts)
+
+ if monitor.background is not None:
+ feh_cmd.append(monitor.background)
+
+ logging.debug("Executing: %s", xrandr_cmd)
+ subprocess.run(xrandr_cmd, check=False)
+ logging.debug("Executing: %s", feh_cmd)
+ subprocess.run(feh_cmd, check=False)
+
+
class MonitorMenu():
def __init__(self, config_file='~/.config/monitor-profiles.json'):
with open(expanduser(config_file)) as fh:
- self.profiles = json.load(fh)
+ data = json.load(fh)
+ self.profiles = [Profile.from_json_dict(item) for item in data]
self.d = dialog.Dialog(autowidgetsize=True)
def run(self, profile_idx=None):
@@ -33,7 +96,7 @@ class MonitorMenu():
if profile_idx is None:
i = 0
for p in self.profiles:
- choices.append((str(i), p['name']))
+ choices.append((str(i), p.name))
i += 1
code, profile_idx = self.d.menu(
@@ -44,31 +107,10 @@ class MonitorMenu():
return
try:
- profile = self.profiles[int(profile_idx)]
+ self.profiles[int(profile_idx)].apply()
except IndexError:
raise NoMatchingProfile from None
- # We build the command line starting from just "xrandr" and adding
- # arguments.
- xrandr_cmd = ['xrandr']
- feh_cmd = ['feh', '--bg-fill']
-
- try:
- xrandr_cmd += profile['xrandr-opts']
- except KeyError:
- pass
-
- for monitor in profile['monitors']:
- xrandr_cmd.extend(monitor['xrandr-opts'])
-
- if monitor.get('background') is not None:
- feh_cmd.append(monitor['background'])
-
- logging.debug("Executing: %s", xrandr_cmd)
- subprocess.run(xrandr_cmd, check=False)
- logging.debug("Executing: %s", feh_cmd)
- subprocess.run(feh_cmd, check=False)
-
def main():
parser = argparse.ArgumentParser()