Digitale bierlijst

gui.py 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. """
  2. Provides the graphical front-end for Piket.
  3. """
  4. import os
  5. import sys
  6. from urllib.parse import urljoin
  7. # pylint: disable=E0611
  8. from PySide2.QtWidgets import (
  9. QApplication,
  10. QGridLayout,
  11. QInputDialog,
  12. QMainWindow,
  13. QPushButton,
  14. QSizePolicy,
  15. QToolBar,
  16. QWidget,
  17. )
  18. from PySide2.QtGui import QIcon
  19. from PySide2.QtCore import QSize
  20. # pylint: enable=E0611
  21. import requests
  22. from piket_client.sound import PLOP_WAVE
  23. def plop() -> None:
  24. """ Asynchronously play the plop sound. """
  25. PLOP_WAVE.play()
  26. SERVER_URL = "http://127.0.0.1:5000"
  27. def get_people() -> [dict]:
  28. """ Request list of active people from the server. """
  29. request = requests.get(urljoin(SERVER_URL, "/people"))
  30. return request.json()["people"]
  31. class NameButton(QPushButton):
  32. """ Wraps a QPushButton to provide a counter. """
  33. def __init__(self, person: dict, *args, **kwargs) -> None:
  34. self.person_id = person["person_id"]
  35. self.name = person["name"]
  36. self.count = person["consumptions"]["1"]
  37. super().__init__(self.current_label, *args, **kwargs)
  38. self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
  39. self.clicked.connect(self.plop)
  40. @property
  41. def current_label(self) -> str:
  42. """ Return the label to show on the button. """
  43. return f"{self.name} ({self.count})"
  44. def plop(self) -> None:
  45. """ Process a click on this button. """
  46. req = requests.post(
  47. urljoin(SERVER_URL, f"people/{self.person_id}/add_consumption")
  48. )
  49. if req.status_code == 200:
  50. json = req.json()
  51. person = json["person"]
  52. self.count = person["consumptions"]["1"]
  53. self.setText(self.current_label)
  54. plop()
  55. else:
  56. print("Oh shit kapot")
  57. class NameButtons(QWidget):
  58. """ Main widget responsible for capturing presses and registering them.
  59. """
  60. def __init__(self) -> None:
  61. super().__init__()
  62. self.layout = None
  63. self.init_ui()
  64. def init_ui(self) -> None:
  65. """ Initialize UI: build GridLayout, retrieve People and build a button
  66. for each. """
  67. self.layout = QGridLayout()
  68. for index, person in enumerate(get_people()):
  69. button = NameButton(person)
  70. self.layout.addWidget(button, index // 2, index % 2)
  71. self.setLayout(self.layout)
  72. class PiketMainWindow(QMainWindow):
  73. """ QMainWindow subclass responsible for showing the main application
  74. window. """
  75. def __init__(self) -> None:
  76. super().__init__()
  77. self.main_widget = None
  78. self.toolbar = None
  79. self.init_ui()
  80. def init_ui(self) -> None:
  81. """ Initialize the UI: construct main widget and toolbar. """
  82. self.main_widget = NameButtons()
  83. self.setCentralWidget(self.main_widget)
  84. font_metrics = self.fontMetrics()
  85. icon_size = font_metrics.height() * 2.5
  86. # Initialize toolbar
  87. self.toolbar = QToolBar()
  88. self.toolbar.setIconSize(QSize(icon_size, icon_size))
  89. # Left
  90. self.toolbar.addAction(
  91. self.load_icon("add_person.svg"), "Nieuw persoon", self.add_person
  92. )
  93. self.toolbar.addAction(self.load_icon("undo.svg"), "Heydrich")
  94. self.toolbar.addWidget(self.create_spacer())
  95. # Right
  96. self.toolbar.addAction(self.load_icon("beer_bottle.svg"), "Bierrr")
  97. self.addToolBar(self.toolbar)
  98. def add_person(self) -> None:
  99. """ Ask for a new Person and register it, then rebuild the central
  100. widget. """
  101. name, ok = QInputDialog.getItem(
  102. self,
  103. "Persoon toevoegen",
  104. "Voer de naam van de nieuwe persoon in, of kies uit de lijst.",
  105. ["Cas", "Frenk"],
  106. 0,
  107. True,
  108. )
  109. if ok and name:
  110. req = requests.post(
  111. urljoin(SERVER_URL, "people"), json={"person": {"name": name}}
  112. )
  113. self.main_widget = NameButtons()
  114. self.setCentralWidget(self.main_widget)
  115. @staticmethod
  116. def create_spacer() -> QWidget:
  117. """ Return an empty QWidget that automatically expands. """
  118. spacer = QWidget()
  119. spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
  120. return spacer
  121. icons_dir = os.path.join(os.path.dirname(__file__), "icons")
  122. @classmethod
  123. def load_icon(cls, filename: str) -> QIcon:
  124. """ Return a QtIcon loaded from the given `filename` in the icons
  125. directory. """
  126. return QIcon(os.path.join(cls.icons_dir, filename))
  127. def main() -> None:
  128. """ Main entry point of GUI client. """
  129. app = QApplication(sys.argv)
  130. font = app.font()
  131. size = font.pointSize()
  132. font.setPointSize(size * 1.75)
  133. app.setFont(font)
  134. main_window = PiketMainWindow()
  135. main_window.show()
  136. app.exec_()
  137. if __name__ == "__main__":
  138. main()