Update to 0.2.0
This commit is contained in:
parent
9bc919459e
commit
02deb8a77f
BIN
delete.png
Normal file
BIN
delete.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 159 B |
12
main.py
Normal file
12
main.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# this will either work flawlessly, or fail to do anything...
|
||||||
|
# if you're trying to understand this, sorry for the spaghetti code
|
||||||
|
|
||||||
|
from ui import Ui
|
||||||
|
from scheduler import Scheduler
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sc = Scheduler()
|
||||||
|
ui = Ui(sc.menu_event, sc.update)
|
||||||
|
sc.set_ui_class(ui)
|
||||||
|
|
||||||
|
ui.run()
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
PySide6
|
||||||
|
playsound
|
135
scheduler.py
Normal file
135
scheduler.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
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[list[int]]
|
||||||
|
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[int] = 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[bool] = dataclasses.field(default_factory=lambda: [True, True, True, True, True, True, False])
|
||||||
|
sound_files: list[str] = dataclasses.field(default_factory=lambda: ["", "", ""])
|
||||||
|
first_bell_before_first_lesson: bool = dataclasses.field(default_factory=lambda: True)
|
||||||
|
custom_sounds: list[CustomSound] = 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[str]:
|
||||||
|
bells_list: list[str] = []
|
||||||
|
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:
|
||||||
|
match button:
|
||||||
|
case 0: self.apply_config()
|
||||||
|
case 1: self.save_config()
|
||||||
|
case 2: self.bells_enabled = True
|
||||||
|
case 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)
|
||||||
|
match bell.sound:
|
||||||
|
case SoundType.FIRST_BELL: play(self.config.sound_files[0])
|
||||||
|
case SoundType.SECOND_BELL: play(self.config.sound_files[1])
|
||||||
|
case SoundType.BREAK: play(self.config.sound_files[2])
|
||||||
|
case 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)
|
7
sound.py
Normal file
7
sound.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import playsound
|
||||||
|
|
||||||
|
def play(file_name):
|
||||||
|
try:
|
||||||
|
playsound.playsound(file_name, block=False)
|
||||||
|
except playsound.PlaysoundException:
|
||||||
|
pass
|
37
ui.py
Normal file
37
ui.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from PySide6 import QtWidgets, QtGui, QtCore
|
||||||
|
from windows import MainWindow, MenuActions, ToolBar, MenuBar, Tray, SoundEditorWindow
|
||||||
|
|
||||||
|
class Ui:
|
||||||
|
def __init__(self, menu_callback, periodic_task):
|
||||||
|
self.app = QtWidgets.QApplication([])
|
||||||
|
self.app.setQuitOnLastWindowClosed(False)
|
||||||
|
self.sound_editor_window = SoundEditorWindow()
|
||||||
|
self.main_window = MainWindow(self.sound_editor_window.show)
|
||||||
|
self.menu_actions = MenuActions(self.main_window, menu_callback)
|
||||||
|
self.menu_bar = MenuBar(self.main_window, self.menu_actions)
|
||||||
|
self.toolbar = ToolBar(self.main_window, self.menu_actions)
|
||||||
|
self.tray = Tray(self.main_window)
|
||||||
|
|
||||||
|
self.sound_editor_window.set_add_sound_callback(self.main_window.additional_sounds_box.add_sound)
|
||||||
|
|
||||||
|
# timer for starting bells
|
||||||
|
self.timer = QtCore.QTimer()
|
||||||
|
self.timer.timeout.connect(periodic_task)
|
||||||
|
self.timer.start(1000)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.main_window.show()
|
||||||
|
self.tray.setVisible(True)
|
||||||
|
self.app.exec()
|
||||||
|
|
||||||
|
def get_settings(self, *args, **kwargs):
|
||||||
|
return self.main_window.get_settings(*args, **kwargs)
|
||||||
|
|
||||||
|
def set_settings(self, *args, **kwargs):
|
||||||
|
self.main_window.set_settings(*args, **kwargs)
|
||||||
|
|
||||||
|
def set_schedule(self, *args, **kwargs):
|
||||||
|
self.main_window.set_schedule(*args, **kwargs)
|
||||||
|
|
||||||
|
def select_bell(self, *args, **kwargs):
|
||||||
|
self.main_window.select_bell(*args, **kwargs)
|
277
widgets.py
Normal file
277
widgets.py
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
from PySide6 import QtWidgets, QtGui, QtCore
|
||||||
|
import time
|
||||||
|
|
||||||
|
DAYS_OF_WEEK = "Понеділок Вівторок Середа Четвер П'ятниця Субота Неділя".split()
|
||||||
|
|
||||||
|
class BasicBox(QtWidgets.QScrollArea):
|
||||||
|
def __init__(self, title):
|
||||||
|
super().__init__()
|
||||||
|
self.widget = QtWidgets.QWidget()
|
||||||
|
self.setWidgetResizable(True)
|
||||||
|
self.setWidget(self.widget)
|
||||||
|
self.layout = QtWidgets.QVBoxLayout(self.widget)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(16)
|
||||||
|
title = QtWidgets.QLabel(title)
|
||||||
|
title.setFont(font)
|
||||||
|
self.layout.addWidget(title)
|
||||||
|
|
||||||
|
class WidgetArray(QtWidgets.QWidget):
|
||||||
|
def __init__(self, deleted_item_callback, edit_button=False):
|
||||||
|
super().__init__()
|
||||||
|
self.deleted_item_callback = deleted_item_callback
|
||||||
|
self.main_layout = QtWidgets.QVBoxLayout(self)
|
||||||
|
|
||||||
|
self.control_widget = QtWidgets.QWidget()
|
||||||
|
self.control_layout = QtWidgets.QHBoxLayout(self.control_widget)
|
||||||
|
self.add_button = QtWidgets.QPushButton()
|
||||||
|
self.add_button.setIcon(QtGui.QIcon("add.png"))
|
||||||
|
self.add_button.setFixedSize(22, 22)
|
||||||
|
self.delete_button = QtWidgets.QPushButton()
|
||||||
|
self.delete_button.setIcon(QtGui.QIcon("delete.png"))
|
||||||
|
self.delete_button.setFixedSize(22, 22)
|
||||||
|
self.delete_button.clicked.connect(self.delete_items)
|
||||||
|
self.control_layout.addWidget(self.add_button)
|
||||||
|
self.control_layout.addWidget(self.delete_button)
|
||||||
|
if edit_button:
|
||||||
|
self.edit_button = QtWidgets.QPushButton()
|
||||||
|
self.edit_button.setIcon(QtGui.QIcon("edit.png"))
|
||||||
|
self.edit_button.setFixedSize(22, 22)
|
||||||
|
self.control_layout.addWidget(self.edit_button)
|
||||||
|
self.control_layout.addStretch(1)
|
||||||
|
|
||||||
|
self.widget_list = QtWidgets.QListWidget()
|
||||||
|
|
||||||
|
self.main_layout.addWidget(self.control_widget)
|
||||||
|
self.main_layout.addWidget(self.widget_list)
|
||||||
|
|
||||||
|
def add_item(self, widget):
|
||||||
|
item = QtWidgets.QListWidgetItem()
|
||||||
|
self.widget_list.addItem(item)
|
||||||
|
self.widget_list.setItemWidget(item, widget)
|
||||||
|
|
||||||
|
def get_items(self):
|
||||||
|
for i in range(self.widget_list.count()):
|
||||||
|
yield self.widget_list.itemWidget(self.widget_list.item(i))
|
||||||
|
|
||||||
|
def delete_items(self):
|
||||||
|
selected_items = self.widget_list.selectedItems()
|
||||||
|
if not selected_items: return
|
||||||
|
# if multiple items are selected, and the parent of this widget list keeps track of the widgets somehow,
|
||||||
|
# this makes so they are deleted in the correct order
|
||||||
|
selected_items.sort(reverse=True, key=lambda item: self.widget_list.row(item))
|
||||||
|
for item in selected_items:
|
||||||
|
row = self.widget_list.row(item)
|
||||||
|
self.widget_list.takeItem(row)
|
||||||
|
self.deleted_item_callback(row)
|
||||||
|
|
||||||
|
class BellStatusBox(BasicBox):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Розклад дзвінків")
|
||||||
|
self.bells_list = QtWidgets.QListWidget()
|
||||||
|
self.layout.addWidget(self.bells_list)
|
||||||
|
|
||||||
|
class ScheduleBox(BasicBox):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Налаштування розкладу")
|
||||||
|
self.grid_widget = QtWidgets.QWidget()
|
||||||
|
self.grid_layout = QtWidgets.QGridLayout(self.grid_widget)
|
||||||
|
self.grid_widget.setLayout(self.grid_layout)
|
||||||
|
|
||||||
|
self.first_lesson_input = QtWidgets.QTimeEdit()
|
||||||
|
self.lesson_length_input = QtWidgets.QSpinBox()
|
||||||
|
self.break_length_input = QtWidgets.QSpinBox()
|
||||||
|
self.first_bell_input = QtWidgets.QSpinBox()
|
||||||
|
self.num_lessons_input = QtWidgets.QSpinBox()
|
||||||
|
|
||||||
|
self.lesson_length_input.setRange(20, 90)
|
||||||
|
self.break_length_input.setRange(2, 59)
|
||||||
|
self.first_bell_input.setRange(1, 4)
|
||||||
|
self.num_lessons_input.setRange(1, 20)
|
||||||
|
|
||||||
|
self.break_length_input.valueChanged.connect(self.update_limits)
|
||||||
|
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Перший урок о"), 0, 0)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Тривалість уроку:"), 1, 0)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Перерва"), 2, 0)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Перший дзвоник за"), 3, 0)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Кількість уроків:"), 4, 0)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("хвилин"), 1, 2)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("хвилин"), 2, 2)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("хвилин до уроку"), 3, 2)
|
||||||
|
self.grid_layout.addWidget(self.first_lesson_input, 0, 1)
|
||||||
|
self.grid_layout.addWidget(self.lesson_length_input, 1, 1)
|
||||||
|
self.grid_layout.addWidget(self.break_length_input, 2, 1)
|
||||||
|
self.grid_layout.addWidget(self.first_bell_input, 3, 1)
|
||||||
|
self.grid_layout.addWidget(self.num_lessons_input, 4, 1)
|
||||||
|
|
||||||
|
self.first_bell_before_first_lesson_checkbox = QtWidgets.QCheckBox("Перший дзвоник перед першим уроком")
|
||||||
|
|
||||||
|
self.layout.addWidget(self.grid_widget)
|
||||||
|
self.layout.addWidget(self.first_bell_before_first_lesson_checkbox)
|
||||||
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
|
def update_limits(self, value):
|
||||||
|
self.first_bell_input.setRange(1, value - 1)
|
||||||
|
|
||||||
|
class DaysSelectBox(BasicBox):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Робочі дні")
|
||||||
|
self.days_checkboxes = []
|
||||||
|
for day_name in DAYS_OF_WEEK:
|
||||||
|
self.days_checkboxes.append(QtWidgets.QCheckBox(day_name))
|
||||||
|
self.layout.addWidget(self.days_checkboxes[-1])
|
||||||
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
|
class AdditionalSoundsBox(BasicBox):
|
||||||
|
def __init__(self, show_sound_diag):
|
||||||
|
super().__init__("Додаткові звуки")
|
||||||
|
self.show_sound_diag = show_sound_diag
|
||||||
|
self.sound_list = WidgetArray(self.deleted_item, True)
|
||||||
|
self.sound_list.add_button.clicked.connect(lambda: self.show_sound_diag())
|
||||||
|
self.sound_list.edit_button.clicked.connect(self.edit_sound)
|
||||||
|
self.layout.addWidget(self.sound_list)
|
||||||
|
self.sounds = []
|
||||||
|
self.editing_sound = 0
|
||||||
|
|
||||||
|
def add_sound(self, name, times, sound_file, edit_n=None):
|
||||||
|
if edit_n is None:
|
||||||
|
self.sounds.append([name, times, sound_file])
|
||||||
|
self.sound_list.add_item(QtWidgets.QLabel(name))
|
||||||
|
else:
|
||||||
|
self.sounds[edit_n] = [name, times, sound_file]
|
||||||
|
self.sound_list.widget_list.itemWidget(self.sound_list.widget_list.item(edit_n)).setText(name)
|
||||||
|
|
||||||
|
def deleted_item(self, n):
|
||||||
|
del self.sounds[n]
|
||||||
|
|
||||||
|
def edit_sound(self):
|
||||||
|
edit_n = self.sound_list.widget_list.currentRow()
|
||||||
|
sound = self.sounds[edit_n]
|
||||||
|
self.show_sound_diag(edit_n, *sound)
|
||||||
|
|
||||||
|
class StatusBox(BasicBox):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Стан")
|
||||||
|
self.grid_widget = QtWidgets.QWidget()
|
||||||
|
self.grid_layout = QtWidgets.QGridLayout(self.grid_widget)
|
||||||
|
self.grid_widget.setLayout(self.grid_layout)
|
||||||
|
|
||||||
|
self.day_of_week_widget = QtWidgets.QLabel()
|
||||||
|
self.current_time_widget = QtWidgets.QLabel()
|
||||||
|
self.uptime_widget = QtWidgets.QLabel()
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("День тижня:"), 0, 0)
|
||||||
|
self.grid_layout.addWidget(self.day_of_week_widget, 0, 1)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Поточний час:"), 1, 0)
|
||||||
|
self.grid_layout.addWidget(self.current_time_widget, 1, 1)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Зі запуску:"), 2, 0)
|
||||||
|
self.grid_layout.addWidget(self.uptime_widget, 2, 1)
|
||||||
|
|
||||||
|
self.layout.addWidget(self.grid_widget)
|
||||||
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
|
self.uptime_timer = QtCore.QElapsedTimer()
|
||||||
|
self.timer = QtCore.QTimer()
|
||||||
|
self.timer.timeout.connect(self.update)
|
||||||
|
self.uptime_timer.start()
|
||||||
|
self.timer.start(1000)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
t = time.localtime()
|
||||||
|
self.day_of_week_widget.setText(DAYS_OF_WEEK[t.tm_wday])
|
||||||
|
self.current_time_widget.setText(f"{t.tm_hour:02}:{t.tm_min:02}:{t.tm_sec:02}")
|
||||||
|
seconds = self.uptime_timer.elapsed() / 1000
|
||||||
|
minutes = seconds / 60
|
||||||
|
hours = minutes / 60
|
||||||
|
days = hours / 24
|
||||||
|
self.uptime_widget.setText(f"{int(days)} днів, {int(hours % 24):02}:{int(minutes % 60):02}:{int(seconds % 60):02}")
|
||||||
|
|
||||||
|
class SoundFilesBox(BasicBox):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Звуки")
|
||||||
|
self.grid_widget = QtWidgets.QWidget()
|
||||||
|
self.grid_layout = QtWidgets.QGridLayout(self.grid_widget)
|
||||||
|
self.grid_widget.setLayout(self.grid_layout)
|
||||||
|
|
||||||
|
self.first_bell_file_text = QtWidgets.QLabel()
|
||||||
|
self.second_bell_file_text = QtWidgets.QLabel()
|
||||||
|
self.break_file_text = QtWidgets.QLabel()
|
||||||
|
self.first_bell_file_button = QtWidgets.QPushButton("Огляд")
|
||||||
|
self.second_bell_file_button = QtWidgets.QPushButton("Огляд")
|
||||||
|
self.break_file_button = QtWidgets.QPushButton("Огляд")
|
||||||
|
|
||||||
|
self.first_bell_file_button .clicked.connect(lambda: self.file_select_diag(0))
|
||||||
|
self.second_bell_file_button.clicked.connect(lambda: self.file_select_diag(1))
|
||||||
|
self.break_file_button .clicked.connect(lambda: self.file_select_diag(2))
|
||||||
|
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Перший дзвоник:"), 0, 0)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Другий дзвоник:"), 1, 0)
|
||||||
|
self.grid_layout.addWidget(QtWidgets.QLabel("Перерва:"), 2, 0)
|
||||||
|
self.grid_layout.addWidget(self.first_bell_file_text, 0, 1)
|
||||||
|
self.grid_layout.addWidget(self.second_bell_file_text, 1, 1)
|
||||||
|
self.grid_layout.addWidget(self.break_file_text, 2, 1)
|
||||||
|
self.grid_layout.addWidget(self.first_bell_file_button, 0, 2)
|
||||||
|
self.grid_layout.addWidget(self.second_bell_file_button, 1, 2)
|
||||||
|
self.grid_layout.addWidget(self.break_file_button, 2, 2)
|
||||||
|
|
||||||
|
self.layout.addWidget(self.grid_widget)
|
||||||
|
self.layout.addStretch(1)
|
||||||
|
|
||||||
|
def file_select_diag(self, sound_type):
|
||||||
|
file_name, _ = QtWidgets.QFileDialog.getOpenFileName(self.grid_widget, "SBC - Відкрити файл", "", "Підтримувані файли (*.wav *.mp3 *.flac)")
|
||||||
|
if file_name is None:
|
||||||
|
return
|
||||||
|
match sound_type:
|
||||||
|
case 0: self.first_bell_file_text .setText(file_name)
|
||||||
|
case 1: self.second_bell_file_text.setText(file_name)
|
||||||
|
case 2: self.break_file_text .setText(file_name)
|
||||||
|
|
||||||
|
class SoundEditorBox(BasicBox):
|
||||||
|
def __init__(self, add_sound_callback, quit_callback):
|
||||||
|
super().__init__("Редактор звуку")
|
||||||
|
self.add_sound_callback = add_sound_callback
|
||||||
|
self.quit_callback = quit_callback
|
||||||
|
self.time_select_widget = WidgetArray(lambda _: None)
|
||||||
|
self.sound_name_input = QtWidgets.QLineEdit()
|
||||||
|
self.save_button = QtWidgets.QPushButton("Зберегти")
|
||||||
|
self.time_select_widget.add_button.clicked.connect(lambda: self.time_select_widget.add_item(QtWidgets.QTimeEdit()))
|
||||||
|
self.save_button.clicked.connect(self.save)
|
||||||
|
|
||||||
|
self.file_select_widget = QtWidgets.QWidget()
|
||||||
|
self.file_select_layout = QtWidgets.QHBoxLayout(self.file_select_widget)
|
||||||
|
self.file_name_select_text = QtWidgets.QLabel()
|
||||||
|
self.file_name_select_button = QtWidgets.QPushButton("Огляд")
|
||||||
|
self.file_name_select_button.clicked.connect(self.file_select_diag)
|
||||||
|
self.file_select_layout.addWidget(self.file_name_select_text)
|
||||||
|
self.file_select_layout.addWidget(self.file_name_select_button)
|
||||||
|
self.file_select_layout.addStretch(1)
|
||||||
|
|
||||||
|
self.layout.addWidget(self.sound_name_input)
|
||||||
|
self.layout.addWidget(self.time_select_widget)
|
||||||
|
self.layout.addWidget(self.file_select_widget)
|
||||||
|
self.layout.addStretch(1)
|
||||||
|
self.layout.addWidget(self.save_button)
|
||||||
|
self.edit_n = None
|
||||||
|
|
||||||
|
def file_select_diag(self):
|
||||||
|
file_name, _ = QtWidgets.QFileDialog.getOpenFileName(self.file_select_widget, "SBC - Відкрити файл", "", "Підтримувані файли (*.wav *.mp3 *.flac)")
|
||||||
|
if file_name is None:
|
||||||
|
return
|
||||||
|
self.file_name_select_text.setText(file_name)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.add_sound_callback(
|
||||||
|
self.sound_name_input.text(),
|
||||||
|
[[item.time().hour(), item.time().minute()] for item in self.time_select_widget.get_items()],
|
||||||
|
self.file_name_select_text.text(),
|
||||||
|
self.edit_n
|
||||||
|
)
|
||||||
|
self.clear_edit()
|
||||||
|
self.quit_callback()
|
||||||
|
|
||||||
|
def clear_edit(self):
|
||||||
|
self.sound_name_input.setText("")
|
||||||
|
for i in range(self.time_select_widget.widget_list.count()):
|
||||||
|
self.time_select_widget.widget_list.takeItem(0)
|
||||||
|
self.edit_n = None
|
176
windows.py
Normal file
176
windows.py
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
from PySide6 import QtWidgets, QtGui, QtCore
|
||||||
|
from widgets import BellStatusBox, ScheduleBox, DaysSelectBox, AdditionalSoundsBox, StatusBox, SoundFilesBox, SoundEditorBox
|
||||||
|
import sys
|
||||||
|
|
||||||
|
VERSION = "0.2.0"
|
||||||
|
|
||||||
|
class MainWindow(QtWidgets.QMainWindow):
|
||||||
|
def __init__(self, show_sound_diag):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("SBC - Головне вікно")
|
||||||
|
self.setWindowIcon(QtGui.QIcon("icon.png"))
|
||||||
|
self.setMinimumSize(800, 768)
|
||||||
|
self.main_widget = QtWidgets.QWidget()
|
||||||
|
self.main_layout = QtWidgets.QGridLayout(self.main_widget)
|
||||||
|
|
||||||
|
self.bell_status_box = BellStatusBox()
|
||||||
|
self.schedule_box = ScheduleBox()
|
||||||
|
self.days_select_box = DaysSelectBox()
|
||||||
|
self.additional_sounds_box = AdditionalSoundsBox(show_sound_diag)
|
||||||
|
self.status_box = StatusBox()
|
||||||
|
self.sound_files_box = SoundFilesBox()
|
||||||
|
|
||||||
|
self.main_layout.addWidget(self.bell_status_box, 0, 0, 3, 1)
|
||||||
|
self.main_layout.addWidget(self.schedule_box, 0, 1, 1, 1)
|
||||||
|
self.main_layout.addWidget(self.days_select_box, 1, 1, 1, 1)
|
||||||
|
self.main_layout.addWidget(self.additional_sounds_box, 2, 1, 1, 1)
|
||||||
|
self.main_layout.addWidget(self.status_box, 0, 2, 1, 1)
|
||||||
|
self.main_layout.addWidget(self.sound_files_box, 1, 2, 1, 1)
|
||||||
|
self.setCentralWidget(self.main_widget)
|
||||||
|
|
||||||
|
def get_settings(self, config, CustomSound):
|
||||||
|
first_lesson_input = self.schedule_box.first_lesson_input.time()
|
||||||
|
|
||||||
|
config.lessons_start = [first_lesson_input.hour(), first_lesson_input.minute()]
|
||||||
|
config.lesson_length = self.schedule_box.lesson_length_input.value()
|
||||||
|
config.break_length = self.schedule_box.break_length_input .value()
|
||||||
|
config.first_bell = self.schedule_box.first_bell_input .value()
|
||||||
|
config.num_lessons = self.schedule_box.num_lessons_input .value()
|
||||||
|
config.first_bell_before_first_lesson = self.schedule_box.first_bell_before_first_lesson_checkbox.isChecked()
|
||||||
|
config.workdays = [checkbox.isChecked() for checkbox in self.days_select_box.days_checkboxes]
|
||||||
|
config.sound_files = [
|
||||||
|
self.sound_files_box.first_bell_file_text .text(),
|
||||||
|
self.sound_files_box.second_bell_file_text .text(),
|
||||||
|
self.sound_files_box.break_file_text .text()
|
||||||
|
]
|
||||||
|
config.custom_sounds = [CustomSound(*sound) for sound in self.additional_sounds_box.sounds]
|
||||||
|
|
||||||
|
def set_settings(self, config):
|
||||||
|
self.schedule_box.first_lesson_input .setTime (QtCore.QTime(*config.lessons_start))
|
||||||
|
self.schedule_box.lesson_length_input.setValue(config.lesson_length)
|
||||||
|
self.schedule_box.break_length_input .setValue(config.break_length)
|
||||||
|
self.schedule_box.first_bell_input .setValue(config.first_bell)
|
||||||
|
self.schedule_box.num_lessons_input .setValue(config.num_lessons)
|
||||||
|
self.schedule_box.first_bell_before_first_lesson_checkbox.setChecked(config.first_bell_before_first_lesson)
|
||||||
|
for i in range(0, 7): self.days_select_box.days_checkboxes[i].setChecked(config.workdays[i])
|
||||||
|
self.sound_files_box.first_bell_file_text .setText(config.sound_files[0])
|
||||||
|
self.sound_files_box.second_bell_file_text .setText(config.sound_files[1])
|
||||||
|
self.sound_files_box.break_file_text .setText(config.sound_files[2])
|
||||||
|
self.additional_sounds_box.sounds = [[sound.name, sound.times, sound.sound_file] for sound in config.custom_sounds]
|
||||||
|
for sound in config.custom_sounds: self.additional_sounds_box.sound_list.add_item(QtWidgets.QLabel(sound.name))
|
||||||
|
|
||||||
|
def set_schedule(self, bells):
|
||||||
|
self.bell_status_box.bells_list.clear()
|
||||||
|
for bell in bells:
|
||||||
|
self.bell_status_box.bells_list.addItem(bell)
|
||||||
|
|
||||||
|
def select_bell(self, bell_n):
|
||||||
|
self.bell_status_box.bells_list.setCurrentItem(self.bell_status_box.bells_list.item(bell_n))
|
||||||
|
|
||||||
|
class MenuActions:
|
||||||
|
def __init__(self, window, callback):
|
||||||
|
self.window = window
|
||||||
|
self.callback = callback
|
||||||
|
self.button_apply = QtGui.QAction(QtGui.QIcon("apply.png"), "Застосувати", window)
|
||||||
|
self.button_save = QtGui.QAction(QtGui.QIcon("save.png"), "Зберегти налаштування", window)
|
||||||
|
self.button_start = QtGui.QAction(QtGui.QIcon("start.png"), "Запустити дзвоники", window)
|
||||||
|
self.button_stop = QtGui.QAction(QtGui.QIcon("stop.png"), "Зупинити все", window)
|
||||||
|
self.button_about = QtGui.QAction( "Про програму", window)
|
||||||
|
|
||||||
|
self.button_start.setEnabled(False)
|
||||||
|
|
||||||
|
self.button_apply.triggered.connect(lambda: self.handle_button(0))
|
||||||
|
self.button_save .triggered.connect(lambda: self.handle_button(1))
|
||||||
|
self.button_start.triggered.connect(lambda: self.handle_button(2))
|
||||||
|
self.button_stop .triggered.connect(lambda: self.handle_button(3))
|
||||||
|
self.button_about.triggered.connect(lambda: self.handle_button(4))
|
||||||
|
|
||||||
|
def handle_button(self, button):
|
||||||
|
match button:
|
||||||
|
case 0 | 1 | 2 | 3:
|
||||||
|
self.callback(button)
|
||||||
|
match button:
|
||||||
|
case 0: QtWidgets.QMessageBox.information(self.window, "SBC - Інформація", "Налаштування застосовано!")
|
||||||
|
case 1: QtWidgets.QMessageBox.information(self.window, "SBC - Інформація", "Налаштування збережено!")
|
||||||
|
case 2:
|
||||||
|
self.button_start.setEnabled(False)
|
||||||
|
self.button_stop .setEnabled(True)
|
||||||
|
QtWidgets.QMessageBox.information(self.window, "SBC - Інформація", "Дзвінки запущено. Якщо ви налаштували щось не так, самі винні!")
|
||||||
|
case 3:
|
||||||
|
self.button_start.setEnabled(True)
|
||||||
|
self.button_stop .setEnabled(False)
|
||||||
|
QtWidgets.QMessageBox.information(self.window, "SBC - Інформація", "Дзвінки зупинено. Щось пішло не так, еге ж? Піди і виправи це негайно!")
|
||||||
|
case 4: QtWidgets.QMessageBox.information(self.window, "SBC - Про програму", \
|
||||||
|
f"SBC {VERSION}\nАвтор: 2o\nTelegram: @xfdtw\nDiscord: @2o___\nЯкщо щось не зрозуміло/не працює пишіть туди.")
|
||||||
|
|
||||||
|
class ToolBar(QtWidgets.QToolBar):
|
||||||
|
def __init__(self, window, menu_actions):
|
||||||
|
super().__init__("Toolbar")
|
||||||
|
window.addToolBar(self)
|
||||||
|
self.setIconSize(QtCore.QSize(16, 16))
|
||||||
|
|
||||||
|
self.addAction(menu_actions.button_apply)
|
||||||
|
self.addAction(menu_actions.button_save)
|
||||||
|
self.addSeparator()
|
||||||
|
self.addAction(menu_actions.button_start)
|
||||||
|
self.addAction(menu_actions.button_stop)
|
||||||
|
|
||||||
|
class MenuBar(QtWidgets.QMenuBar):
|
||||||
|
def __init__(self, window, menu_actions):
|
||||||
|
super().__init__()
|
||||||
|
window.setMenuBar(self)
|
||||||
|
self.settings_menu = self.addMenu("&Налаштування")
|
||||||
|
self.bells_menu = self.addMenu("&Дзвінки")
|
||||||
|
self.help_menu = self.addMenu("Д&опомога")
|
||||||
|
|
||||||
|
self.settings_menu.addAction(menu_actions.button_apply)
|
||||||
|
self.settings_menu.addAction(menu_actions.button_save)
|
||||||
|
self.bells_menu.addAction(menu_actions.button_start)
|
||||||
|
self.bells_menu.addAction(menu_actions.button_stop)
|
||||||
|
self.help_menu.addAction(menu_actions.button_about)
|
||||||
|
|
||||||
|
class Tray(QtWidgets.QSystemTrayIcon):
|
||||||
|
def __init__(self, window):
|
||||||
|
super().__init__()
|
||||||
|
self.window = window
|
||||||
|
self.setIcon(QtGui.QIcon("icon.png"))
|
||||||
|
self.setVisible(True)
|
||||||
|
|
||||||
|
self.menu = QtWidgets.QMenu(self.window)
|
||||||
|
open_window = QtGui.QAction("Показати головне вікно", self)
|
||||||
|
close_all = QtGui.QAction("Вийти", self)
|
||||||
|
open_window.triggered.connect(self.window.show)
|
||||||
|
close_all.triggered.connect(self.close_all_diag)
|
||||||
|
self.menu.addAction(open_window)
|
||||||
|
self.menu.addAction(close_all)
|
||||||
|
self.setContextMenu(self.menu)
|
||||||
|
|
||||||
|
def close_all_diag(self):
|
||||||
|
if QtWidgets.QMessageBox.question(self.window, "SBC - Вихід", \
|
||||||
|
"Ви дійсно хочете вийти з програми? Після цього дітки кричатимуть чого в них уроки по 5 годин...", \
|
||||||
|
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No) == QtWidgets.QMessageBox.StandardButton.Yes:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
class SoundEditorWindow(QtWidgets.QMainWindow):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("SBC - Редактор звуків")
|
||||||
|
self.setWindowIcon(QtGui.QIcon("icon.png"))
|
||||||
|
self.setMinimumSize(256, 384)
|
||||||
|
|
||||||
|
def set_add_sound_callback(self, add_sound_callback):
|
||||||
|
self.sound_editor_box = SoundEditorBox(add_sound_callback, self.hide)
|
||||||
|
self.setCentralWidget(self.sound_editor_box)
|
||||||
|
|
||||||
|
def show(self, edit_n=None, name="", times=[], sound_file=""):
|
||||||
|
self.sound_editor_box.edit_n = edit_n
|
||||||
|
self.sound_editor_box.sound_name_input.setText(name)
|
||||||
|
for sound_time in times:
|
||||||
|
self.sound_editor_box.time_select_widget \
|
||||||
|
.add_item(QtWidgets.QTimeEdit(QtCore.QTime(*sound_time)))
|
||||||
|
self.sound_editor_box.file_name_select_text.setText(sound_file)
|
||||||
|
super().show()
|
||||||
|
|
||||||
|
def closeEvent(self, *args, **kwargs):
|
||||||
|
self.sound_editor_box.clear_edit()
|
||||||
|
super().closeEvent(*args, **kwargs)
|
Loading…
x
Reference in New Issue
Block a user