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

Sign up to get free protection for your applications and to get access to all the features.
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