tweak
This commit is contained in:
1 parent
671a764c6a
commit
d62fc46626
11 files changed
+301
-218
No files matched your search
+9
-9
@@ -147,7 +147,7 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
self.controls.update_players()
|
self.controls.update_players()
|
||||||
self.update_state()
|
self.update_state()
|
||||||
for player_setup_block in PlayerSetupBlock.INSTANCES:
|
for player_setup_block in PlayerSetupBlock.INSTANCES:
|
||||||
player_setup_block.update_players(bw,self.players_info[bw])
|
player_setup_block.update_players(bw, self.players_info[bw])
|
||||||
|
|
||||||
def set_note(self, note):
|
def set_note(self, note):
|
||||||
self.game.current_node.note = note
|
self.game.current_node.note = note
|
||||||
@@ -215,7 +215,7 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
def _do_new_game_popup(self):
|
def _do_new_game_popup(self):
|
||||||
self.controls.timer.paused = True
|
self.controls.timer.paused = True
|
||||||
if not self.new_game_popup:
|
if not self.new_game_popup:
|
||||||
self.new_game_popup = I18NPopup(title_key="New Game title", size_hint=(0.5, 0.6), content=NewGamePopup(self)).__self__
|
self.new_game_popup = I18NPopup(title_key="New Game title", size=[800, 800], content=NewGamePopup(self)).__self__
|
||||||
self.new_game_popup.content.popup = self.new_game_popup
|
self.new_game_popup.content.popup = self.new_game_popup
|
||||||
self.new_game_popup.open()
|
self.new_game_popup.open()
|
||||||
|
|
||||||
@@ -226,6 +226,13 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
self.timer_settings_popup.content.popup = self.timer_settings_popup
|
self.timer_settings_popup.content.popup = self.timer_settings_popup
|
||||||
self.timer_settings_popup.open()
|
self.timer_settings_popup.open()
|
||||||
|
|
||||||
|
def _do_teacher_popup(self):
|
||||||
|
self.controls.timer.paused = True
|
||||||
|
if not self.teacher_settings_popup:
|
||||||
|
self.teacher_settings_popup = I18NPopup(title_key="teacher settings", size=[800, 800], content=ConfigTeacherPopup(self)).__self__
|
||||||
|
self.teacher_settings_popup.content.popup = self.teacher_settings_popup
|
||||||
|
self.teacher_settings_popup.open()
|
||||||
|
|
||||||
# todo pop
|
# todo pop
|
||||||
|
|
||||||
def _do_analyze_sgf_popup(self):
|
def _do_analyze_sgf_popup(self):
|
||||||
@@ -264,13 +271,6 @@ class KaTrainGui(Screen, KaTrainBase):
|
|||||||
self.ai_settings_popup.add_widget(ConfigAIPopupContents(self, self.ai_settings_popup))
|
self.ai_settings_popup.add_widget(ConfigAIPopupContents(self, self.ai_settings_popup))
|
||||||
self.ai_settings_popup.open()
|
self.ai_settings_popup.open()
|
||||||
|
|
||||||
def _do_teacher_popup(self):
|
|
||||||
self.controls.timer.paused = True
|
|
||||||
if not self.teacher_settings_popup:
|
|
||||||
self.teacher_settings_popup = I18NPopup(title_key="Edit Teacher Settings", size_hint=(0.7, 0.8)).__self__
|
|
||||||
self.teacher_settings_popup.add_widget(ConfigTeacherPopup(self, self.teacher_settings_popup))
|
|
||||||
self.teacher_settings_popup.open()
|
|
||||||
|
|
||||||
def _do_output_sgf(self):
|
def _do_output_sgf(self):
|
||||||
for pl in Move.PLAYERS:
|
for pl in Move.PLAYERS:
|
||||||
if not self.game.root.get_property(f"P{pl}"):
|
if not self.game.root.get_property(f"P{pl}"):
|
||||||
|
|||||||
+12
-4
@@ -12,10 +12,6 @@
|
|||||||
"_hint_wide_root_noise": "A higher value here (typically 0.05-0.1)\nmakes the analysis explore more moves\nat the cost of some strength.",
|
"_hint_wide_root_noise": "A higher value here (typically 0.05-0.1)\nmakes the analysis explore more moves\nat the cost of some strength.",
|
||||||
"_enable_ownership": true
|
"_enable_ownership": true
|
||||||
},
|
},
|
||||||
"timer": {
|
|
||||||
"byo_length": 30,
|
|
||||||
"byo_periods": 5
|
|
||||||
},
|
|
||||||
"general": {
|
"general": {
|
||||||
"sgf_load": "~/Downloads",
|
"sgf_load": "~/Downloads",
|
||||||
"sgf_save": "./sgfout",
|
"sgf_save": "./sgfout",
|
||||||
@@ -23,6 +19,10 @@
|
|||||||
"debug_level": 1,
|
"debug_level": 1,
|
||||||
"language": "en"
|
"language": "en"
|
||||||
},
|
},
|
||||||
|
"timer": {
|
||||||
|
"byo_length": 30,
|
||||||
|
"byo_periods": 5
|
||||||
|
},
|
||||||
"game": {
|
"game": {
|
||||||
"size": "19",
|
"size": "19",
|
||||||
"komi": 6.5,
|
"komi": 6.5,
|
||||||
@@ -55,6 +55,14 @@
|
|||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
],
|
],
|
||||||
|
"show_dots": [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
],
|
||||||
"eval_off_show_last": 3,
|
"eval_off_show_last": 3,
|
||||||
"eval_show_ai": true,
|
"eval_show_ai": true,
|
||||||
"lock_ai": false
|
"lock_ai": false
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ class KaTrainBase:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
self.log(f"Missing configuration option {setting}", OUTPUT_ERROR)
|
self.log(f"Missing configuration option {setting}", OUTPUT_ERROR)
|
||||||
|
|
||||||
|
|
||||||
def update_player(self, bw, **kwargs):
|
def update_player(self, bw, **kwargs):
|
||||||
self.players_info[bw].update(**kwargs)
|
self.players_info[bw].update(**kwargs)
|
||||||
|
|
||||||
@@ -100,7 +99,6 @@ class KaTrainBase:
|
|||||||
for v in self.players_info.values():
|
for v in self.players_info.values():
|
||||||
v.periods_used = 0
|
v.periods_used = 0
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_player_info(self) -> Player:
|
def last_player_info(self) -> Player:
|
||||||
return self.players_info[self.game.current_node.player]
|
return self.players_info[self.game.current_node.player]
|
||||||
@@ -108,4 +106,3 @@ class KaTrainBase:
|
|||||||
@property
|
@property
|
||||||
def next_player_info(self) -> Player:
|
def next_player_info(self) -> Player:
|
||||||
return self.players_info[self.game.current_node.next_player]
|
return self.players_info[self.game.current_node.next_player]
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ class Game:
|
|||||||
if eval_thresholds is None:
|
if eval_thresholds is None:
|
||||||
eval_thresholds = self.katrain.config("trainer/eval_thresholds")
|
eval_thresholds = self.katrain.config("trainer/eval_thresholds")
|
||||||
|
|
||||||
player_names = {bw: re.sub(r"['<>:\"/\\|?*]", "", self.root.get_property("P"+bw) or str(self.katrain.players_info[bw])) for bw in "BW" }
|
player_names = {bw: re.sub(r"['<>:\"/\\|?*]", "", self.root.get_property("P" + bw) or str(self.katrain.players_info[bw])) for bw in "BW"}
|
||||||
game_name = f"katrain_{player_names['B']} vs {player_names['W']} {self.game_id}"
|
game_name = f"katrain_{player_names['B']} vs {player_names['W']} {self.game_id}"
|
||||||
file_name = os.path.abspath(os.path.join(path, f"{game_name}.sgf"))
|
file_name = os.path.abspath(os.path.join(path, f"{game_name}.sgf"))
|
||||||
os.makedirs(os.path.dirname(file_name), exist_ok=True)
|
os.makedirs(os.path.dirname(file_name), exist_ok=True)
|
||||||
|
|||||||
+2
-7
@@ -903,13 +903,7 @@
|
|||||||
spacing: 3*CP_SPACING
|
spacing: 3*CP_SPACING
|
||||||
padding: CP_PADDING
|
padding: CP_PADDING
|
||||||
height: (self.parent.item_height or 50) * 5
|
height: (self.parent.item_height or 50) * 5
|
||||||
players: {'B':black,'W':white}
|
|
||||||
PlayerSetup:
|
|
||||||
player: 'B'
|
|
||||||
id: black
|
|
||||||
PlayerSetup:
|
|
||||||
player: 'W'
|
|
||||||
id: white
|
|
||||||
|
|
||||||
<HamburgerMenuContents@MDBoxLayout+BackgroundMixin>:
|
<HamburgerMenuContents@MDBoxLayout+BackgroundMixin>:
|
||||||
katrain: app.gui
|
katrain: app.gui
|
||||||
@@ -921,6 +915,7 @@
|
|||||||
text: i18n._('menu:playersetup')
|
text: i18n._('menu:playersetup')
|
||||||
PlayerSetupBlock:
|
PlayerSetupBlock:
|
||||||
id: player_setup
|
id: player_setup
|
||||||
|
update_global: True
|
||||||
MenuLabel:
|
MenuLabel:
|
||||||
size_hint: 1,1
|
size_hint: 1,1
|
||||||
MenuLabel:
|
MenuLabel:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
from kivy.graphics.context_instructions import Color
|
from kivy.graphics.context_instructions import Color
|
||||||
@@ -136,7 +137,7 @@ class BadukPanWidget(Widget):
|
|||||||
self.draw_hover_contents() # remove ghost
|
self.draw_hover_contents() # remove ghost
|
||||||
|
|
||||||
# drawing functions
|
# drawing functions
|
||||||
def redraw(self,*_args):
|
def redraw(self, *_args):
|
||||||
self.draw_board()
|
self.draw_board()
|
||||||
self.draw_board_contents()
|
self.draw_board_contents()
|
||||||
|
|
||||||
@@ -155,10 +156,10 @@ class BadukPanWidget(Widget):
|
|||||||
Color(*innercol)
|
Color(*innercol)
|
||||||
Line(circle=(self.gridpos_x[x], self.gridpos_y[y], stone_size * 0.475 / 0.85), width=0.1 * stone_size)
|
Line(circle=(self.gridpos_x[x], self.gridpos_y[y], stone_size * 0.475 / 0.85), width=0.1 * stone_size)
|
||||||
|
|
||||||
def eval_color(self, points_lost):
|
def eval_color(self, points_lost, show_dots_for_class: List[bool] = None) -> Optional[List[float]]:
|
||||||
colors = EVAL_COLORS
|
|
||||||
i = evaluation_class(points_lost, self.trainer_config["eval_thresholds"])
|
i = evaluation_class(points_lost, self.trainer_config["eval_thresholds"])
|
||||||
return colors[i]
|
if show_dots_for_class is None or show_dots_for_class[i]:
|
||||||
|
return EVAL_COLORS[i]
|
||||||
|
|
||||||
def draw_board(self, *_args):
|
def draw_board(self, *_args):
|
||||||
if not (self.katrain and self.katrain.game):
|
if not (self.katrain and self.katrain.game):
|
||||||
@@ -236,8 +237,11 @@ class BadukPanWidget(Widget):
|
|||||||
has_stone[m.coords] = m.player
|
has_stone[m.coords] = m.player
|
||||||
|
|
||||||
show_dots_for = {p: self.trainer_config["eval_show_ai"] or katrain.game.players[p].human for p in Move.PLAYERS}
|
show_dots_for = {p: self.trainer_config["eval_show_ai"] or katrain.game.players[p].human for p in Move.PLAYERS}
|
||||||
|
show_dots_for_class = self.trainer_config["show_dots"]
|
||||||
nodes = katrain.game.current_node.nodes_from_root
|
nodes = katrain.game.current_node.nodes_from_root
|
||||||
realized_points_lost = None
|
realized_points_lost = None
|
||||||
|
|
||||||
|
katrain.config("teacher/show_dots")
|
||||||
for i, node in enumerate(nodes[::-1]): # reverse order!
|
for i, node in enumerate(nodes[::-1]): # reverse order!
|
||||||
points_lost = node.points_lost
|
points_lost = node.points_lost
|
||||||
evalsize = 1
|
evalsize = 1
|
||||||
@@ -250,7 +254,7 @@ class BadukPanWidget(Widget):
|
|||||||
if has_stone.get(m.coords) and not drawn_stone.get(m.coords): # skip captures, last only for
|
if has_stone.get(m.coords) and not drawn_stone.get(m.coords): # skip captures, last only for
|
||||||
move_eval_on = show_dots_for.get(m.player) and (i < show_n_eval or full_eval_on)
|
move_eval_on = show_dots_for.get(m.player) and (i < show_n_eval or full_eval_on)
|
||||||
if move_eval_on and points_lost is not None:
|
if move_eval_on and points_lost is not None:
|
||||||
evalcol = self.eval_color(points_lost)
|
evalcol = self.eval_color(points_lost, show_dots_for_class)
|
||||||
else:
|
else:
|
||||||
evalcol = None
|
evalcol = None
|
||||||
inner = stone_color[m.opponent] if i == 0 else None
|
inner = stone_color[m.opponent] if i == 0 else None
|
||||||
|
|||||||
+21
-12
@@ -228,18 +228,6 @@ class StyledSpinner(Spinner):
|
|||||||
item.font_name = self.font_name
|
item.font_name = self.font_name
|
||||||
|
|
||||||
|
|
||||||
class PlayerSetupBlock(MDBoxLayout):
|
|
||||||
players = ObjectProperty(None)
|
|
||||||
INSTANCES = []
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
PlayerSetupBlock.INSTANCES.append(self)
|
|
||||||
|
|
||||||
def update_players(self, bw, player_info): # update sub widget based on gui state change
|
|
||||||
self.players[bw].update_widget(player_type=player_info.player_type, player_subtype=player_info.player_subtype)
|
|
||||||
|
|
||||||
|
|
||||||
class PlayerSetup(MDBoxLayout):
|
class PlayerSetup(MDBoxLayout):
|
||||||
player = OptionProperty("B", options=["B", "W"])
|
player = OptionProperty("B", options=["B", "W"])
|
||||||
|
|
||||||
@@ -267,11 +255,32 @@ class PlayerSetup(MDBoxLayout):
|
|||||||
self.player_subtype.select_key(player_subtype) # should trigger setup options
|
self.player_subtype.select_key(player_subtype) # should trigger setup options
|
||||||
|
|
||||||
def update_global_player_info(self):
|
def update_global_player_info(self):
|
||||||
|
if self.parent and self.parent.update_global:
|
||||||
katrain = MDApp.get_running_app().gui
|
katrain = MDApp.get_running_app().gui
|
||||||
if katrain.game and katrain.game.current_node:
|
if katrain.game and katrain.game.current_node:
|
||||||
katrain.update_player(self.player, **self.player_type_dump)
|
katrain.update_player(self.player, **self.player_type_dump)
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerSetupBlock(MDBoxLayout):
|
||||||
|
players = ObjectProperty(None)
|
||||||
|
black = ObjectProperty(None)
|
||||||
|
white = ObjectProperty(None)
|
||||||
|
update_global = BooleanProperty(False)
|
||||||
|
INSTANCES = []
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.black = PlayerSetup(player="B")
|
||||||
|
self.white = PlayerSetup(player="W")
|
||||||
|
self.players = {"B": self.black, "W": self.white}
|
||||||
|
self.add_widget(self.black)
|
||||||
|
self.add_widget(self.white)
|
||||||
|
PlayerSetupBlock.INSTANCES.append(self)
|
||||||
|
|
||||||
|
def update_players(self, bw, player_info): # update sub widget based on gui state change
|
||||||
|
self.players[bw].update_widget(player_type=player_info.player_type, player_subtype=player_info.player_subtype)
|
||||||
|
|
||||||
|
|
||||||
class PlayerInfo(MDBoxLayout, BackgroundMixin):
|
class PlayerInfo(MDBoxLayout, BackgroundMixin):
|
||||||
captures = NumericProperty(0)
|
captures = NumericProperty(0)
|
||||||
player = OptionProperty("B", options=["B", "W"])
|
player = OptionProperty("B", options=["B", "W"])
|
||||||
|
|||||||
+60
-175
@@ -1,22 +1,21 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Dict, List, DefaultDict, Tuple
|
|
||||||
import re
|
import re
|
||||||
|
from typing import Dict, Tuple, Any, Union, List
|
||||||
|
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
from kivy.properties import StringProperty, BooleanProperty
|
from kivy.properties import StringProperty, BooleanProperty
|
||||||
|
from kivy.uix.anchorlayout import AnchorLayout
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
from kivy.uix.gridlayout import GridLayout
|
|
||||||
from kivy.uix.label import Label
|
|
||||||
from kivy.uix.popup import Popup
|
from kivy.uix.popup import Popup
|
||||||
from kivymd.uix.boxlayout import MDBoxLayout
|
from kivymd.uix.boxlayout import MDBoxLayout
|
||||||
from kivymd.uix.selectioncontrol import MDCheckbox
|
from kivymd.uix.selectioncontrol import MDCheckbox
|
||||||
from kivymd.uix.textfield import MDTextField
|
from kivymd.uix.textfield import MDTextField
|
||||||
|
|
||||||
from katrain.core.constants import OUTPUT_ERROR, OUTPUT_DEBUG
|
from katrain.core.constants import OUTPUT_ERROR, OUTPUT_DEBUG, OUTPUT_INFO
|
||||||
from katrain.core.engine import KataGoEngine
|
from katrain.core.engine import KataGoEngine
|
||||||
from katrain.core.game import Game, GameNode
|
from katrain.core.utils import i18n
|
||||||
from katrain.gui.kivyutils import StyledSpinner
|
from katrain.gui.kivyutils import StyledSpinner, BackgroundMixin
|
||||||
from katrain.gui.style import DEFAULT_FONT
|
from katrain.gui.style import DEFAULT_FONT, EVAL_COLORS
|
||||||
|
|
||||||
|
|
||||||
class I18NPopup(Popup):
|
class I18NPopup(Popup):
|
||||||
@@ -98,7 +97,7 @@ class QuickConfigGui(MDBoxLayout):
|
|||||||
self.popup = None
|
self.popup = None
|
||||||
Clock.schedule_once(lambda _dt: self.set_properties(self))
|
Clock.schedule_once(lambda _dt: self.set_properties(self))
|
||||||
|
|
||||||
def collect_properties(self, widget):
|
def collect_properties(self, widget) -> Dict:
|
||||||
if isinstance(widget, (LabelledTextInput, LabelledSpinner, LabelledCheckBox)) and getattr(widget, "input_property", None):
|
if isinstance(widget, (LabelledTextInput, LabelledSpinner, LabelledCheckBox)) and getattr(widget, "input_property", None):
|
||||||
try:
|
try:
|
||||||
ret = {widget.input_property: widget.input_value}
|
ret = {widget.input_property: widget.input_value}
|
||||||
@@ -111,13 +110,19 @@ class QuickConfigGui(MDBoxLayout):
|
|||||||
ret[k] = v
|
ret[k] = v
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_setting(self, key):
|
def get_setting(self, key) -> Tuple[Any,Union[Dict,List],str]:
|
||||||
keys = key.split("/")
|
keys = key.split("/")
|
||||||
config = self.katrain._config
|
config = self.katrain._config
|
||||||
for k in keys[:-1]:
|
for k in keys[:-1]:
|
||||||
if k not in config:
|
if k not in config:
|
||||||
config[k] = {}
|
config[k] = {}
|
||||||
config = config[k]
|
config = config[k]
|
||||||
|
|
||||||
|
if "::" in keys[-1]:
|
||||||
|
arraykey, ix = keys[-1].split("::")
|
||||||
|
array = config[arraykey]
|
||||||
|
return array[int(ix)], array, ix
|
||||||
|
else:
|
||||||
if keys[-1] not in config:
|
if keys[-1] not in config:
|
||||||
config[keys[-1]] = ""
|
config[keys[-1]] = ""
|
||||||
self.katrain.log(f"Configuration setting {repr(key)} was missing, created it, but this likely indicates a broken config file.", OUTPUT_ERROR)
|
self.katrain.log(f"Configuration setting {repr(key)} was missing, created it, but this likely indicates a broken config file.", OUTPUT_ERROR)
|
||||||
@@ -146,7 +151,7 @@ class QuickConfigGui(MDBoxLayout):
|
|||||||
old_value, conf, key = self.get_setting(multikey)
|
old_value, conf, key = self.get_setting(multikey)
|
||||||
if value != old_value:
|
if value != old_value:
|
||||||
self.katrain.log(f"Updating setting {multikey} = {value}", OUTPUT_DEBUG)
|
self.katrain.log(f"Updating setting {multikey} = {value}", OUTPUT_DEBUG)
|
||||||
conf[key] = value # reference straight back to katrain._config
|
conf[key] = value # reference straight back to katrain._config - may be array or dict
|
||||||
updated.add(multikey)
|
updated.add(multikey)
|
||||||
if save_to_file:
|
if save_to_file:
|
||||||
self.katrain.save_config()
|
self.katrain.save_config()
|
||||||
@@ -158,9 +163,6 @@ class QuickConfigGui(MDBoxLayout):
|
|||||||
|
|
||||||
|
|
||||||
class ConfigTimerPopup(QuickConfigGui):
|
class ConfigTimerPopup(QuickConfigGui):
|
||||||
def __init__(self, katrain):
|
|
||||||
super().__init__(katrain)
|
|
||||||
|
|
||||||
def update_config(self, save_to_file=True):
|
def update_config(self, save_to_file=True):
|
||||||
super().update_config(save_to_file=save_to_file)
|
super().update_config(save_to_file=save_to_file)
|
||||||
for p in self.katrain.players_info.values():
|
for p in self.katrain.players_info.values():
|
||||||
@@ -176,98 +178,66 @@ class NewGamePopup(QuickConfigGui):
|
|||||||
self.rules_spinner.value_refs = [name for abbr, name in katrain.engine.RULESETS_ABBR]
|
self.rules_spinner.value_refs = [name for abbr, name in katrain.engine.RULESETS_ABBR]
|
||||||
|
|
||||||
def update_config(self, save_to_file=True):
|
def update_config(self, save_to_file=True):
|
||||||
updated = super().update_config(save_to_file=save_to_file)
|
super().update_config(save_to_file=save_to_file)
|
||||||
self.katrain.log(f"New game settings: {self.katrain.config('game')}", OUTPUT_DEBUG)
|
self.katrain.log(f"New game settings: {self.katrain.config('game')}", OUTPUT_DEBUG)
|
||||||
if self.restart.active:
|
if self.restart.active:
|
||||||
self.katrain.log("Restarting Engine", OUTPUT_DEBUG)
|
self.katrain.log("Restarting Engine", OUTPUT_DEBUG)
|
||||||
self.katrain.engine.restart()
|
self.katrain.engine.restart()
|
||||||
|
for bw, player_setup in self.player_setup.players.items():
|
||||||
|
self.katrain.update_player(bw, **player_setup.player_type_dump)
|
||||||
self.katrain("new-game")
|
self.katrain("new-game")
|
||||||
|
|
||||||
# for bw, player_widget in self.nav_drawer_contents.player_setup.players.items():
|
|
||||||
# self.players[bw].update(**player_widget.player_def)
|
def wrap_anchor(widget):
|
||||||
|
anchor = AnchorLayout()
|
||||||
|
anchor.add_widget(widget)
|
||||||
|
return anchor
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigTeacherPopup(QuickConfigGui):
|
||||||
|
def __init__(self, katrain):
|
||||||
|
self.build()
|
||||||
|
super().__init__(katrain)
|
||||||
|
|
||||||
|
def add_option_widgets(self, widgets):
|
||||||
|
for widget in widgets:
|
||||||
|
self.options_grid.add_widget(wrap_anchor(widget))
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
undos = self.katrain.config("trainer/num_undo_prompts")
|
||||||
|
thresholds = self.katrain.config("trainer/eval_thresholds")
|
||||||
|
savesgfs = self.katrain.config("trainer/save_feedback")
|
||||||
|
show_dots = self.katrain.config("trainer/show_dots")
|
||||||
|
|
||||||
|
for i, (color, threshold, undo, show_dot, savesgf) in enumerate(zip(EVAL_COLORS, thresholds, undos, show_dots, savesgfs)):
|
||||||
|
self.add_option_widgets(
|
||||||
|
[
|
||||||
|
BackgroundMixin(background_color=color[:3]),
|
||||||
|
LabelledFloatInput(text=str(threshold), input_property=f"trainer/eval_thresholds::{i}"),
|
||||||
|
LabelledFloatInput(text=str(undo), input_property=f"trainer/num_undo_prompts::{i}"),
|
||||||
|
LabelledCheckBox(text=str(show_dot), input_property=f"trainer/show_dots::{i}"),
|
||||||
|
LabelledCheckBox(text=str(savesgf), input_property=f"trainer/save_feedback::{i}"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConfigPopup(QuickConfigGui):
|
class ConfigPopup(QuickConfigGui):
|
||||||
def __init__(self, katrain, popup: Popup, config: Dict, ignore_cats: Tuple = (), **kwargs):
|
def update_config(self, save_to_file=True):
|
||||||
self.config = config
|
updated = super().update_config(save_to_file=save_to_file)
|
||||||
self.ignore_cats = ignore_cats
|
self.katrain.debug_level = self.katrain.config("general/debug_level", OUTPUT_INFO)
|
||||||
self.orientation = "vertical"
|
|
||||||
super().__init__(katrain, popup, **kwargs)
|
|
||||||
Clock.schedule_once(self.build, 0)
|
|
||||||
|
|
||||||
def build(self, _):
|
ignore = {"max_visits", "max_time", "enable_ownership", "wide_root_noise"}
|
||||||
|
detected_restart = [key for key in updated if "engine" in key and not any(ig in key for ig in ignore)]
|
||||||
props_in_col = [0, 0]
|
if detected_restart:
|
||||||
cols = [BoxLayout(orientation="vertical"), BoxLayout(orientation="vertical")]
|
|
||||||
|
|
||||||
for k1, all_d in sorted(self.config.items(), key=lambda tup: -len(tup[1])): # sort to make greedy bin packing work better
|
|
||||||
if k1 in self.ignore_cats:
|
|
||||||
continue
|
|
||||||
d = {k: v for k, v in all_d.items() if isinstance(v, (int, float, str, bool)) and not k.startswith("_")} # no lists . dict could be supported but hard to scale
|
|
||||||
cat = GridLayout(cols=2, rows=len(d) + 1, size_hint=(1, len(d) + 1))
|
|
||||||
cat.add_widget(Label(text=""))
|
|
||||||
cat.add_widget(ScaledLightLabel(text=f"{k1} settings", bold=True))
|
|
||||||
for k2, v in d.items():
|
|
||||||
label = ScaledLightLabel(text=f"{k2}:")
|
|
||||||
widget = self.type_to_widget_class(v)(text=str(v), input_property=f"{k1}/{k2}")
|
|
||||||
hint = all_d.get("_hint_" + k2)
|
|
||||||
if hint:
|
|
||||||
label.tooltip_text = hint
|
|
||||||
if isinstance(widget, LabelledTextInput):
|
|
||||||
widget.hint_text = hint
|
|
||||||
cat.add_widget(label)
|
|
||||||
cat.add_widget(widget)
|
|
||||||
if props_in_col[0] <= props_in_col[1]:
|
|
||||||
cols[0].add_widget(cat)
|
|
||||||
props_in_col[0] += len(d)
|
|
||||||
else:
|
|
||||||
cols[1].add_widget(cat)
|
|
||||||
props_in_col[1] += len(d)
|
|
||||||
|
|
||||||
col_container = BoxLayout(size_hint=(1, 0.9))
|
|
||||||
col_container.add_widget(cols[0])
|
|
||||||
col_container.add_widget(cols[1])
|
|
||||||
self.add_widget(col_container)
|
|
||||||
self.info_label = Label(halign="center")
|
|
||||||
self.apply_button = StyledButton(text="Apply", on_press=lambda _: self.update_config())
|
|
||||||
self.save_button = StyledButton(text="Apply and Save", on_press=lambda _: self.update_config(save_to_file=True))
|
|
||||||
btn_container = BoxLayout(orientation="horizontal", size_hint=(1, 0.1), spacing=1, padding=1)
|
|
||||||
btn_container.add_widget(self.apply_button)
|
|
||||||
btn_container.add_widget(self.info_label)
|
|
||||||
btn_container.add_widget(self.save_button)
|
|
||||||
self.add_widget(btn_container)
|
|
||||||
|
|
||||||
def update_config(self, save_to_file=False):
|
|
||||||
updated_cat = defaultdict(list) # type: DefaultDict[List[str]]
|
|
||||||
try:
|
|
||||||
for k, v in self.collect_properties(self).items():
|
|
||||||
k1, k2 = k.split("/")
|
|
||||||
if self.config[k1][k2] != v:
|
|
||||||
self.katrain.log(f"Updating setting {k} = {v}", OUTPUT_DEBUG)
|
|
||||||
updated_cat[k1].append(k2)
|
|
||||||
self.config[k1][k2] = v
|
|
||||||
self.popup.dismiss()
|
|
||||||
except InputParseError as e:
|
|
||||||
self.info_label.text = str(e)
|
|
||||||
self.katrain.log(e, OUTPUT_ERROR)
|
|
||||||
return
|
|
||||||
|
|
||||||
if save_to_file:
|
|
||||||
self.katrain.save_config()
|
|
||||||
|
|
||||||
engine_updates = updated_cat["engine"]
|
|
||||||
if "visits" in engine_updates:
|
|
||||||
self.katrain.engine.visits = engine_updates["visits"]
|
|
||||||
if {key for key in engine_updates if key not in {"max_visits", "max_time", "enable_ownership", "wide_root_noise"}}:
|
|
||||||
self.katrain.log(f"Restarting Engine after {engine_updates} settings change")
|
|
||||||
self.info_label.text = "Restarting engine\nplease wait."
|
|
||||||
self.katrain.controls.set_status(f"Restarted Engine after {engine_updates} settings change.")
|
|
||||||
|
|
||||||
def restart_engine(_dt):
|
def restart_engine(_dt):
|
||||||
|
self.katrain.log(f"Restarting Engine after {detected_restart} settings change")
|
||||||
|
self.katrain.controls.set_status(i18n._("restarting engine"))
|
||||||
|
|
||||||
old_engine = self.katrain.engine # type: KataGoEngine
|
old_engine = self.katrain.engine # type: KataGoEngine
|
||||||
old_proc = old_engine.katago_process
|
old_proc = old_engine.katago_process
|
||||||
if old_proc:
|
if old_proc:
|
||||||
old_engine.shutdown(finish=True)
|
old_engine.shutdown(finish=False)
|
||||||
new_engine = KataGoEngine(self.katrain, self.config["engine"])
|
new_engine = KataGoEngine(self.katrain, self.config["engine"])
|
||||||
self.katrain.engine = new_engine
|
self.katrain.engine = new_engine
|
||||||
self.katrain.game.engines = {"B": new_engine, "W": new_engine}
|
self.katrain.game.engines = {"B": new_engine, "W": new_engine}
|
||||||
@@ -276,91 +246,6 @@ class ConfigPopup(QuickConfigGui):
|
|||||||
|
|
||||||
Clock.schedule_once(restart_engine, 0)
|
Clock.schedule_once(restart_engine, 0)
|
||||||
|
|
||||||
self.katrain.debug_level = self.config["debug"]["level"]
|
|
||||||
self.katrain.update_state(redraw_board=True)
|
|
||||||
|
|
||||||
|
|
||||||
class LoadSGFPopup(BoxLayout):
|
class LoadSGFPopup(BoxLayout):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ConfigTeacherPopup(QuickConfigGui):
|
|
||||||
def __init__(self, katrain, popup, **kwargs):
|
|
||||||
self.settings = katrain.config("trainer")
|
|
||||||
self.sgf_settings = katrain.config("sgf")
|
|
||||||
self.ui_settings = katrain.config("board_ui")
|
|
||||||
super().__init__(katrain, popup, self.settings, **kwargs)
|
|
||||||
self.spacing = 2
|
|
||||||
Clock.schedule_once(self.build, 0)
|
|
||||||
|
|
||||||
def build(self, _dt):
|
|
||||||
thresholds = self.settings["eval_thresholds"]
|
|
||||||
undos = self.settings["num_undo_prompts"]
|
|
||||||
colors = self.ui_settings["eval_colors"]
|
|
||||||
thrbox = GridLayout(spacing=1, padding=2, cols=5, rows=len(thresholds) + 1)
|
|
||||||
thrbox.add_widget(ScaledLightLabel(text="Point loss greater than", bold=True))
|
|
||||||
thrbox.add_widget(ScaledLightLabel(text="Gives this many undos", bold=True))
|
|
||||||
thrbox.add_widget(ScaledLightLabel(text="Color (fixed)", bold=True))
|
|
||||||
thrbox.add_widget(ScaledLightLabel(text="Show dots", bold=True))
|
|
||||||
thrbox.add_widget(ScaledLightLabel(text="Save in SGF", bold=True))
|
|
||||||
for i, (thr, undos, color) in enumerate(zip(thresholds, undos, colors)):
|
|
||||||
thrbox.add_widget(LabelledFloatInput(text=str(thr), input_property=f"eval_thresholds::{i}"))
|
|
||||||
thrbox.add_widget(LabelledFloatInput(text=str(undos), input_property=f"num_undo_prompts::{i}"))
|
|
||||||
thrbox.add_widget(BackgroundMixin(background_color=color[:3]))
|
|
||||||
thrbox.add_widget(LabelledCheckBox(text=str(color[3] == 1), input_property=f"alpha::{i}"))
|
|
||||||
thrbox.add_widget(LabelledCheckBox(size_hint=(0.5, 1), text=str(self.sgf_settings["save_feedback"][i]), input_property=f"save_feedback::{i}"))
|
|
||||||
self.add_widget(thrbox)
|
|
||||||
|
|
||||||
xsettings = BoxLayout(size_hint=(1, 0.15), spacing=2)
|
|
||||||
xsettings.add_widget(ScaledLightLabel(text="Show last <n> dots"))
|
|
||||||
xsettings.add_widget(LabelledIntInput(size_hint=(0.5, 1), text=str(self.settings["eval_off_show_last"]), input_property="eval_off_show_last"))
|
|
||||||
self.add_widget(xsettings)
|
|
||||||
xsettings = BoxLayout(size_hint=(1, 0.15), spacing=2)
|
|
||||||
xsettings.add_widget(ScaledLightLabel(text="Show dots/SGF comments for AI players"))
|
|
||||||
xsettings.add_widget(LabelledCheckBox(size_hint=(0.5, 1), text=str(self.settings["eval_show_ai"]), input_property="eval_show_ai"))
|
|
||||||
self.add_widget(xsettings)
|
|
||||||
xsettings = BoxLayout(size_hint=(1, 0.15), spacing=2)
|
|
||||||
xsettings.add_widget(ScaledLightLabel(text="Disable analysis while in teach mode"))
|
|
||||||
xsettings.add_widget(LabelledCheckBox(size_hint=(0.5, 1), text=str(self.settings["lock_ai"]), input_property="lock_ai"))
|
|
||||||
self.add_widget(xsettings)
|
|
||||||
|
|
||||||
bl = BoxLayout(size_hint=(1, 0.15), spacing=2)
|
|
||||||
bl.add_widget(StyledButton(text=f"Apply", on_press=lambda _: self.update_config(False)))
|
|
||||||
self.info_label = Label()
|
|
||||||
bl.add_widget(self.info_label)
|
|
||||||
bl.add_widget(StyledButton(text=f"Apply and Save", on_press=lambda _: self.update_config(True)))
|
|
||||||
self.add_widget(bl)
|
|
||||||
|
|
||||||
def update_config(self, save_to_file=False):
|
|
||||||
try:
|
|
||||||
for k, v in self.collect_properties(self).items():
|
|
||||||
if "::" in k:
|
|
||||||
k1, i = k.split("::")
|
|
||||||
i = int(i)
|
|
||||||
if "alpha" in k1:
|
|
||||||
v = 1.0 if v else 0.0
|
|
||||||
if self.ui_settings["eval_colors"][i][3] != v:
|
|
||||||
self.katrain.log(f"Updating alpha {i} = {v}", OUTPUT_DEBUG)
|
|
||||||
self.ui_settings["eval_colors"][i][3] = v
|
|
||||||
elif "save_feedback" in k1:
|
|
||||||
if self.sgf_settings[k1][i] != v:
|
|
||||||
self.sgf_settings[k1][i] = v
|
|
||||||
self.katrain.log(f"Updating setting sgf/{k1}[{i}] = {v}", OUTPUT_DEBUG)
|
|
||||||
|
|
||||||
else:
|
|
||||||
if self.settings[k1][i] != v:
|
|
||||||
self.settings[k1][i] = v
|
|
||||||
self.katrain.log(f"Updating setting trainer/{k1}[{i}] = {v}", OUTPUT_DEBUG)
|
|
||||||
else:
|
|
||||||
if self.settings[k] != v:
|
|
||||||
self.settings[k] = v
|
|
||||||
self.katrain.log(f"Updating setting {k} = {v}", OUTPUT_DEBUG)
|
|
||||||
if save_to_file:
|
|
||||||
self.katrain.save_config()
|
|
||||||
self.popup.dismiss()
|
|
||||||
except InputParseError as e:
|
|
||||||
self.info_label.text = str(e)
|
|
||||||
self.katrain.log(e, OUTPUT_ERROR)
|
|
||||||
return
|
|
||||||
self.katrain.update_state()
|
|
||||||
self.popup.dismiss()
|
|
||||||
Binary file not shown.
@@ -20,7 +20,7 @@ msgid "menu:load"
|
|||||||
msgstr "Load Game"
|
msgstr "Load Game"
|
||||||
|
|
||||||
msgid "menu:settings"
|
msgid "menu:settings"
|
||||||
msgstr "General Settings"
|
msgstr "General & Engine Settings"
|
||||||
|
|
||||||
msgid "menu:clocksettings"
|
msgid "menu:clocksettings"
|
||||||
msgstr "Timer Settings"
|
msgstr "Timer Settings"
|
||||||
@@ -278,6 +278,46 @@ msgstr "Note that handicaps above 9 are not supported on non-square boards."
|
|||||||
msgid "new game"
|
msgid "new game"
|
||||||
msgstr "Start New Game"
|
msgstr "Start New Game"
|
||||||
|
|
||||||
|
|
||||||
|
# teacher settings from here
|
||||||
|
msgstr "teacher settings"
|
||||||
|
msgid "Edit Teacher Settings"
|
||||||
|
|
||||||
|
msgid "point loss threshold"
|
||||||
|
msgstr "Point loss greater than"
|
||||||
|
|
||||||
|
msgid "num undos"
|
||||||
|
msgstr "Number of undos"
|
||||||
|
|
||||||
|
msgid "num undos"
|
||||||
|
msgstr "Number of undos"
|
||||||
|
|
||||||
|
msgid "dot color"
|
||||||
|
msgstr "Colour (fixed)"
|
||||||
|
|
||||||
|
msgid "show dots"
|
||||||
|
msgstr "Show dots"
|
||||||
|
|
||||||
|
msgid "save dots"
|
||||||
|
msgstr "Save in SGF"
|
||||||
|
|
||||||
|
msgid "show last n dots"
|
||||||
|
msgstr "Show dots for the last <this many> moves"
|
||||||
|
|
||||||
|
msgid "show ai dots"
|
||||||
|
msgstr "Show dots/SGF comments for AI players"
|
||||||
|
|
||||||
|
msgid "lock ai when teaching"
|
||||||
|
msgstr "Disable analysis while in teach mode"
|
||||||
|
|
||||||
|
#. submit button
|
||||||
|
msgid "update teacher"
|
||||||
|
msgstr "Update Teaching Game Settings"
|
||||||
|
|
||||||
|
#main config settings
|
||||||
|
msgid "restarting engine"
|
||||||
|
msgstr "Restarting Engine after Settings Change."
|
||||||
|
|
||||||
#. misc errors and info messages
|
#. misc errors and info messages
|
||||||
msgid "sgf written"
|
msgid "sgf written"
|
||||||
msgstr "SGF with analysis written to {file_name}"
|
msgstr "SGF with analysis written to {file_name}"
|
||||||
|
|||||||
+146
-1
@@ -43,7 +43,7 @@
|
|||||||
font_size: DESC_FONT_SIZE
|
font_size: DESC_FONT_SIZE
|
||||||
|
|
||||||
<I18NPopup>:
|
<I18NPopup>:
|
||||||
background_color: [*[c*255/66 for c in BACKGROUND_COLOR[:3]], 1]
|
#background_color: [*[c*255/66 for c in BACKGROUND_COLOR[:3]], 1]
|
||||||
title: i18n._(self.title_key)
|
title: i18n._(self.title_key)
|
||||||
title_size: DESC_FONT_SIZE
|
title_size: DESC_FONT_SIZE
|
||||||
title_font: self.font_name
|
title_font: self.font_name
|
||||||
@@ -65,6 +65,144 @@
|
|||||||
<SmallDescriptionLabel@DescriptionLabel>
|
<SmallDescriptionLabel@DescriptionLabel>
|
||||||
font_size: DESC_FONT_SIZE * 0.66
|
font_size: DESC_FONT_SIZE * 0.66
|
||||||
|
|
||||||
|
<ConfigPopup>:
|
||||||
|
BoxLayout:
|
||||||
|
orientation: 'horizontal'
|
||||||
|
GridLayout:
|
||||||
|
cols: 2
|
||||||
|
rows: 9
|
||||||
|
spacing: CP_SPACING
|
||||||
|
Label:
|
||||||
|
Label:
|
||||||
|
text: 'engine'
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("general:sgf_load")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledTextInput:
|
||||||
|
input_property: "general/sgf_load" # just last load dir?
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("general:sgf_save")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledTextInput:
|
||||||
|
input_property: "general/sgf_save"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("general:anim_pv_time")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledFloatInput:
|
||||||
|
input_property: "general/anim_pv_time"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("general:debug_level")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledIntInput:
|
||||||
|
input_property: "general/debug_level"
|
||||||
|
BackgroundMixing:
|
||||||
|
background_color: LIGHTGREY
|
||||||
|
size_hint: None, 1
|
||||||
|
width: 2
|
||||||
|
GridLayout:
|
||||||
|
cols: 2
|
||||||
|
rows: 9
|
||||||
|
spacing: CP_SPACING
|
||||||
|
Label:
|
||||||
|
Label:
|
||||||
|
text: 'engine'
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("engine:katago")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledTextInput:
|
||||||
|
input_property: "engine/katago"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("engine:model")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledTextInput:
|
||||||
|
input_property: "engine/model"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("engine:config")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledTextInput:
|
||||||
|
input_property: "engine/config"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("engine:max_visits")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledIntInput:
|
||||||
|
input_property: "engine/max_visits"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("engine:max_time")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledFloatInput:
|
||||||
|
input_property: "engine/fast_visits"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("engine:fast_visits")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledFloatInput:
|
||||||
|
input_property: "engine/max_time"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("engine:threads")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledIntInput:
|
||||||
|
input_property: "engine/threads"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("engine:wide_root_noise")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledFloatInput:
|
||||||
|
input_property: "engine/wide_root_noise"
|
||||||
|
AnchorLayout:
|
||||||
|
size_hint: 1,1
|
||||||
|
AutoSizedRoundedRectangleButton:
|
||||||
|
padding_x: 15
|
||||||
|
size_hint: None,0.5
|
||||||
|
text: i18n._("update settings")
|
||||||
|
on_press: root.update_config(True)
|
||||||
|
|
||||||
|
<ConfigTeacherPopup>
|
||||||
|
options_grid: options_grid
|
||||||
|
GridLayout:
|
||||||
|
cols: 5
|
||||||
|
rows: 7
|
||||||
|
size_hint: 1, 7
|
||||||
|
id: options_grid
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("dot color")
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("point loss threshold")
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("num undos")
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("show dots")
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("save dots")
|
||||||
|
GridLayout:
|
||||||
|
cols: 2
|
||||||
|
rows: 3
|
||||||
|
size_hint: 1, 3
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("show last n dots")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledIntInput:
|
||||||
|
input_property: "trainer/eval_off_show_last"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("show ai dots")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledIntInput:
|
||||||
|
input_property: "trainer/eval_off_show_last"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("show last n dots")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledIntInput:
|
||||||
|
input_property: "trainer/eval_show_ai"
|
||||||
|
DescriptionLabel:
|
||||||
|
text: i18n._("lock ai when teaching")
|
||||||
|
AnchorLayout:
|
||||||
|
LabelledCheckBox:
|
||||||
|
input_property: "trainer/lock_ai"
|
||||||
|
AnchorLayout:
|
||||||
|
size_hint: 1,1
|
||||||
|
AutoSizedRoundedRectangleButton:
|
||||||
|
padding_x: 15
|
||||||
|
size_hint: None,0.5
|
||||||
|
text: i18n._("update teacher")
|
||||||
|
on_press: root.update_config(True)
|
||||||
|
|
||||||
|
|
||||||
<ConfigTimerPopup>:
|
<ConfigTimerPopup>:
|
||||||
MDGridLayout:
|
MDGridLayout:
|
||||||
@@ -103,6 +241,10 @@
|
|||||||
<NewGamePopup>:
|
<NewGamePopup>:
|
||||||
restart: restart
|
restart: restart
|
||||||
rules_spinner: rules_spinner
|
rules_spinner: rules_spinner
|
||||||
|
player_setup: player_setup
|
||||||
|
PlayerSetupBlock:
|
||||||
|
size_hint: 1, 2
|
||||||
|
id: player_setup
|
||||||
GridLayout:
|
GridLayout:
|
||||||
rows: 6
|
rows: 6
|
||||||
cols: 3
|
cols: 3
|
||||||
@@ -199,6 +341,9 @@
|
|||||||
on_press: root.update_config(True)
|
on_press: root.update_config(True)
|
||||||
|
|
||||||
|
|
||||||
|
<NewLoadSGFPopup>:
|
||||||
|
FileBrowser:
|
||||||
|
|
||||||
# old
|
# old
|
||||||
|
|
||||||
<LoadSGFPopup>:
|
<LoadSGFPopup>:
|
||||||
|
|||||||
Reference in new issue
Block a user