Note that for compatability with Win7SP1, Pyside 6.1.3 and playsound 1.2.2 will be needed(instead of the latest versions).
134 lines
5.0 KiB
Python
134 lines
5.0 KiB
Python
import time, json, dataclasses
|
|
from enum import Enum
|
|
from playsound import playsound
|
|
from sound import play
|
|
|
|
class SoundType(Enum):
|
|
FIRST_BELL = 1
|
|
SECOND_BELL = 2
|
|
BREAK = 3
|
|
CUSTOM = 4
|
|
|
|
BELL_NAMES = {
|
|
SoundType.FIRST_BELL: "1-й дзвоник",
|
|
SoundType.SECOND_BELL: "2-й дзвоник",
|
|
SoundType.BREAK: "Перерва"
|
|
}
|
|
|
|
@dataclasses.dataclass
|
|
class Bell:
|
|
sound: SoundType
|
|
hour: int
|
|
minute: int
|
|
played: bool = False
|
|
# only used when sound = SoundType.CUSTOM
|
|
custom_file: str = ""
|
|
custom_name: str = ""
|
|
|
|
@dataclasses.dataclass
|
|
class CustomSound:
|
|
name: str
|
|
times: list
|
|
sound_file: str
|
|
|
|
@dataclasses.dataclass
|
|
class Config:
|
|
# yes, this is permanently "borrowed" from the internet
|
|
# thx to https://stackoverflow.com/questions/53632152/why-cant-dataclasses-have-mutable-defaults-in-their-class-attributes-declaratio
|
|
lessons_start: list = dataclasses.field(default_factory=lambda: [8, 0])
|
|
lesson_length: int = dataclasses.field(default_factory=lambda: 40)
|
|
break_length: int = dataclasses.field(default_factory=lambda: 5)
|
|
first_bell: int = dataclasses.field(default_factory=lambda: 1)
|
|
num_lessons: int = dataclasses.field(default_factory=lambda: 12)
|
|
workdays: list = dataclasses.field(default_factory=lambda: [True, True, True, True, True, True, False])
|
|
sound_files: list = dataclasses.field(default_factory=lambda: ["", "", ""])
|
|
first_bell_before_first_lesson: bool = dataclasses.field(default_factory=lambda: True)
|
|
custom_sounds: list = dataclasses.field(default_factory=lambda: [])
|
|
|
|
class Scheduler:
|
|
def __init__(self) -> None:
|
|
self.bells: list[Bell] = []
|
|
self.bells_enabled: bool = True
|
|
self.load_config()
|
|
|
|
def set_ui_class(self, ui_class) -> None:
|
|
self.ui = ui_class
|
|
self.generate_bells()
|
|
self.ui.set_settings(self.config)
|
|
|
|
def apply_config(self) -> None:
|
|
self.ui.get_settings(self.config, CustomSound)
|
|
self.generate_bells()
|
|
|
|
def generate_bells(self) -> None:
|
|
self.bells = []
|
|
if not self.config.workdays[time.localtime().tm_wday]:
|
|
self.ui.set_schedule(self.get_bells_list())
|
|
return
|
|
|
|
day_minute: int = self.config.lessons_start[0] * 60 + self.config.lessons_start[1] - self.config.first_bell
|
|
for lesson_number in range(self.config.num_lessons):
|
|
if lesson_number != 0 or self.config.first_bell_before_first_lesson:
|
|
self.bells.append(Bell(SoundType.FIRST_BELL, day_minute // 60, day_minute % 60))
|
|
day_minute += self.config.first_bell
|
|
self.bells.append(Bell(SoundType.SECOND_BELL, day_minute // 60, day_minute % 60))
|
|
day_minute += self.config.lesson_length
|
|
self.bells.append(Bell(SoundType.BREAK, day_minute // 60, day_minute % 60))
|
|
day_minute += self.config.break_length - self.config.first_bell
|
|
|
|
for sound in self.config.custom_sounds:
|
|
for sound_time in sound.times:
|
|
self.bells.append(Bell(SoundType.CUSTOM, *sound_time, custom_file=sound.sound_file, custom_name=sound.name))
|
|
|
|
# so that the first bell before the first lesson can't roll time into the negatives
|
|
self.bells = [bell for bell in self.bells if bell.hour <= 23]
|
|
self.bells.sort(key=lambda sound: sound.hour * 60 + sound.minute)
|
|
|
|
self.ui.set_schedule(self.get_bells_list())
|
|
|
|
def get_bells_list(self) -> list:
|
|
bells_list: list = []
|
|
for bell in self.bells:
|
|
if bell.sound != SoundType.CUSTOM: bell_name: str = BELL_NAMES[bell.sound]
|
|
else: bell_name: str = '"' + bell.custom_name + '"'
|
|
bells_list.append(f"{bell.hour:02}:{bell.minute:02} - {bell_name}")
|
|
return bells_list
|
|
|
|
def menu_event(self, button) -> None:
|
|
if button == 0: self.apply_config()
|
|
elif button == 1: self.save_config()
|
|
elif button == 2: self.bells_enabled = True
|
|
elif button == 3: self.bells_enabled = False
|
|
|
|
def update(self) -> None:
|
|
if not self.bells_enabled:
|
|
return
|
|
t = time.localtime()
|
|
for bell_n, bell in enumerate(self.bells):
|
|
if (not bell.played) and (bell.hour == t.tm_hour) and (bell.minute == t.tm_min):
|
|
bell.played = True
|
|
self.ui.select_bell(bell_n)
|
|
if bell.sound == SoundType.FIRST_BELL: play(self.config.sound_files[0])
|
|
elif bell.sound == SoundType.SECOND_BELL: play(self.config.sound_files[1])
|
|
elif bell.sound == SoundType.BREAK: play(self.config.sound_files[2])
|
|
elif bell.sound == SoundType.CUSTOM: play(bell.custom_file)
|
|
break
|
|
|
|
def load_config(self) -> None:
|
|
try:
|
|
with open("config.json", "r") as fp:
|
|
self.config = Config(**json.load(fp))
|
|
for i in range(len(self.config.custom_sounds)):
|
|
self.config.custom_sounds[i] = CustomSound(**self.config.custom_sounds[i])
|
|
except:
|
|
# if something goes wrong, load the default values
|
|
# nothing should go wrong with the config file, if the user doesn't edit
|
|
# it manually. if that's the case - it's their fault, not mine :P
|
|
self.config = Config()
|
|
|
|
def save_config(self) -> None:
|
|
temp_config = Config()
|
|
self.ui.get_settings(temp_config, CustomSound)
|
|
with open("config.json", "w") as fp:
|
|
json.dump(dataclasses.asdict(temp_config), fp)
|