Jun-20-2023, 02:05 PM
Hello everyone.
Here is my alarms app. I did it for fun, because programming is a hobby for me, and because my girlfriend asked for something to help her remember taking medicine.
Any feedback will be appreciated!
GitHub repo
main.py
Here is my alarms app. I did it for fun, because programming is a hobby for me, and because my girlfriend asked for something to help her remember taking medicine.
Any feedback will be appreciated!

GitHub repo
main.py
import sys import datetime as dt from PySide6 import QtWidgets, QtGui, QtCore from db import Database from alarm import Alarm from main_window import TimerWindow def check_alarms() -> None: """Checks alarms and display notification.""" now = dt.datetime.now() alarms = [Alarm(t[0], t[1], t[2], t[3]) for t in Database().alarms] for alarm in alarms: hour = alarm.hour minutes = alarm.minutes description = alarm.description if hour == now.hour and minutes == now.minute: aviso = QtWidgets.QMessageBox() aviso.setIcon(QtWidgets.QMessageBox.Icon.Information) aviso.setWindowTitle(f"Alarm {hour:02}:{minutes:02}") aviso.setText(description) aviso.exec() def main() -> None: """Program start""" app = QtWidgets.QApplication(sys.argv) app.setQuitOnLastWindowClosed(False) # Timer timer = QtCore.QTimer() timer.start(1000 * 31) # Timer timeout every 31 seconds timer.timeout.connect(check_alarms) # Main window window = TimerWindow() # System tray tray = QtWidgets.QSystemTrayIcon() icon = QtGui.QIcon("icon.ico") tray.setIcon(icon) tray.setVisible(True) # Tray menu menu = QtWidgets.QMenu() open_window = QtGui.QAction("Show alarms") open_window.triggered.connect(window.show) menu.addAction(open_window) close_app = QtGui.QAction("Quit") close_app.triggered.connect(app.quit) menu.addAction(close_app) tray.setContextMenu(menu) window.show() sys.exit(app.exec()) if __name__ == "__main__": main()main_window.py
import sys from PySide6 import QtWidgets, QtGui from alarm import Alarm from db import Database class NewTimerDialog(QtWidgets.QDialog): def __init__(self) -> None: super().__init__() self.setWindowTitle("Add alarm") buttons = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel self.dialog_buttons = QtWidgets.QDialogButtonBox(buttons) self.dialog_buttons.accepted.connect(self.accept) self.dialog_buttons.rejected.connect(self.reject) self.layout = QtWidgets.QVBoxLayout() self.label_time = QtWidgets.QLabel("New alarm time:") self.time = QtWidgets.QTimeEdit() self.label_desc = QtWidgets.QLabel("Alarm description:") self.description = QtWidgets.QLineEdit() self.layout.addWidget(self.label_time) self.layout.addWidget(self.time) self.layout.addWidget(self.label_desc) self.layout.addWidget(self.description) self.layout.addWidget(self.dialog_buttons) self.setLayout(self.layout) class TimerWindow(QtWidgets.QMainWindow): def __init__(self) -> None: super().__init__() self.db = Database() self.setWindowTitle("Despertador 0.2.0") self.setWindowIcon(QtGui.QIcon("icon.ico")) self.setMinimumSize(240, 360) self.main_panel = QtWidgets.QWidget() self.main_layout = QtWidgets.QGridLayout(self.main_panel) self.btn_add = QtWidgets.QPushButton("Add alarm") self.btn_add.clicked.connect(self.add_timer) self.timers_panel = QtWidgets.QWidget() self.timers_layout = QtWidgets.QVBoxLayout(self.timers_panel) self.timers_layout.addStretch() self.timers_layout.setDirection(self.timers_layout.Direction.BottomToTop) self.main_layout.addWidget(self.btn_add, 0, 0) self.main_layout.addWidget(self.timers_panel, 1, 0) # DATA self.timers = [] self.update_timers() for timer in self.timers: self.display_timer(timer) self.scroll = QtWidgets.QScrollArea() self.scroll.setWidgetResizable(True) self.scroll.setVerticalScrollBarPolicy( self.scroll.verticalScrollBarPolicy().ScrollBarAlwaysOn ) self.scroll.setWidget(self.main_panel) self.setCentralWidget(self.scroll) def update_timers(self) -> None: self.timers = [Alarm(t[0], t[1], t[2], t[3]) for t in self.db.alarms] def display_timer(self, timer: Alarm) -> None: def remove_from_layout( timer, layout: QtWidgets.QLayout, widget: QtWidgets.QWidget ) -> None: layout.removeWidget(widget) widget.deleteLater() self.remove_timer(timer) widget = QtWidgets.QGroupBox() layout = QtWidgets.QGridLayout(widget) label_description = QtWidgets.QLabel(f"<b>{timer.description}</b>") label_time = QtWidgets.QLabel(f"{timer.hour:02}:{timer.minutes:02}") btn_remove = QtWidgets.QPushButton(text="Remove alarm") btn_remove.clicked.connect(lambda: remove_from_layout(timer, layout, widget)) layout.addWidget(label_description, 0, 0) layout.addWidget(label_time, 1, 0) layout.addWidget(btn_remove, 2, 0) self.timers_layout.addWidget(widget) def add_timer(self) -> None: new_timer = NewTimerDialog() if new_timer.exec(): description = new_timer.description.text() if not description: description = "New alarm" hour = new_timer.time.time().hour() minutes = new_timer.time.time().minute() id = self.db.add_alarm(description=description, hour=hour, minutes=minutes) self.display_timer(Alarm(id, description, hour, minutes)) def remove_timer(self, timer) -> None: self.db.remove_alarme(key=timer.id) self.update_timers()db.py
import pathlib import sqlite3 DB_TIMERS = pathlib.Path("alarms.db") class Database: """Database controller.""" def __init__(self, db=DB_TIMERS) -> None: self._db = pathlib.Path(db) self._query( """CREATE TABLE IF NOT EXISTS tb_alarms (id INTEGER NOT NULL PRIMARY KEY, description TEXT, hour INTEGER, minutes INTEGER);""" ) def add_alarm(self, description: str, hour: int, minutes: int) -> None: """Adds a new alarm.""" with sqlite3.connect(self._db) as conn: c = conn.cursor() c.execute( "INSERT INTO tb_alarms (description, hour, minutes) VALUES (?, ?, ?);", [description, hour, minutes], ) return c.lastrowid def remove_alarme(self, key: int) -> None: """Remove a alarm by the primary key.""" with sqlite3.connect(self._db) as conn: c = conn.cursor() c.execute("DELETE FROM tb_alarms WHERE id=(?);", (key,)) def _query(self, query: str) -> None: """DEBUG ONLY, directly execute queries.""" with sqlite3.connect(self._db) as conn: c = conn.cursor() return c.execute(query) @property def alarms(self) -> list[tuple]: """List of all alarms on the database.""" with sqlite3.connect(self._db) as conn: c = conn.cursor() return c.execute("SELECT * FROM tb_alarms;").fetchall()alarm.py
from dataclasses import dataclass @dataclass class Alarm: id: int description: str hour: int minutes: int