quizzy 0.2.0__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
quizzy/app.py CHANGED
@@ -3,10 +3,13 @@ from __future__ import annotations
3
3
  import argparse
4
4
  import pathlib
5
5
 
6
- from textual import app, containers, log, message, reactive, screen, widgets
6
+ from textual import app, binding, containers, log, message, reactive, screen, widgets
7
7
 
8
8
  from quizzy import __version__, models
9
9
 
10
+ NoCorrectAnswerType = type("NoCorrectAnswerType", (object,), {})
11
+ NoCorrectAnswer = NoCorrectAnswerType()
12
+
10
13
 
11
14
  def get_arg_parser() -> argparse.ArgumentParser:
12
15
  parser = argparse.ArgumentParser(prog=__name__.split(".")[0], description="A terminal quiz app")
@@ -15,9 +18,16 @@ def get_arg_parser() -> argparse.ArgumentParser:
15
18
  return parser
16
19
 
17
20
 
18
- class AnswerScreen(screen.ModalScreen[models.Team | None]):
21
+ QuestionScreenResult = models.Team | NoCorrectAnswerType | None
22
+
23
+
24
+ class AnswerScreen(screen.ModalScreen[QuestionScreenResult], can_focus=True):
19
25
  NOONE_ANSWERED_ID = "__noone-answered"
20
26
 
27
+ BINDINGS = [
28
+ binding.Binding("escape", "no_correct_answer", "Dismiss", key_display="Esc"),
29
+ ]
30
+
21
31
  def __init__(self, category: str, question: models.Question, teams: list[models.Team]) -> None:
22
32
  super().__init__(classes="question-answer-screen")
23
33
  self.category = category
@@ -32,7 +42,7 @@ class AnswerScreen(screen.ModalScreen[models.Team | None]):
32
42
  answer_widget = widgets.Markdown(self.question.answer, id="answer")
33
43
  answer_widget.border_title = "Answer"
34
44
 
35
- whoanswered = containers.HorizontalGroup(
45
+ who_answered = containers.HorizontalGroup(
36
46
  *[
37
47
  containers.Vertical(widgets.Button(team.name, id=team_id, variant="primary"))
38
48
  for team_id, team in self.teams.items()
@@ -40,12 +50,12 @@ class AnswerScreen(screen.ModalScreen[models.Team | None]):
40
50
  id="who-answered",
41
51
  classes="horizontal-100",
42
52
  )
43
- whoanswered.border_title = "Who Answered Correctly?"
53
+ who_answered.border_title = "Who Answered Correctly?"
44
54
 
45
55
  container = containers.Grid(
46
56
  question_widget,
47
57
  answer_widget,
48
- whoanswered,
58
+ who_answered,
49
59
  containers.Horizontal(
50
60
  widgets.Button(
51
61
  "😭 No one answered correctly 😭", id=self.NOONE_ANSWERED_ID, variant="error", classes="button-100"
@@ -57,18 +67,25 @@ class AnswerScreen(screen.ModalScreen[models.Team | None]):
57
67
  )
58
68
 
59
69
  container.border_title = f"{self.category} - {self.question.value} points"
70
+ yield widgets.Footer()
60
71
  yield container
61
72
 
73
+ def action_no_correct_answer(self) -> None:
74
+ self.dismiss(NoCorrectAnswer)
75
+
62
76
  def on_button_pressed(self, event: widgets.Button.Pressed) -> None:
63
77
  if event.button.id == self.NOONE_ANSWERED_ID:
64
- self.dismiss(None)
78
+ self.dismiss(NoCorrectAnswer)
65
79
  elif event.button.id in self.teams:
66
80
  team = self.teams[event.button.id]
67
81
  self.dismiss(team)
68
82
 
69
83
 
70
- class QuestionScreen(screen.ModalScreen[models.Team | None]):
84
+ class QuestionScreen(screen.ModalScreen[QuestionScreenResult], can_focus=True):
71
85
  SHOW_ANSWER_ID = "show-answer"
86
+ BINDINGS = [
87
+ binding.Binding("escape", "dismiss(None)", "Dismiss"),
88
+ ]
72
89
 
73
90
  def __init__(self, category: str, question: models.Question, teams: list[models.Team]) -> None:
74
91
  super().__init__(classes="question-answer-screen")
@@ -91,17 +108,22 @@ class QuestionScreen(screen.ModalScreen[models.Team | None]):
91
108
  )
92
109
 
93
110
  container.border_title = f"{self.category} - {self.question.value} points"
111
+ yield widgets.Footer()
94
112
  yield container
95
113
 
96
114
  def on_button_pressed(self, event: widgets.Button.Pressed) -> None:
97
- def dismiss(team: models.Team | None) -> None:
115
+ def dismiss(team: QuestionScreenResult) -> None:
98
116
  self.dismiss(team)
99
117
 
100
118
  if event.button.id == self.SHOW_ANSWER_ID:
119
+ event.stop()
101
120
  self.app.push_screen(AnswerScreen(self.category, self.question, self.teams), dismiss)
102
121
 
103
122
 
104
- class TeamScore(containers.Vertical):
123
+ class TeamScore(containers.Horizontal):
124
+ MODIFIER_BUTTON_VALUE = 100
125
+ _ADD_BUTTON_ID = f"add-{MODIFIER_BUTTON_VALUE}"
126
+ _SUBTRACT_BUTTON_ID = f"subtract-{MODIFIER_BUTTON_VALUE}"
105
127
  score = reactive.reactive(0, recompose=True)
106
128
 
107
129
  def __init__(self, team: models.Team) -> None:
@@ -112,6 +134,22 @@ class TeamScore(containers.Vertical):
112
134
 
113
135
  def compose(self) -> app.ComposeResult:
114
136
  yield widgets.Static(str(self.score))
137
+ yield containers.Horizontal(
138
+ widgets.Button("+ 100", id=self._ADD_BUTTON_ID, variant="success"),
139
+ widgets.Button("- 100", id=self._SUBTRACT_BUTTON_ID, variant="error"),
140
+ classes="modifier-buttons-container",
141
+ )
142
+
143
+ def on_button_pressed(self, event: widgets.Button.Pressed) -> None:
144
+ if event.button.id == self._ADD_BUTTON_ID:
145
+ self.score += self.MODIFIER_BUTTON_VALUE
146
+ event.stop()
147
+ elif event.button.id == self._SUBTRACT_BUTTON_ID:
148
+ if self.score <= self.MODIFIER_BUTTON_VALUE:
149
+ self.score = 0
150
+ else:
151
+ self.score -= self.MODIFIER_BUTTON_VALUE
152
+ event.stop()
115
153
 
116
154
  def watch_score(self, score: int) -> None:
117
155
  """
@@ -152,13 +190,14 @@ class QuestionButton(widgets.Button):
152
190
  self.disabled = question.answered
153
191
 
154
192
  def on_click(self) -> None:
155
- # First, disable the button to prevent multiple clicks
156
- self.disabled = True
157
- self.question.answered = True
158
-
159
- def wait_for_result(team: models.Team | None) -> None:
193
+ def wait_for_result(team: QuestionScreenResult) -> None:
160
194
  if team is None:
161
- log("question-button: No-one answered the question")
195
+ return
196
+ # First, disable the button to prevent multiple clicks
197
+ self.disabled = True
198
+ self.question.answered = True
199
+ if isinstance(team, NoCorrectAnswerType):
200
+ log("question-button: No one answered the question")
162
201
  else:
163
202
  log(f"question-button: {team.id} answered the question")
164
203
  self.post_message(self.Answered(team, self.question.value))
@@ -206,3 +245,6 @@ class QuizzyApp(app.App[None]):
206
245
 
207
246
  def on_question_button_answered(self, event: QuestionButton.Answered) -> None:
208
247
  self.scoreboard_widget.update_team_score(event.team.id, event.value)
248
+
249
+ def on_mount(self) -> None:
250
+ self.theme = "textual-light"
quizzy/quizzy.tcss CHANGED
@@ -1,8 +1,28 @@
1
1
  TeamScore {
2
- border: solid $primary;
2
+ border: round $primary;
3
3
  align: center top;
4
4
  height: 3;
5
5
  margin: 1;
6
+
7
+ Static {
8
+ max-width: 50%;
9
+ }
10
+
11
+ Horizontal.modifier-buttons-container {
12
+ align: right middle;
13
+
14
+ Button {
15
+ margin: 0 2;
16
+ padding: 0;
17
+ min-width: 8;
18
+ border: none;
19
+ width: 8;
20
+ &:focus {
21
+ /* Disable reversing for text of focussed buttons in the team score row */
22
+ text-style: bold;
23
+ }
24
+ }
25
+ }
6
26
  }
7
27
 
8
28
 
@@ -15,20 +35,20 @@ TeamScore {
15
35
  height: 80%;
16
36
  min-height: 40;
17
37
  padding: 0 2;
18
- border: solid $primary;
38
+ border: round $primary;
19
39
  background: $surface;
20
40
  content-align: center middle;
21
41
 
22
42
  #question {
23
43
  content-align: center middle;
24
44
  padding: 2;
25
- border: solid $foreground 80%;
45
+ border: round $foreground 80%;
26
46
  }
27
47
 
28
48
  #answer {
29
49
  content-align: center middle;
30
50
  padding: 2;
31
- border: solid $success 80%;
51
+ border: round $success 80%;
32
52
 
33
53
  }
34
54
  }
@@ -59,7 +79,7 @@ AnswerScreen {
59
79
  grid-rows: 40% 40% 5 4;
60
80
 
61
81
  #who-answered {
62
- border: solid $foreground 80%;
82
+ border: round $foreground 80%;
63
83
  Button {
64
84
  margin: 0 1;
65
85
  width: 100%;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quizzy
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: A Python TUI quiz app
5
5
  Author-email: Jonas Ehrlich <jonas.ehrlich@gmail.com>
6
6
  License-Expression: MIT
@@ -0,0 +1,10 @@
1
+ quizzy/__init__.py,sha256=IILIKzO2OO60Rhsh3SqhdKHDf7XI_RLRwzztBGZZmHY,78
2
+ quizzy/__main__.py,sha256=TxTz5dT__4--aF49xzShPppphmYLCNYKMraPhp6scIc,117
3
+ quizzy/app.py,sha256=4Eh_c-kmM9T0MlfOmf4huYxdgFMWwQ_eCDzamLE81zQ,9312
4
+ quizzy/models.py,sha256=JrAc2IvtXByIKNg_lX7uQMK19zwi2WsYcNbttXccbOs,1265
5
+ quizzy/quizzy.tcss,sha256=VcPEGQ1J7oGatPf3kB-Hzo9AHQdrI7cOV8hDT_l9-3A,1810
6
+ quizzy-0.3.1.dist-info/METADATA,sha256=XPzfP0l-hLMlOBw19NNmXHfLTSOWKG9qyfHZkB_no1Q,1590
7
+ quizzy-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ quizzy-0.3.1.dist-info/entry_points.txt,sha256=2RiVMgcS4h7TM59u9dyBQFm53cG6Eyekmb8fqZ5rXHM,48
9
+ quizzy-0.3.1.dist-info/licenses/LICENSE,sha256=JWN3MACgsucm6y_rlL_2MUzst0-wNh-Wab3XkxtfVzM,1070
10
+ quizzy-0.3.1.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- quizzy/__init__.py,sha256=IILIKzO2OO60Rhsh3SqhdKHDf7XI_RLRwzztBGZZmHY,78
2
- quizzy/__main__.py,sha256=TxTz5dT__4--aF49xzShPppphmYLCNYKMraPhp6scIc,117
3
- quizzy/app.py,sha256=QP4pZsIYXlzvyd_laf-VhlQzy1Lw8rRIc-UDnoi8PLY,7693
4
- quizzy/models.py,sha256=JrAc2IvtXByIKNg_lX7uQMK19zwi2WsYcNbttXccbOs,1265
5
- quizzy/quizzy.tcss,sha256=ixFfm8pT_JLdL0XZ-4n8u0fs3l9lAHWQa3-q9d4bRRU,1455
6
- quizzy-0.2.0.dist-info/METADATA,sha256=GGo2imhK6FbqVeaA83L-i8E-ook8d2rBKN0E6FQWLLg,1590
7
- quizzy-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- quizzy-0.2.0.dist-info/entry_points.txt,sha256=2RiVMgcS4h7TM59u9dyBQFm53cG6Eyekmb8fqZ5rXHM,48
9
- quizzy-0.2.0.dist-info/licenses/LICENSE,sha256=JWN3MACgsucm6y_rlL_2MUzst0-wNh-Wab3XkxtfVzM,1070
10
- quizzy-0.2.0.dist-info/RECORD,,
File without changes