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 +57 -15
- quizzy/quizzy.tcss +25 -5
- {quizzy-0.2.0.dist-info → quizzy-0.3.1.dist-info}/METADATA +1 -1
- quizzy-0.3.1.dist-info/RECORD +10 -0
- quizzy-0.2.0.dist-info/RECORD +0 -10
- {quizzy-0.2.0.dist-info → quizzy-0.3.1.dist-info}/WHEEL +0 -0
- {quizzy-0.2.0.dist-info → quizzy-0.3.1.dist-info}/entry_points.txt +0 -0
- {quizzy-0.2.0.dist-info → quizzy-0.3.1.dist-info}/licenses/LICENSE +0 -0
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
|
-
|
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
|
-
|
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
|
-
|
53
|
+
who_answered.border_title = "Who Answered Correctly?"
|
44
54
|
|
45
55
|
container = containers.Grid(
|
46
56
|
question_widget,
|
47
57
|
answer_widget,
|
48
|
-
|
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(
|
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[
|
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:
|
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.
|
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
|
-
|
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
|
-
|
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:
|
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:
|
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:
|
45
|
+
border: round $foreground 80%;
|
26
46
|
}
|
27
47
|
|
28
48
|
#answer {
|
29
49
|
content-align: center middle;
|
30
50
|
padding: 2;
|
31
|
-
border:
|
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:
|
82
|
+
border: round $foreground 80%;
|
63
83
|
Button {
|
64
84
|
margin: 0 1;
|
65
85
|
width: 100%;
|
@@ -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,,
|
quizzy-0.2.0.dist-info/RECORD
DELETED
@@ -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
|
File without changes
|
File without changes
|