From 6254de74e44a87a5849f30ad07743f119ca43719 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 11 Aug 2026 04:08:55 +0300 Subject: [PATCH] feat(ui): wip session_view.py --- src/mind_reader/ui/views/session_view.py | 160 +++++++++++++++++++---- 1 file changed, 136 insertions(+), 24 deletions(-) diff --git a/src/mind_reader/ui/views/session_view.py b/src/mind_reader/ui/views/session_view.py index c858cc9..37107e1 100644 --- a/src/mind_reader/ui/views/session_view.py +++ b/src/mind_reader/ui/views/session_view.py @@ -39,6 +39,23 @@ class ArrowDirection(Enum): RIGHT = auto() +@dataclass +class SessionStats: + total: int + setted: int + correct: int + wrong: int + taken_time: float + + @property + def accuracy(self) -> float: + accuracy: float + if self.total == 0: + accuracy = 0.0 + accuracy = (self.correct / self.total) * 100 + return round(accuracy, 1) + + @dataclass(frozen=True) class SetterCardColour: NOT_SET = wx.Colour(82, 97, 107) @@ -56,8 +73,8 @@ class GuesserCardColour: @dataclass(frozen=True) class ArrowSymbol: - LEFT: str = "◀" - RIGHT: str = "▶" + LEFT: str = "←" + RIGHT: str = "→" class Card(wx.Panel): @@ -69,18 +86,16 @@ class Card(wx.Panel): self._update() def _setup(self): - self.SetMinSize(wx.Size(35, 35)) + self.SetMinSize(wx.Size(45, 75)) self.label = wx.StaticText(self, style=wx.ALIGN_CENTER) - # font = self.label.GetFont() - # font.SetPointSize(6) - # self.label.SetFont(font) + font = self.label.GetFont() + font.SetPointSize(20) + self.label.SetFont(font) sizer = wx.BoxSizer(wx.VERTICAL) - sizer.AddStretchSpacer() - sizer.Add(self.label, 0, wx.ALIGN_CENTER) - sizer.AddStretchSpacer() + sizer.Add(self.label, 0, wx.EXPAND) self.SetSizer(sizer) @@ -114,13 +129,6 @@ class Card(wx.Panel): colour = GuesserCardColour.WRONG show_arrow = True - match self._direction: - case ArrowDirection.LEFT: - symbol = ArrowSymbol.LEFT - - case ArrowDirection.RIGHT: - symbol = ArrowSymbol.RIGHT - if show_arrow and self._direction: match self._direction: case ArrowDirection.LEFT: @@ -150,33 +158,109 @@ class Card(wx.Panel): class SessionPanel(wx.Panel): - def __init__(self, parent, sequence_length: int = 20): + def __init__( + self, + parent, + setter_name: str, + guesser_name: str, + sequence_length: int = 20, + ): super().__init__(parent) + self._setter_name = setter_name + self._guesser_name = guesser_name self._sequence_length = sequence_length self._setter_sequence: list[Card] = [] self._guesser_sequence: list[Card] = [] + self._statusbar = self._get_statusbar() + self._setup() + self._fit_parent_frame() def _setup(self) -> None: main_sizer = wx.BoxSizer(wx.VERTICAL) + setter_title = wx.StaticText( + self, label=f"Загадывает: {self._setter_name}" + ) setter_sizer = wx.BoxSizer(wx.HORIZONTAL) for _ in range(self._sequence_length): card = Card(self, CardState.SETTER_NOT_SET) - setter_sizer.Add(card, 0, wx.ALL, 3) + setter_sizer.Add(card, 0, wx.ALL, 4) self._setter_sequence.append(card) + guesser_title = wx.StaticText( + self, label=f"Отгадывает: {self._guesser_name}" + ) guesser_sizer = wx.BoxSizer(wx.HORIZONTAL) for _ in range(self._sequence_length): card = Card(self, CardState.GUESSER_DISABLED) - guesser_sizer.Add(card, 0, wx.ALL, 3) + guesser_sizer.Add(card, 0, wx.ALL, 4) self._guesser_sequence.append(card) - main_sizer.Add(setter_sizer, 0, wx.ALL, 5) - main_sizer.Add(guesser_sizer, 0, wx.ALL, 5) + controls_sizer = self._create_controls_sizer() + + secondary_sizer = wx.BoxSizer(wx.VERTICAL) + secondary_sizer.Add(setter_title, 0, wx.LEFT | wx.TOP, 9) + secondary_sizer.Add(setter_sizer, 0, wx.ALL, 5) + secondary_sizer.Add(guesser_title, 0, wx.LEFT | wx.TOP, 9) + secondary_sizer.Add(guesser_sizer, 0, wx.ALL, 5) + secondary_sizer.Add( + controls_sizer, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 25 + ) + + main_sizer.Add(secondary_sizer, 0, wx.LEFT | wx.ALL, 0) + self.SetSizer(main_sizer) + def _create_controls_sizer(self) -> wx.Sizer: + left = wx.Button(self, label=ArrowSymbol.LEFT, size=wx.Size(145, 55)) + right = wx.Button(self, label=ArrowSymbol.RIGHT, size=wx.Size(145, 55)) + + font = left.GetFont() + font.SetPointSize(22) + + left.SetFont(font) + right.SetFont(font) + + controls_sizer = wx.BoxSizer(wx.HORIZONTAL) + controls_sizer.Add(left, 0, wx.RIGHT, 10) + controls_sizer.Add(right, 0, wx.LEFT, 10) + + return controls_sizer + + def _get_statusbar(self) -> Optional[wx.StatusBar]: + top_frame = self.GetTopLevelParent() + + if isinstance(top_frame, wx.Frame): + statusbar = top_frame.GetStatusBar() + if not statusbar: + statusbar = top_frame.CreateStatusBar(1) + statusbar.SetFieldsCount(4) + return statusbar + + return None + + def _autofit_statusbar(self, texts: list[str]) -> None: + if not self._statusbar: + return + + dc = wx.ClientDC(self._statusbar) + width_1 = dc.GetTextExtent(texts[0]).width + width_2 = dc.GetTextExtent(texts[1]).width + width_3 = dc.GetTextExtent(texts[2]).width + self._statusbar.SetStatusWidths([-1, width_1, width_2, width_3]) + + def _fit_parent_frame(self) -> None: + top_frame = self.GetTopLevelParent() + + if isinstance(top_frame, wx.Frame): + self.Layout() + best_size = self.GetBestSize() + min_size = top_frame.ClientToWindowSize(best_size) + top_frame.SetMinSize(min_size) + top_frame.Fit() + def update_setter_card( self, index: int, @@ -193,15 +277,40 @@ class SessionPanel(wx.Panel): ) -> None: self._guesser_sequence[index].set(state, direction) + def update_statusbar( + self, + statusbar_text: Optional[str], + stats: SessionStats, + ): + if not self._statusbar: + return + + if statusbar_text is not None: + self._statusbar.SetStatusText(statusbar_text, 0) + + progress = f" Прогресс {stats.setted}/{stats.total} " + self._statusbar.SetStatusText(progress, 1) + + accuracy = ( + f" Точность {stats.accuracy}% ({stats.correct}/{stats.wrong}) " + ) + self._statusbar.SetStatusText(accuracy, 2) + + minutes = int(stats.taken_time) // 60 + seconds = int(stats.taken_time) % 60 + taken_time = f" Время {minutes:02d}:{seconds:02d} " + self._statusbar.SetStatusText(taken_time, 3) + + self._autofit_statusbar([progress, accuracy, taken_time]) + class TestFrame(wx.Frame): def __init__(self): - super().__init__(None, title="TestFrame", size=wx.Size(900, 500)) - main_panel = SessionPanel(self) + super().__init__(None, title="TestFrame") + main_panel = SessionPanel(self, "Бывалый", "Балбес") main_sizer = wx.BoxSizer(wx.VERTICAL) main_sizer.Add(main_panel, 1, wx.EXPAND) self.SetSizer(main_sizer) - self.Layout() main_panel.update_setter_card( 0, CardState.SETTER_REVEALED, ArrowDirection.LEFT @@ -211,6 +320,9 @@ class TestFrame(wx.Frame): 0, CardState.GUESSER_WRONG, ArrowDirection.RIGHT ) + s = SessionStats(20, 1, 0, 1, 5.1) + main_panel.update_statusbar(None, s) + if __name__ == "__main__": enable_high_dpi_support()