From 3cde5b6c7f4f9459bfed67f649582c136824a9cc Mon Sep 17 00:00:00 2001 From: Olivier Gayot Date: Sat, 11 Feb 2023 17:24:57 +0100 Subject: Use dataclasses instead of relying on untyped dicts Signed-off-by: Olivier Gayot --- monitor-menu.py | 90 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file 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() -- cgit v1.2.3