diff --git a/katrain/__main__.py b/katrain/__main__.py index b343433..b677496 100644 --- a/katrain/__main__.py +++ b/katrain/__main__.py @@ -23,7 +23,7 @@ from kivy.uix.popup import Popup from kivy.uix.screenmanager import Screen from katrain.core.ai import ai_move -from katrain.core.common import OUTPUT_INFO, OUTPUT_ERROR, OUTPUT_DEBUG, OUTPUT_EXTRA_DEBUG, OUTPUT_KATAGO_STDERR, find_package_resource +from katrain.core.common import OUTPUT_INFO, OUTPUT_ERROR, OUTPUT_DEBUG, OUTPUT_EXTRA_DEBUG, OUTPUT_KATAGO_STDERR, find_package_resource, LANGUAGE from katrain.core.engine import KataGoEngine from katrain.core.game import Game, IllegalMoveException, KaTrainSGF from katrain.core.sgf_parser import Move, ParseError @@ -331,9 +331,6 @@ class KaTrainGui(Screen): return True -LANGUAGE = "nl" -i18n = Lang(LANGUAGE) - class KaTrainApp(MDApp): gui = ObjectProperty(None) diff --git a/katrain/core/common.py b/katrain/core/common.py index 77adf98..42465fc 100644 --- a/katrain/core/common.py +++ b/katrain/core/common.py @@ -86,3 +86,7 @@ class Lang(Observable): # update all the kv rules attached to this text for func, args, kwargs in self.observers: func(args[0], None, None) + +LANGUAGE = "nl" +i18n = Lang(LANGUAGE) + diff --git a/katrain/gui.kv b/katrain/gui.kv index 6be5f6a..f3f60ae 100644 --- a/katrain/gui.kv +++ b/katrain/gui.kv @@ -89,9 +89,11 @@ text_color: root.color if self.state=='down' else self.inactive_color outline_color: root.color if self.state=='down' else self.inactive_color +: # only wants height + width: self.label.texture_size[0] + self.hor_padding * 2 + : # only wants height width: self.label.texture_size[0] + self.hor_padding * 2 - font_size: self.height * 0.8 : @@ -185,7 +187,9 @@ background_normal: '' background_color: (0,0,0,0) icon_size: self.icon_size or [min(self.size[0],self.size[1]),min(self.size[0],self.size[1])] + image: image Image: + id: image size: root.icon_size pos: [root.pos[i] + (root.size[i] - root.icon_size[i])/2 for i in [0,1]] source: root.icon @@ -633,7 +637,6 @@ : katrain: app.gui - button_controls: app.gui and app.gui.button_controls orientation: 'vertical' players: {'B':B_player,'W':W_player} engine_status_col: [1,1,1,1] @@ -665,73 +668,66 @@ id: timer size_hint_x: 0.7 CollapsablePanel: - size_hint_y: GRAPH_BOX_HEIGHT + size_hint_y_open: GRAPH_BOX_HEIGHT + closed_label: 'Score Graph' options: ['Score','Win Rate','PtLoss'] option_colors: [SCORE_COLOR,WINRATE_COLOR,POINTLOSS_COLOR] - option_active: [True,False,False] + option_default_active: [True,False,False] options_height: root.tab_option_height ScoreGraph: id: graph - BoxLayout: - size_hint_y: STATS_BOX_HEIGHT + CollapsablePanel: + size_hint_y_open: STATS_BOX_HEIGHT + closed_label: 'Move Stats' + options: ['Score','Win Rate','PtLoss'] + option_colors: [SCORE_COLOR,WINRATE_COLOR,POINTLOSS_COLOR] + option_default_active: [True,True,True] + options_height: root.tab_option_height StatsBox: id: stats - BoxLayout: + CollapsablePanel: size_hint_y: INFO_BOX_HEIGHT - orientation: 'vertical' - ScrollableLabel: - id: status - size_hint: 1, None - height: min(self.parent.height*0.66,self.label.texture_size[1]) - outline_color: RED - ScrollableLabel: - id: info - markup: True - on_ref_press: root.katrain.board_gui.show_pv_from_comments(args[1]) - size_hint: 1, None - height: self.parent.height - status.height - note.height - 1 - TextInput: - id: note - size_hint: 1, None - on_text: root.katrain.set_note(self.text) - hint_text: 'Your SGF notes for this position here.' - height: min( (len(self._lines)+0.7) * self.line_height, self.parent.height - status.height - 2.7*self.line_height) + closed_label: 'Move Info & Notes' + options: ['Info','Notes'] + option_colors: [RED,GREEN] + option_default_active: [True,False] + options_height: root.tab_option_height + MDBoxLayout: + orientation: 'vertical' + ScrollableLabel: + id: status + size_hint: 1, None + height: min(self.parent.height*0.66,self.label.texture_size[1]) + outline_color: RED + ScrollableLabel: + id: info + markup: True + on_ref_press: root.katrain.board_gui.show_pv_from_comments(args[1]) + size_hint: 1, None + height: self.parent.height - status.height - note.height - 1 + TextInput: + id: note + size_hint: 1, None + on_text: root.katrain.set_note(self.text) + hint_text: 'Your SGF notes for this position here.' + height: min( (len(self._lines)+0.7) * self.line_height, self.parent.height - status.height - 2.7*self.line_height) -: +: md_bg_color: BOX_BACKGROUND_COLOR - size: self.parent.button_size - font_size: self.height * 0.7 + size_hint_y: 1 -: +: md_bg_color: BOX_BACKGROUND_COLOR - size: self.parent.button_size - font_size: self.height * 0.7 - on_press: app.gui.controls.check_hide_show() + on_press: app.gui.update_state() + size_hint_y: 1 - - - orientation: 'vertical' - button_size: self.parent.button_size - adaptive_size: True - pos_hint: {'center_y':0.5,'x':0} - spacing: CP_SMALL_SPACING - -: - button_size: self.parent.button_size - adaptive_width: True - - -: +: katrain: app.gui - orientation: 'vertical' - button_size: (self.size[1] * 0.08 , self.size[1] * 0.04) - size_hint_x: None - width: self.minimum_width # if not self.parent.zen else 1 + orientation: 'horizontal' padding: CP_PADDING, CP_PADDING spacing: CP_SPACING - info: info - note: note + show_children: show_children eval: eval policy: policy hints: hints @@ -739,102 +735,42 @@ analyze_extra: analyze_extra analyze_equalize: analyze_equalize analyze_sweep: analyze_sweep - graph: {'winrate': winrate_graph,'score':score_graph,'points':points_graph} - ButtonBoxHolder: - size_hint_y: PLAYER_BOX_HEIGHT - ButtonBox: - RHSButton: - text: 'Pass' - on_left_press: print('pass?') - ButtonBoxHolder: - size_hint_y: TIMER_BOX_HEIGHT - ButtonBox: - RHSToggleButton: - text: 'MAN' - RHSToggleButton: - text: 'BYO' - ButtonBoxHolder: - size_hint_y: GRAPH_BOX_HEIGHT - ButtonBox: - RHSToggleButton: - text: 'Win %' - group: 'graph' - color: WINRATE_COLOR - id: winrate_graph - RHSToggleButton: - text: 'Score' - color: SCORE_COLOR - state: "down" - group: 'graph' - id: score_graph - RHSToggleButton: - text: 'Loss' - group: 'graph' - color: POINTLOSS_COLOR - id: points_graph - ButtonBoxHolder: - size_hint_y: STATS_BOX_HEIGHT - ButtonBox: - RHSToggleButton: - text: 'Win %' - color: WINRATE_COLOR - state: "down" - RHSToggleButton: - text: 'Score' - color: SCORE_COLOR - state: "down" - RHSToggleButton: - text: 'Loss' - color: POINTLOSS_COLOR - state: "down" - ButtonBoxHolder: - size_hint_y: INFO_BOX_HEIGHT - ButtonBox: - RHSToggleButton: - id: info - text: 'Info' - color: RED - state: "down" - group: "text" - RHSToggleButton: - id: note - text: 'Note' - color: GREEN - group: "text" - ButtonBoxHolder: - size_hint_y: INFO_BOX_HEIGHT * 0.01 - ButtonBox: - RHSToggleButton: - id: eval - text: 'Dots' - color: WHITE - RHSToggleButton: - id: ownership - text: 'Territory' - color: WHITE - RHSToggleButton: - id: policy - text: 'Policy' - color: WHITE - RHSToggleButton: - id: hints - text: 'Top Moves' - color: WHITE - RHSButton: - id: analyze_extra - text: 'Extra' - tooltip_text: '[a]\nExtra analysis\nto increase accuracy\nof top move predictions\nand score difference' - on_left_press: root.katrain("analyze-extra","extra") - RHSButton: - id: analyze_equalize - text: 'Equalize' - tooltip_text: '[s]\nAnalysis of all\npredicted top moves\nwith the same accuracy' - on_left_press: root.katrain("analyze-extra","equalize") - RHSButton: - id: analyze_sweep - text: 'Sweep' - tooltip_text: '[d]\nFast analysis\nof all legal moves' - on_left_press: root.katrain("analyze-extra","sweep") + RHSToggleButton: + id: show_children + text: 'Next\nMoves' + color: WHITE + state: 'down' + RHSToggleButton: + id: eval + text: 'Evaluation\nDots' + color: WHITE + RHSToggleButton: + id: ownership + text: 'Expected\nTerritory' + color: WHITE + RHSToggleButton: + id: policy + text: 'Policy\nMoves' + color: WHITE + RHSToggleButton: + id: hints + text: 'Top\nMoves' + color: WHITE + RHSButton: + id: analyze_extra + text: 'Extra' + tooltip_text: '[a]\nExtra analysis\nto increase accuracy\nof top move predictions\nand score difference' + on_left_press: root.katrain("analyze-extra","extra") + RHSButton: + id: analyze_equalize + text: 'Equalize' + tooltip_text: '[s]\nAnalysis of all\npredicted top moves\nwith the same accuracy' + on_left_press: root.katrain("analyze-extra","equalize") + RHSButton: + id: analyze_sweep + text: 'Sweep' + tooltip_text: '[d]\nFast analysis\nof all legal moves' + on_left_press: root.katrain("analyze-extra","sweep") @@ -933,8 +869,7 @@ board_gui: board_gui controls: controls board_controls: board_controls - button_controls: button_controls - analysis_controls: button_controls + analysis_controls: analysis_controls nav_drawer: nav_drawer zen: False NavigationLayout: @@ -946,6 +881,10 @@ MDBoxLayout: orientation: 'vertical' padding: CP_PADDING, CP_PADDING, CP_PADDING, 0 + AnalysisControls: + id: analysis_controls + size_hint_y: None + height: min(100,self.width / 13.5) if not root.zen else 1 BadukPanWidget: id: board_gui BadukPanControls: @@ -957,8 +896,6 @@ size_hint_x: None width: 0 if root.zen else self.height * 0.35 opacity: 0 if root.zen else 1 - RightButtonControls: - id: button_controls MyNavigationDrawer: id: nav_drawer state: "close" diff --git a/katrain/gui/badukpan.py b/katrain/gui/badukpan.py index 8db8dd9..86d7724 100644 --- a/katrain/gui/badukpan.py +++ b/katrain/gui/badukpan.py @@ -316,20 +316,21 @@ class BadukPanWidget(Widget): # children of current moves in undo / review alpha = self.ui_config["ghost_alpha"] - for child_node in current_node.children: - points_lost = child_node.points_lost - move = child_node.move - if move and move.coords is not None: - if points_lost is None: - evalcol = None - else: - evalcol = copy.copy(self.eval_color(points_lost)) - evalcol[3] = alpha - # if ((teaching and child_node.auto_undo) or katrain.controls.play_analyze_mode == "analyze") and child_node.analysis_ready: # TODO:? - if child_node.analysis_ready: - self.active_pv_moves.append((move.coords, [move.gtp()] + child_node.candidate_moves[0]["pv"], current_node)) - scale = self.ui_config["child_scale"] - self.draw_stone(move.coords[0], move.coords[1], (*stone_color[move.player][:3], alpha), None, None, evalcol, evalscale=scale, scale=scale) + if katrain.analysis_controls.show_children.active: + for child_node in current_node.children: + points_lost = child_node.points_lost + move = child_node.move + if move and move.coords is not None: + if points_lost is None: + evalcol = None + else: + evalcol = copy.copy(self.eval_color(points_lost)) + evalcol[3] = alpha + # if ((teaching and child_node.auto_undo) or katrain.controls.play_analyze_mode == "analyze") and child_node.analysis_ready: # TODO:? + if child_node.analysis_ready: + self.active_pv_moves.append((move.coords, [move.gtp()] + child_node.candidate_moves[0]["pv"], current_node)) + scale = self.ui_config["child_scale"] + self.draw_stone(move.coords[0], move.coords[1], (*stone_color[move.player][:3], alpha), None, None, evalcol, evalscale=scale, scale=scale) # hints or PV if katrain.analysis_controls.hints.active and not game_ended and not lock_ai: diff --git a/katrain/gui/kivyutils.py b/katrain/gui/kivyutils.py index 2b366b1..6b1cf9b 100644 --- a/katrain/gui/kivyutils.py +++ b/katrain/gui/kivyutils.py @@ -26,7 +26,12 @@ from kivymd.uix.button import BasePressedButton, BaseFlatButton from kivymd.uix.floatlayout import MDFloatLayout from kivymd.uix.navigationdrawer import MDNavigationDrawer +from katrain.core.common import i18n + # --- new mixins + + + class BackgroundColor(Widget): background_color = ListProperty([1, 1, 1, 0]) @@ -86,6 +91,8 @@ class SizedMDFlatRectangleToggleButton(SizedMDFlatRectangleButton, ToggleButtonB return self.state == "down" return self.state == "down" +class AutoSizedMDFlatRectangleButton(SizedMDFlatRectangleButton): + hor_padding = NumericProperty(3) class AutoSizedMDFlatRectangleToggleButton(SizedMDFlatRectangleToggleButton): hor_padding = NumericProperty(3) @@ -143,42 +150,73 @@ class MainMenuItem(RectangularRippleBehavior, LeftButtonBehavior, MDBoxLayout): class CollapsablePanel(MDBoxLayout): - __events__ = ["on_option_select"] + __events__ = ["on_option_state"] + options = ListProperty([]) options_height = NumericProperty(25) - option_active = ListProperty([]) + options_left_padding = NumericProperty(6) + option_labels = ListProperty([]) + option_default_active = ListProperty([]) option_colors = ListProperty([]) + closed_label = StringProperty('Closed Panel') + + size_hint_y_open = NumericProperty(1) + height_open = NumericProperty(None) + state = OptionProperty("open", options=["open", "close"]) - close_icon = "img/flaticon/previous999.png" - open_icon = "img/flaticon/next999.png" + close_icon = "img/flaticon/previous5.png" + open_icon = "img/flaticon/next5.png" def __init__(self, **kwargs): - self.contents = None - self.header = MDBoxLayout(adaptive_height=True, padding=[4, 0, 0, 0], spacing=2) + self.header, self.contents, self.open_close_button = None, None, None self.option_buttons = [] super().__init__(**kwargs) self.orientation = "vertical" - self.bind(options=self.build, option_colors=self.build, options_height=self.build, option_active=self.build) + self.bind(options=self.build_options, option_colors=self.build_options, options_height=self.build_options, + option_default_active=self.build_options,options_left_padding=self.build_options) + self.bind(state=self.build,size_hint_y_open=self.build,height_open=self.build) + self.build_options() - def build(self, *args, **kwargs): - print(args, kwargs, "b",self.size,self.pos) - self.clear_widgets() - - self.header.clear_widgets() + def build_options(self, *args, **kwargs): + self.header = MDBoxLayout(height=self.options_height,size_hint_y=None, + padding=[self.options_left_padding, 0, 0, 0], spacing=2) self.option_buttons = [] - for opt, opt_col, active in zip(self.options, self.option_colors, self.option_active): - button = AutoSizedMDFlatRectangleToggleButton(text=opt, color=opt_col, height=self.options_height, on_press=self.trigger_select, state="down" if active else "normal") + option_labels = self.option_labels or [i18n._(opt) for opt in self.options] + for lbl, opt_col, active in zip(option_labels, self.option_colors, self.option_default_active): + button = AutoSizedMDFlatRectangleToggleButton(text=lbl,color=opt_col, height=self.options_height, + on_press=self.trigger_select, state="down" if active else "normal") self.option_buttons.append(button) - self.header.add_widget(button) - self.header.add_widget(Label()) # spacer - open_close_button = TransparentIconButton( # << / >> collapse button + self.open_close_button = TransparentIconButton( # << / >> collapse button icon=self.open_close_icon(), icon_size=[0.5 * self.options_height, 0.5 * self.options_height], - size=[0.6 * self.options_height, self.options_height], - on_press=self.set_state, + width=0.75*self.options_height,size_hint_x=None, + on_press=lambda *_args: self.set_state('toggle'), ) - #self.bind(state=lambda _: open_close_button.setter("icon")(self.open_close_icon())) - #self.header.add_widget(open_close_button) + self.bind(state=lambda *_args: self.open_close_button.setter("icon")(None,self.open_close_icon())) + self.build() + + def build(self, *args, **kwargs): + self.header.clear_widgets() + if self.state=='open': + for button in self.option_buttons: + self.header.add_widget(button) + self.header.add_widget(Label()) # spacer + else: + self.header.add_widget(Label(text=i18n._(self.closed_label),halign='right',height=self.options_height)) + self.header.add_widget(self.open_close_button) + + super().clear_widgets() + super().add_widget(self.header) + height, size_hint_y = 1, None + if self.state=='open' and self.contents: + super().add_widget(self.contents) + if self.height_open: + height = self.height_open + else: + size_hint_y = self.size_hint_y_open + else: + height = self.header.height + self.height, self.size_hint_y = height,size_hint_y def open_close_icon(self): return self.open_icon if self.state == "open" else self.close_icon @@ -187,25 +225,26 @@ class CollapsablePanel(MDBoxLayout): if self.contents: raise ValueError("CollapsablePanel can only have one child") self.contents = widget - super().add_widget(self.header) - super().add_widget(self.contents) - print(self.children,"CHILDREN!",self.size,self.pos) + self.build() def set_state(self, state="toggle"): + print("SS",state) if state == "toggle": state = "close" if self.state == "open" else "open" self.state = state + self.build() if self.state == "open": self.trigger_select() def trigger_select(self, *_args): if self.state == "open": - self.on_option_select({opt: btn.state == "down" for opt, btn in zip(self.options, self.option_buttons)}) + self.on_option_state({opt:btn.state == "down" for opt,btn in zip(self.options,self.option_buttons)}) - def on_option_select(self, options): + def on_option_state(self, options): pass + # --- not checked