""" Provides the graphical front-end for Piket. """ import os import sys from urllib.parse import urljoin # pylint: disable=E0611 from PySide2.QtWidgets import ( QApplication, QPushButton, QGridLayout, QWidget, QMainWindow, QSizePolicy, QToolBar, ) from PySide2.QtGui import QIcon from PySide2.QtCore import QSize # pylint: enable=E0611 import requests from piket_client.sound import PLOP_WAVE def plop() -> None: """ Asynchronously play the plop sound. """ PLOP_WAVE.play() SERVER_URL = "http://127.0.0.1:5000" def get_people() -> [dict]: ''' Request list of active people from the server. ''' request = requests.get(urljoin(SERVER_URL, "/people")) return request.json()["people"] class NameButton(QPushButton): """ Wraps a QPushButton to provide a counter. """ def __init__(self, person: dict, *args, **kwargs) -> None: self.person_id = person["id"] self.name = person["name"] self.count = person["count"] super().__init__(self.current_label, *args, **kwargs) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.clicked.connect(self.plop) @property def current_label(self) -> str: ''' Return the label to show on the button. ''' return f"{self.name} ({self.count})" def plop(self) -> None: ''' Process a click on this button. ''' req = requests.post( urljoin(SERVER_URL, f"people/{self.person_id}/add_consumption") ) if req.status_code == 200: json = req.json() person = json["person"] self.count = person["count"] self.setText(self.current_label) plop() else: print("Oh shit kapot") class NameButtons(QWidget): ''' Main widget responsible for capturing presses and registering them. ''' def __init__(self) -> None: super().__init__() self.layout = None self.init_ui() def init_ui(self) -> None: ''' Initialize UI: build GridLayout, retrieve People and build a button for each. ''' self.layout = QGridLayout() for index, person in enumerate(get_people()): button = NameButton(person) self.layout.addWidget(button, index // 2, index % 2) self.setLayout(self.layout) class PiketMainWindow(QMainWindow): ''' QMainWindow subclass responsible for showing the main application window. ''' def __init__(self) -> None: super().__init__() self.main_widget = None self.toolbar = None self.init_ui() def init_ui(self) -> None: ''' Initialize the UI: construct main widget and toolbar. ''' self.main_widget = NameButtons() self.setCentralWidget(self.main_widget) font_metrics = self.fontMetrics() icon_size = font_metrics.height() * 2.5 # Initialize toolbar self.toolbar = QToolBar() self.toolbar.setIconSize(QSize(icon_size, icon_size)) # Left self.toolbar.addAction(self.load_icon("add_person.svg"), "Nieuw persoon") self.toolbar.addAction(self.load_icon("undo.svg"), "Heydrich") self.toolbar.addWidget(self.create_spacer()) # Right self.toolbar.addAction(self.load_icon("beer_bottle.svg"), "Bierrr") self.addToolBar(self.toolbar) @staticmethod def create_spacer() -> QWidget: """ Return an empty QWidget that automatically expands. """ spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) return spacer icons_dir = os.path.join(os.path.dirname(__file__), "icons") @classmethod def load_icon(cls, filename: str) -> QIcon: """ Return a QtIcon loaded from the given `filename` in the icons directory. """ return QIcon(os.path.join(cls.icons_dir, filename)) def main() -> None: ''' Main entry point of GUI client. ''' app = QApplication(sys.argv) font = app.font() size = font.pointSize() font.setPointSize(size * 1.75) app.setFont(font) main_window = PiketMainWindow() main_window.show() app.exec_() if __name__ == "__main__": main()