This commit is contained in:
Sander Land committed 2020-12-11 20:34:38 +01:00
1 parent 3d43bf660f
commit bfa673d4b6
22 files changed
+477 -404

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
[flake8]
ignore = E501, E203, W503, E402 # line length, space before binary op, line break before binary op, import not at top
exclude = .git,__pycache__,build,dist
+39 -15
View File
@@ -1,7 +1,11 @@
"""isort:skip_file""" """isort:skip_file"""
# first, logging level lower # first, logging level lower
import json
import os import os
from kivy.core.window import Window
from kivy.uix.widget import Widget
os.environ["KCFG_KIVY_LOG_LEVEL"] = os.environ.get("KCFG_KIVY_LOG_LEVEL", "warning") os.environ["KCFG_KIVY_LOG_LEVEL"] = os.environ.get("KCFG_KIVY_LOG_LEVEL", "warning")
if "KIVY_AUDIO" not in os.environ: if "KIVY_AUDIO" not in os.environ:
os.environ["KIVY_AUDIO"] = "sdl2" # some backends hard crash / this seems to be most stable os.environ["KIVY_AUDIO"] = "sdl2" # some backends hard crash / this seems to be most stable
@@ -46,9 +50,11 @@ from kivy.lang import Builder
from kivy.resources import resource_add_path from kivy.resources import resource_add_path
from kivy.uix.popup import Popup from kivy.uix.popup import Popup
from kivy.uix.screenmanager import Screen from kivy.uix.screenmanager import Screen
from katrain.core.ai import generate_ai_move from kivy.resources import resource_find
from kivy.core.window import Window from kivy.properties import NumericProperty, ObjectProperty, StringProperty
from kivy.clock import Clock
from kivy.metrics import dp from kivy.metrics import dp
from katrain.core.ai import generate_ai_move
from katrain.core.lang import DEFAULT_LANGUAGE, i18n from katrain.core.lang import DEFAULT_LANGUAGE, i18n
from katrain.core.constants import ( from katrain.core.constants import (
@@ -57,7 +63,6 @@ from katrain.core.constants import (
OUTPUT_INFO, OUTPUT_INFO,
OUTPUT_DEBUG, OUTPUT_DEBUG,
OUTPUT_EXTRA_DEBUG, OUTPUT_EXTRA_DEBUG,
MODE_PLAY,
MODE_ANALYZE, MODE_ANALYZE,
HOMEPAGE, HOMEPAGE,
VERSION, VERSION,
@@ -66,18 +71,23 @@ from katrain.core.constants import (
PLAYING_NORMAL, PLAYING_NORMAL,
PLAYER_HUMAN, PLAYER_HUMAN,
SGF_INTERNAL_COMMENTS_MARKER, SGF_INTERNAL_COMMENTS_MARKER,
MODE_PLAY,
DATA_FOLDER,
) )
from katrain.gui.popups import ConfigTeacherPopup, ConfigTimerPopup, I18NPopup, SaveSGFPopup from katrain.gui.popups import ConfigTeacherPopup, ConfigTimerPopup, I18NPopup, SaveSGFPopup
from katrain.core.base_katrain import KaTrainBase from katrain.core.base_katrain import KaTrainBase
from katrain.core.engine import KataGoEngine from katrain.core.engine import KataGoEngine
from katrain.core.game import Game, IllegalMoveException, KaTrainSGF from katrain.core.game import Game, IllegalMoveException, KaTrainSGF
from katrain.core.sgf_parser import Move, ParseError from katrain.core.sgf_parser import Move, ParseError
from katrain.gui.kivyutils import *
from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, ConfigAIPopup from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, ConfigAIPopup
from katrain.gui.style import ENGINE_BUSY_COL, ENGINE_DOWN_COL, ENGINE_READY_COL from katrain.gui.theme import Theme
from katrain.gui.widgets import * from kivymd.app import MDApp
from katrain.gui.badukpan import AnalysisControls, BadukPanControls, BadukPanWidget
from katrain.gui.controlspanel import ControlsPanel # used in kv
from katrain.gui.kivyutils import *
from katrain.gui.widgets import MoveTree, I18NFileBrowser, SelectionSlider, ScoreGraph # noqa F401
from katrain.gui.badukpan import AnalysisControls, BadukPanControls, BadukPanWidget # noqa F401
from katrain.gui.controlspanel import ControlsPanel # noqa F401
class KaTrainGui(Screen, KaTrainBase): class KaTrainGui(Screen, KaTrainBase):
@@ -165,11 +175,11 @@ class KaTrainGui(Screen, KaTrainBase):
# update engine status dot # update engine status dot
if not self.engine or not self.engine.katago_process or self.engine.katago_process.poll() is not None: if not self.engine or not self.engine.katago_process or self.engine.katago_process.poll() is not None:
self.board_controls.engine_status_col = ENGINE_DOWN_COL self.board_controls.engine_status_col = Theme.ENGINE_DOWN_COLOR
elif len(self.engine.queries) == 0: elif len(self.engine.queries) == 0:
self.board_controls.engine_status_col = ENGINE_READY_COL self.board_controls.engine_status_col = Theme.ENGINE_READY_COLOR
else: else:
self.board_controls.engine_status_col = ENGINE_BUSY_COL self.board_controls.engine_status_col = Theme.ENGINE_BUSY_COLOR
self.board_controls.queries_remaining = len(self.engine.queries) self.board_controls.queries_remaining = len(self.engine.queries)
# redraw board/stones # redraw board/stones
@@ -590,7 +600,8 @@ class KaTrainGui(Screen, KaTrainBase):
yappi.start() yappi.start()
self.log("starting profiler", OUTPUT_ERROR) self.log("starting profiler", OUTPUT_ERROR)
elif keycode[1] == "f10" and self.debug_level >= OUTPUT_EXTRA_DEBUG: elif keycode[1] == "f10" and self.debug_level >= OUTPUT_EXTRA_DEBUG:
import yappi, time import time
import yappi
stats = yappi.get_func_stats() stats = yappi.get_func_stats()
filename = f"callgrind.{int(time.time())}.prof" filename = f"callgrind.{int(time.time())}.prof"
@@ -607,7 +618,6 @@ class KaTrainApp(MDApp):
super().__init__() super().__init__()
def build(self): def build(self):
global DEFAULT_FONT
self.icon = ICON # how you're supposed to set an icon self.icon = ICON # how you're supposed to set an icon
self.title = f"KaTrain v{VERSION}" self.title = f"KaTrain v{VERSION}"
@@ -617,8 +627,22 @@ class KaTrainApp(MDApp):
kv_file = find_package_resource("katrain/gui.kv") kv_file = find_package_resource("katrain/gui.kv")
popup_kv_file = find_package_resource("katrain/popups.kv") popup_kv_file = find_package_resource("katrain/popups.kv")
resource_add_path(PATHS["PACKAGE"]) resource_add_path(PATHS["PACKAGE"] + "/sounds")
DEFAULT_FONT = resource_find(DEFAULT_FONT) resource_add_path(PATHS["PACKAGE"] + "/img")
resource_add_path(os.path.abspath(os.path.expanduser(DATA_FOLDER))) # prefer resources in .katrain
theme_file = resource_find("theme.json")
if theme_file:
try:
with open(theme_file) as f:
theme_overrides = json.load(f)
for k, v in theme_overrides.items():
setattr(Theme, k, v)
print(f"[{theme_file}] Found theme override {k} = {v}")
except Exception as e: # noqa E722
print(f"Failed to load theme file {theme_file}: {e}")
Theme.DEFAULT_FONT = resource_find(Theme.DEFAULT_FONT)
Builder.load_file(kv_file) Builder.load_file(kv_file)
Window.bind(on_request_close=self.on_request_close) Window.bind(on_request_close=self.on_request_close)
+3 -3
View File
@@ -125,11 +125,11 @@ def policy_weighted_move(policy_moves, lower_bound, weaken_fac):
def generate_influence_territory_weights(ai_mode, ai_settings, policy_grid, size): def generate_influence_territory_weights(ai_mode, ai_settings, policy_grid, size):
thr_line = ai_settings["threshold"] - 1 # zero-based thr_line = ai_settings["threshold"] - 1 # zero-based
if ai_mode == AI_INFLUENCE: if ai_mode == AI_INFLUENCE:
weight = lambda x, y: (1 / ai_settings["line_weight"]) ** ( weight = lambda x, y: (1 / ai_settings["line_weight"]) ** ( # noqa E731
max(0, thr_line - min(size[0] - 1 - x, x)) + max(0, thr_line - min(size[1] - 1 - y, y)) max(0, thr_line - min(size[0] - 1 - x, x)) + max(0, thr_line - min(size[1] - 1 - y, y))
) ) # noqa E731
else: else:
weight = lambda x, y: (1 / ai_settings["line_weight"]) ** ( weight = lambda x, y: (1 / ai_settings["line_weight"]) ** ( # noqa E731
max(0, min(size[0] - 1 - x, x, size[1] - 1 - y, y) - thr_line) max(0, min(size[0] - 1 - x, x, size[1] - 1 - y, y) - thr_line)
) )
weighted_coords = [ weighted_coords = [
+14 -3
View File
@@ -6,7 +6,18 @@ from kivy import Config
from kivy.storage.jsonstore import JsonStore from kivy.storage.jsonstore import JsonStore
from katrain.core.ai import ai_rank_estimation from katrain.core.ai import ai_rank_estimation
from katrain.core.constants import * from katrain.core.constants import (
PLAYER_HUMAN,
PLAYER_AI,
PLAYING_NORMAL,
PLAYING_TEACHING,
OUTPUT_INFO,
OUTPUT_ERROR,
OUTPUT_DEBUG,
AI_DEFAULT,
CONFIG_MIN_VERSION,
DATA_FOLDER,
)
from katrain.core.utils import find_package_resource from katrain.core.utils import find_package_resource
@@ -44,7 +55,7 @@ class Player:
class KaTrainBase: class KaTrainBase:
USER_CONFIG_FILE = os.path.expanduser("~/.katrain/config.json") USER_CONFIG_FILE = os.path.expanduser(os.path.join(DATA_FOLDER, "config.json"))
PACKAGE_CONFIG_FILE = "katrain/config.json" PACKAGE_CONFIG_FILE = "katrain/config.json"
"""Settings, logging, and players functionality, so other classes like bots who need a katrain instance can be used without a GUI""" """Settings, logging, and players functionality, so other classes like bots who need a katrain instance can be used without a GUI"""
@@ -91,7 +102,7 @@ class KaTrainBase:
else: # user file exists else: # user file exists
try: try:
version = JsonStore(user_config_file).get("general")["version"] version = JsonStore(user_config_file).get("general")["version"]
except Exception: # broken file etc except Exception: # noqa E722 broken file etc
version = "0.0.0" version = "0.0.0"
if version < CONFIG_MIN_VERSION: if version < CONFIG_MIN_VERSION:
backup = user_config_file + f".{version}.backup" backup = user_config_file + f".{version}.backup"
+1
View File
@@ -3,6 +3,7 @@ VERSION = "1.7.0"
HOMEPAGE = "https://github.com/sanderland/katrain" HOMEPAGE = "https://github.com/sanderland/katrain"
CONFIG_MIN_VERSION = "1.7.0" # keep config files from this version CONFIG_MIN_VERSION = "1.7.0" # keep config files from this version
ANALYSIS_FORMAT_VERSION = "1.0" ANALYSIS_FORMAT_VERSION = "1.0"
DATA_FOLDER = "~/.katrain"
OUTPUT_ERROR = -1 OUTPUT_ERROR = -1
OUTPUT_KATAGO_STDERR = -0.5 OUTPUT_KATAGO_STDERR = -0.5
+1 -1
View File
@@ -215,7 +215,7 @@ class Game:
if not copy_from_node.children: if not copy_from_node.children:
break break
copy_from_node = copy_from_node.ordered_children[0] copy_from_node = copy_from_node.ordered_children[0]
except: except IllegalMoveException:
pass # illegal move = stop pass # illegal move = stop
self._calculate_groups() # recalculate groups self._calculate_groups() # recalculate groups
self.katrain.controls.set_status( self.katrain.controls.set_status(
+2 -2
View File
@@ -15,7 +15,7 @@ from katrain.core.constants import (
from katrain.core.lang import i18n from katrain.core.lang import i18n
from katrain.core.sgf_parser import Move, SGFNode from katrain.core.sgf_parser import Move, SGFNode
from katrain.core.utils import evaluation_class, pack_floats, unpack_floats, var_to_grid from katrain.core.utils import evaluation_class, pack_floats, unpack_floats, var_to_grid
from katrain.gui.style import INFO_PV_COLOR from katrain.gui.theme import Theme
def analysis_dumps(analysis): def analysis_dumps(analysis):
@@ -287,7 +287,7 @@ class GameNode(SGFNode):
def make_pv(self, player, pv, interactive): def make_pv(self, player, pv, interactive):
pvtext = f"{player}{' '.join(pv)}" pvtext = f"{player}{' '.join(pv)}"
if interactive: if interactive:
pvtext = f"[u][ref={pvtext}][color={INFO_PV_COLOR}]{pvtext}[/color][/ref][/u]" pvtext = f"[u][ref={pvtext}][color={Theme.INFO_PV_COLOR}]{pvtext}[/color][/ref][/u]"
return pvtext return pvtext
def comment(self, sgf=False, teach=False, details=False, interactive=True): def comment(self, sgf=False, teach=False, details=False, interactive=True):
+2 -2
View File
@@ -5,7 +5,7 @@ import sys
from kivy._event import Observable from kivy._event import Observable
from katrain.core.utils import find_package_resource from katrain.core.utils import find_package_resource
from katrain.gui.style import DEFAULT_FONT from katrain.gui.theme import Theme
class Lang(Observable): class Lang(Observable):
@@ -53,7 +53,7 @@ class Lang(Observable):
return return
# get the right locales directory, and instantiate a gettext # get the right locales directory, and instantiate a gettext
self.lang = lang self.lang = lang
self.font_name = self.FONTS.get(lang) or DEFAULT_FONT self.font_name = self.FONTS.get(lang) or Theme.DEFAULT_FONT
i18n_dir, _ = os.path.split(find_package_resource("katrain/i18n/__init__.py")) i18n_dir, _ = os.path.split(find_package_resource("katrain/i18n/__init__.py"))
locale_dir = os.path.join(i18n_dir, "locales") locale_dir = os.path.join(i18n_dir, "locales")
locales = gettext.translation("katrain", locale_dir, languages=[lang, DEFAULT_LANGUAGE]) locales = gettext.translation("katrain", locale_dir, languages=[lang, DEFAULT_LANGUAGE])
+6 -6
View File
@@ -439,7 +439,7 @@ class SGF:
re = "W+" re = "W+"
elif "lack win" in lines[10]: elif "lack win" in lines[10]:
re = "B+" re = "B+"
except: except IndexError:
pass pass
if handicap < 0 or handicap > 9: if handicap < 0 or handicap > 9:
@@ -527,7 +527,7 @@ class SGF:
try: try:
grlt = int(re.search(grlt_regex, line).group(1)) grlt = int(re.search(grlt_regex, line).group(1))
zipsu = int(re.search(zipsu_regex, line).group(1)) zipsu = int(re.search(zipsu_regex, line).group(1))
except: except: # noqa E722
return "" return ""
return gib_make_result(grlt, zipsu) return gib_make_result(grlt, zipsu)
@@ -561,7 +561,7 @@ class SGF:
komi = int(re.search(r"GONGJE:(\d+),", line).group(1)) / 10 komi = int(re.search(r"GONGJE:(\d+),", line).group(1)) / 10
if komi: if komi:
root.set_property("KM", komi) root.set_property("KM", komi)
except: except: # noqa E722
pass pass
if line.startswith("\\[GAMETAG="): if line.startswith("\\[GAMETAG="):
@@ -570,7 +570,7 @@ class SGF:
match = re.search(r"C(\d\d\d\d):(\d\d):(\d\d)", line) match = re.search(r"C(\d\d\d\d):(\d\d):(\d\d)", line)
date = "{}-{}-{}".format(match.group(1), match.group(2), match.group(3)) date = "{}-{}-{}".format(match.group(1), match.group(2), match.group(3))
root.set_property("DT", date) root.set_property("DT", date)
except: except: # noqa E722
pass pass
if "RE" not in root.properties: if "RE" not in root.properties:
@@ -583,7 +583,7 @@ class SGF:
komi = int(re.search(r",G(\d+),", line).group(1)) / 10 komi = int(re.search(r",G(\d+),", line).group(1)) / 10
if komi: if komi:
root.set_property("KM", komi) root.set_property("KM", komi)
except: except: # noqa E722
pass pass
if line[0:3] == "INI": if line[0:3] == "INI":
@@ -592,7 +592,7 @@ class SGF:
setup = line.split() setup = line.split()
try: try:
handicap = int(setup[3]) handicap = int(setup[3])
except IndexError: except ParseError:
continue continue
if handicap < 0 or handicap > 9: if handicap < 0 or handicap > 9:
+100 -122
View File
@@ -10,30 +10,14 @@
#:import MODE_ANALYZE katrain.core.constants.MODE_ANALYZE #:import MODE_ANALYZE katrain.core.constants.MODE_ANALYZE
#:import STATUS_INFO katrain.core.constants.STATUS_INFO #:import STATUS_INFO katrain.core.constants.STATUS_INFO
#:import Theme katrain.gui.theme.Theme
#:import BLACK katrain.gui.theme.BLACK
#:import LIGHTER_GREY katrain.gui.theme.LIGHTER_GREY
#:import WHITE katrain.gui.style.WHITE
#:import BLACK katrain.gui.style.BLACK
#:import LIGHTGREY katrain.gui.style.LIGHTGREY
#:import LIGHTERGREY katrain.gui.style.LIGHTERGREY
#:import GREEN katrain.gui.style.GREEN
#:import YELLOW katrain.gui.style.YELLOW
#:import RED katrain.gui.style.RED
#:import DARKRED katrain.gui.style.DARKRED
#:import ORANGE katrain.gui.style.ORANGE
#:import BACKGROUND_COLOR katrain.gui.style.BACKGROUND_COLOR #:set CP_SPACING Theme.CP_SPACING
#:import BOX_BACKGROUND_COLOR katrain.gui.style.BOX_BACKGROUND_COLOR #:set CP_SMALL_SPACING Theme.CP_SMALL_SPACING
#:import LIGHTER_BACKGROUND_COLOR katrain.gui.style.LIGHTER_BACKGROUND_COLOR #:set CP_PADDING Theme.CP_PADDING
#:import TEXT_COLOR katrain.gui.style.TEXT_COLOR
#:import SCORE_COLOR katrain.gui.style.SCORE_COLOR
#:import WINRATE_COLOR katrain.gui.style.WINRATE_COLOR
#:import POINTLOSS_COLOR katrain.gui.style.POINTLOSS_COLOR
#:set BUTTON_INACTIVE_COLOR LIGHTGREY
#:set CP_SPACING dp(6)
#:set CP_SMALL_SPACING dp(3)
#:set CP_PADDING dp(6)
#:set NOTES_FONT_SIZE sp(18)
#:set EPSILON 1e-9 #:set EPSILON 1e-9
@@ -68,8 +52,8 @@
<SizedButton>: <SizedButton>:
ripple_duration_in_slow: 0.6 ripple_duration_in_slow: 0.6
text_color: TEXT_COLOR text_color: Theme.TEXT_COLOR
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
label: label label: label
text_size: root.size text_size: root.size
Label: Label:
@@ -85,27 +69,27 @@
width: root.label.texture_size[0] width: root.label.texture_size[0]
<ToggleButtonMixin>: <ToggleButtonMixin>:
inactive_background_color: BACKGROUND_COLOR inactive_background_color: Theme.BACKGROUND_COLOR
active_background_color: BOX_BACKGROUND_COLOR active_background_color: Theme.BOX_BACKGROUND_COLOR
background_color: self.active_background_color if self.state=='down' else self.inactive_background_color background_color: self.active_background_color if self.state=='down' else self.inactive_background_color
outline_color: self.active_outline_color if self.state=='down' else self.inactive_outline_color outline_color: self.active_outline_color if self.state=='down' else self.inactive_outline_color
<SizedRectangleButton>: <SizedRectangleButton>:
outline_color: WHITE outline_color: Theme.BUTTON_BORDER_COLOR
text_color: self.outline_color text_color: self.outline_color
<AutoSizedRectangleButton>: <AutoSizedRectangleButton>:
outline_color: WHITE outline_color: Theme.BUTTON_BORDER_COLOR
text_color: self.outline_color text_color: self.outline_color
<SizedRectangleToggleButton>: <SizedRectangleToggleButton>:
inactive_outline_color: BUTTON_INACTIVE_COLOR inactive_outline_color: Theme.BUTTON_INACTIVE_COLOR
active_outline_color: WHITE active_outline_color: Theme.BUTTON_BORDER_COLOR
text_color: self.outline_color text_color: self.outline_color
<AutoSizedRectangleToggleButton>: <AutoSizedRectangleToggleButton>:
inactive_outline_color: BUTTON_INACTIVE_COLOR inactive_outline_color: Theme.BUTTON_INACTIVE_COLOR
active_outline_color: WHITE active_outline_color: Theme.BUTTON_BORDER_COLOR
text_color: self.outline_color text_color: self.outline_color
<SizedRoundedRectangleButton@SizedRectangleButton>: <SizedRoundedRectangleButton@SizedRectangleButton>:
@@ -115,7 +99,7 @@
background_radius: self.height/3.5 background_radius: self.height/3.5
<BGBoxLayout>: <BGBoxLayout>:
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
<TransparentIconButton>: <TransparentIconButton>:
background_normal: '' background_normal: ''
@@ -131,20 +115,20 @@
<I18NSpinnerOption@SpinnerOption>: <I18NSpinnerOption@SpinnerOption>:
background_color: LIGHTER_BACKGROUND_COLOR background_color: Theme.LIGHTER_BACKGROUND_COLOR
background_normal: '' background_normal: ''
lang_change_tracking: i18n._('') # for font lang_change_tracking: i18n._('') # for font
color: WHITE color: Theme.INPUT_FONT_COLOR
canvas.after: canvas.after:
Color: Color:
rgba: LIGHTGREY rgba: LIGHT_GREY
Line Line
points: self.x,self.y,self.x+self.width,self.y points: self.x,self.y,self.x+self.width,self.y
width: 1 width: 1
<Spinner>: <Spinner>:
-font_size: self.height * 0.5 -font_size: self.height * 0.5
background_color: [*[c*255/88 for c in BACKGROUND_COLOR[:3]], 1] # compensate for texture background_color: [*[c*255/88 for c in Theme.BACKGROUND_COLOR[:3]], 1] # compensate for texture
option_cls: 'I18NSpinnerOption' option_cls: 'I18NSpinnerOption'
<Button>: <Button>:
@@ -159,7 +143,7 @@
id: image id: image
pos: (root.pos[0] + (root.width - root.min_size)/2,root.pos[1] + (root.height - root.min_size)/2) pos: (root.pos[0] + (root.width - root.min_size)/2,root.pos[1] + (root.height - root.min_size)/2)
size: (root.min_size,root.min_size) size: (root.min_size,root.min_size)
source: 'img/white.png' if root.player=='W' else 'img/black.png' source: 'white_circle.png' if root.player=='W' else 'black_circle.png'
mipmap: True mipmap: True
Label: Label:
id: lbl id: lbl
@@ -167,7 +151,7 @@
text_size: None, root.min_size text_size: None, root.min_size
bold: True bold: True
valign: 'center' valign: 'center'
color: BLACK if root.player=='W' else WHITE color: Theme.CIRCLE_TEXT_COLORS[root.player]
pos: (image.x, image.y) pos: (image.x, image.y)
size: image.size size: image.size
font_size: root.min_size * 1.2 / (1 + 1 * len(self.text)) font_size: root.min_size * 1.2 / (1 + 1 * len(self.text))
@@ -203,13 +187,13 @@
Label: Label:
size_hint: 0.075, 0.5 size_hint: 0.075, 0.5
NavIconButton: NavIconButton:
icon: 'img/Previous-End.png' icon: 'Previous-End.png'
on_press: root.katrain('undo',999) on_press: root.katrain('undo',999)
NavIconButton: NavIconButton:
icon: 'img/Previous-5.png' icon: 'Previous-5.png'
on_press: root.katrain('undo',10) on_press: root.katrain('undo',10)
NavIconButton: NavIconButton:
icon: 'img/Previous.png' icon: 'Previous.png'
on_press: root.katrain('undo') on_press: root.katrain('undo')
MDFloatLayout: MDFloatLayout:
size_hint: 0.05, 1 size_hint: 0.05, 1
@@ -227,16 +211,16 @@
player: 'W' player: 'W'
text: '' text: ''
NavIconButton: NavIconButton:
icon: 'img/Next.png' icon: 'Next.png'
on_press: root.katrain('redo') on_press: root.katrain('redo')
NavIconButton: NavIconButton:
icon: 'img/Next-5.png' icon: 'Next-5.png'
on_press: root.katrain('redo',10) on_press: root.katrain('redo',10)
NavIconButton: NavIconButton:
icon: 'img/Next-End.png' icon: 'Next-End.png'
on_press: root.katrain('redo',999) on_press: root.katrain('redo',999)
NavIconButton: NavIconButton:
icon: 'img/Next-Mistake.png' icon: 'Next-Mistake.png'
color: [0.79,0.06,0.06,1] color: [0.79,0.06,0.06,1]
on_press: root.katrain('next-mistake') on_press: root.katrain('next-mistake')
ClickableLabel: ClickableLabel:
@@ -245,7 +229,7 @@
width: self.height width: self.height
canvas.before: canvas.before:
Color: Color:
rgba: YELLOW rgba: Theme.ENGINE_PONDERING_COLOR
Ellipse: Ellipse:
pos: self.pos[0] + self.width/2 - self.width/6, self.pos[1] + self.height/2 - self.width/6 pos: self.pos[0] + self.width/2 - self.width/6, self.pos[1] + self.height/2 - self.width/6
size: self.width/3, self.width/3 size: self.width/3, self.width/3
@@ -268,7 +252,7 @@
<HamburgerButton@TransparentIconButton>: <HamburgerButton@TransparentIconButton>:
icon: 'img/hamburger.png' icon: 'hamburger.png'
width: self.height width: self.height
icon_size: [self.height * 0.5, self.height * 0.5] icon_size: [self.height * 0.5, self.height * 0.5]
@@ -279,8 +263,8 @@
player: 'B' player: 'B'
player_type: 'player:human' player_type: 'player:human'
player_subtype: 'game:normal' player_subtype: 'game:normal'
background_color: BOX_BACKGROUND_COLOR if self.active else BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR if self.active else Theme.BACKGROUND_COLOR
outline_color: BOX_BACKGROUND_COLOR outline_color: Theme.BOX_BACKGROUND_COLOR
outline_width: 2 outline_width: 2
padding: [2*CP_PADDING,CP_PADDING,CP_PADDING,CP_PADDING] padding: [2*CP_PADDING,CP_PADDING,CP_PADDING,CP_PADDING]
CircleWithText: CircleWithText:
@@ -314,9 +298,9 @@
height: self.width height: self.width
size_hint: 1, None size_hint: 1, None
min_size: min(self.height,self.width) min_size: min(self.height,self.width)
inactive_line_color: WHITE inactive_line_color: Theme.BUTTON_BORDER_COLOR
inactive_fill_color: [1,1,1,0] inactive_fill_color: [1,1,1,0]
active_line_color: ORANGE active_line_color: Theme.PAUSE_ACTIVE_COLOR
active_fill_color: [1,1,1,0] active_fill_color: [1,1,1,0]
line_width: max(1.1,self.min_size/20) line_width: max(1.1,self.min_size/20)
line_color: root.active_line_color if self.active else root.inactive_line_color line_color: root.active_line_color if self.active else root.inactive_line_color
@@ -345,10 +329,9 @@
cap: 'square' cap: 'square'
<TimerLabel@Label> <TimerLabel@Label>
timeout: False timeout: False
color: ORANGE if self.timeout else GREEN color: Theme.TIMER_TEXT_TIMEOUT_COLOR if self.timeout else Theme.TIMER_TEXT_COLOR
markup: True markup: True
max_lines: 1 max_lines: 1
font_name: 'fonts/digital-7 (mono).ttf' font_name: 'fonts/digital-7 (mono).ttf'
@@ -363,7 +346,7 @@
MDFloatLayout: MDFloatLayout:
size_hint: 0.4,1 size_hint: 0.4,1
TimerLabel: TimerLabel:
color: BACKGROUND_COLOR color: Theme.BACKGROUND_COLOR
text: '{}:88'.format('188' if root.state[0] >= 6000 else '88') text: '{}:88'.format('188' if root.state[0] >= 6000 else '88')
font_size: 0.85*self.height font_size: 0.85*self.height
pos_hint: {'right':1,'center_y':0.5} pos_hint: {'right':1,'center_y':0.5}
@@ -441,30 +424,30 @@
<StatsBox> <StatsBox>
orientation: 'vertical' orientation: 'vertical'
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
labels: {'score':score_label,'winrate':winrate_label,'points':pointloss_label} labels: {'score':score_label,'winrate':winrate_label,'points':pointloss_label}
StatsLabel: StatsLabel:
id: winrate_label id: winrate_label
label: i18n._('stats:winrate') label: i18n._('stats:winrate')
text: root.winrate text: root.winrate
color: WINRATE_COLOR color: Theme.WINRATE_COLOR
StatsLabel: StatsLabel:
id: score_label id: score_label
label: i18n._('stats:score') label: i18n._('stats:score')
text: root.score text: root.score
color: SCORE_COLOR color: Theme.SCORE_COLOR
StatsLabel: StatsLabel:
id: pointloss_label id: pointloss_label
label: i18n._('stats:pointslost') if root.points_lost is None or root.points_lost > 0 else i18n._('stats:pointsgained') label: i18n._('stats:pointslost') if root.points_lost is None or root.points_lost > 0 else i18n._('stats:pointsgained')
text: '{}: {:.1f}'.format(root.player,abs(root.points_lost)) if root.points_lost is not None else '...' text: '{}: {:.1f}'.format(root.player,abs(root.points_lost)) if root.points_lost is not None else '...'
color: POINTLOSS_COLOR color: Theme.POINTLOSS_COLOR
<ScrollableLabel>: <ScrollableLabel>:
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
do_scroll_x: False do_scroll_x: False
scroll_type: ['bars'] scroll_type: ['bars']
bar_width: 5 bar_width: 5
bar_color: LIGHTGREY bar_color: Theme.SCROLLBAR_COLOR
label: label label: label
canvas.before: canvas.before:
Color: Color:
@@ -477,9 +460,9 @@
lang_change_tracking: i18n._('') lang_change_tracking: i18n._('')
markup: root.markup markup: root.markup
padding: 5, 5 padding: 5, 5
font_size: NOTES_FONT_SIZE font_size: Theme.NOTES_FONT_SIZE
line_height: root.line_height line_height: root.line_height
color: TEXT_COLOR color: Theme.TEXT_COLOR
size_hint_y: None size_hint_y: None
height: self.texture_size[1] height: self.texture_size[1]
text_size: self.width, None text_size: self.width, None
@@ -497,7 +480,7 @@
<CollapsablePanelHeader>: <CollapsablePanelHeader>:
canvas.after: canvas.after:
Color: Color:
rgba: BOX_BACKGROUND_COLOR rgba: Theme.BOX_BACKGROUND_COLOR
Line: Line:
width: 1 width: 1
points: (self.x,self.y+1,self.x+self.width,self.y+1) points: (self.x,self.y+1,self.x+self.width,self.y+1)
@@ -567,7 +550,7 @@
options_height: root.tab_option_height options_height: root.tab_option_height
closed_label: 'closedlabel:scoregraph' closed_label: 'closedlabel:scoregraph'
options: ['score','winrate'] options: ['score','winrate']
option_colors: [SCORE_COLOR,WINRATE_COLOR] option_colors: [Theme.SCORE_COLOR,Theme.WINRATE_COLOR]
option_active: [True,False,False] option_active: [True,False,False]
on_option_state: on_option_state:
graph.show_graphs(args[1]) graph.show_graphs(args[1])
@@ -584,7 +567,7 @@
options_height: root.tab_option_height options_height: root.tab_option_height
closed_label: 'closedlabel:movestats' closed_label: 'closedlabel:movestats'
options: ['score','winrate','points'] options: ['score','winrate','points']
option_colors: [SCORE_COLOR,WINRATE_COLOR,POINTLOSS_COLOR] option_colors: [Theme.SCORE_COLOR,Theme.WINRATE_COLOR,Theme.POINTLOSS_COLOR]
option_active: [True,True,True] option_active: [True,True,True]
on_option_state: on_option_state:
for k, v in args[1].items(): stats.labels[k].hidden = not v for k, v in args[1].items(): stats.labels[k].hidden = not v
@@ -596,26 +579,26 @@
size_hint_y_open: 1 size_hint_y_open: 1
closed_label: 'closedlabel:info&notes' closed_label: 'closedlabel:info&notes'
options: ['info','info-details','notes'] options: ['info','info-details','notes']
option_colors: [WHITE,WHITE,YELLOW] option_colors: [Theme.INFO_TAB_FONT_COLOR,Theme.INFO_TAB_FONT_COLOR,Theme.NOTES_TAB_FONT_COLOR]
option_active: [True,False,False] option_active: [True,False,False]
on_option_state: on_option_state:
info.detailed = args[1]['info-details'] info.detailed = args[1]['info-details']
info.size_hint_y = int(args[1]['info'] or info.detailed)+1e-9 info.size_hint_y = int(args[1]['info'] or info.detailed)+EPSILON
info.opacity = int(args[1]['info'] or info.detailed) info.opacity = int(args[1]['info'] or info.detailed)
notebox.size_hint_y = int(args[1]['notes'])*0.66+1e-9 notebox.size_hint_y = int(args[1]['notes'])*0.66+EPSILON
notebox.opacity = int(args[1]['notes']) notebox.opacity = int(args[1]['notes'])
app.gui.update_state() app.gui.update_state()
options_height: root.tab_option_height options_height: root.tab_option_height
BGBoxLayout: BGBoxLayout:
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
orientation: 'vertical' orientation: 'vertical'
ScrollableLabel: ScrollableLabel:
id: status id: status
error: False error: False
size_hint: 1, None size_hint: 1, None
height: min(self.parent.height*0.66,self.label.texture_size[1]) height: min(self.parent.height*0.66,self.label.texture_size[1])
outline_color: RED if self.error else WHITE outline_color: Theme.ERROR_BORDER_COLOR if self.error else Theme.INFO_TAB_FONT_COLOR
background_color: LIGHTER_BACKGROUND_COLOR background_color: Theme.LIGHTER_BACKGROUND_COLOR
ScrollableLabel: ScrollableLabel:
id: info id: info
detailed: False detailed: False
@@ -630,28 +613,28 @@
size_hint: 1,0 size_hint: 1,0
LabelledTextInput: LabelledTextInput:
id: note id: note
font_size: NOTES_FONT_SIZE font_size: Theme.NOTES_FONT_SIZE
multiline: True multiline: True
on_text: root.katrain.set_note(self.text) on_text: root.katrain.set_note(self.text)
hint_text: i18n._('SGF Notes Hint') hint_text: i18n._('SGF Notes Hint')
color_mode: 'custom' color_mode: 'custom'
line_color_focus: TEXT_COLOR line_color_focus: Theme.TEXT_COLOR
size_hint: 1, 1 size_hint: 1, 1
canvas.before: canvas.before:
Color: Color:
rgba: LIGHTERGREY rgba: LIGHTER_GREY
Line: Line:
points: self.x, self.y+self.height, self.x+self.width, self.y+self.height points: self.x, self.y+self.height, self.x+self.width, self.y+self.height
width: 1 width: 1
Label: Label:
size_hint_y: 1e-9 # takes up remaining space if info is closed size_hint_y: EPSILON # takes up remaining space if info is closed
<MenuItem> <MenuItem>
font_size: self.height * 0.5 font_size: self.height * 0.5
size_hint_y: None size_hint_y: None
height: dp(50) height: dp(50)
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
width_margin: 1.33 width_margin: 1.33
content_width: icon.width + label.texture_size[0] * self.width_margin content_width: icon.width + label.texture_size[0] * self.width_margin
MDFloatLayout: MDFloatLayout:
@@ -674,7 +657,7 @@
size_hint: None, None size_hint: None, None
font_size: root.font_size font_size: root.font_size
font_name: root.font_name font_name: root.font_name
color: WHITE color: Theme.MENU_ITEM_FONT_COLOR
Label: Label:
text: root.shortcut text: root.shortcut
halign: 'left' halign: 'left'
@@ -682,7 +665,7 @@
size_hint_x: 1/3 size_hint_x: 1/3
font_size: root.font_size font_size: root.font_size
font_name: root.font_name font_name: root.font_name
color: LIGHTGREY color: Theme.MENU_ITEM_SHORTCUT_COLOR
@@ -716,15 +699,15 @@
<AnalysisMenuItem@MenuItem>: <AnalysisMenuItem@MenuItem>:
on_close: app.gui.analysis_controls.close_dropdown() on_close: app.gui.analysis_controls.close_dropdown()
background_color: LIGHTER_BACKGROUND_COLOR background_color: Theme.LIGHTER_BACKGROUND_COLOR
height: dp(45) height: dp(45)
canvas.before: canvas.before:
Color: Color:
rgba: WHITE rgba: Theme.MENU_ITEM_FONT_COLOR
Line: Line:
points: self.x,self.y, self.x,self.y+self.height points: self.x,self.y, self.x,self.y+self.height
Color: Color:
rgba: LIGHTGREY rgba: Theme.MENU_ITEM_SHORTCUT_COLOR
Line Line
points: self.x,self.y,self.x+self.width,self.y points: self.x,self.y,self.x+self.width,self.y
width: 1 width: 1
@@ -735,52 +718,52 @@
katrain: app.gui katrain: app.gui
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:extra") text: i18n._("analysis:extra")
icon: 'img/Extra.png' icon: 'Extra.png'
shortcut: 'a' shortcut: 'a'
on_action: root.katrain("analyze-extra", "extra") on_action: root.katrain("analyze-extra", "extra")
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:equalize") text: i18n._("analysis:equalize")
icon: 'img/Equalize.png' icon: 'Equalize.png'
shortcut: 's' shortcut: 's'
on_action: root.katrain("analyze-extra", "equalize") on_action: root.katrain("analyze-extra", "equalize")
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:sweep") text: i18n._("analysis:sweep")
icon: 'img/Sweep.png' icon: 'Sweep.png'
shortcut: 'd' shortcut: 'd'
on_action: root.katrain("analyze-extra", "sweep") on_action: root.katrain("analyze-extra", "sweep")
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:alternatives") text: i18n._("analysis:alternatives")
icon: 'img/Alternative.png' icon: 'Alternative.png'
shortcut: 'f' shortcut: 'f'
on_action: root.katrain("analyze-extra", "alternative") on_action: root.katrain("analyze-extra", "alternative")
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:region") text: i18n._("analysis:region")
icon: 'img/local.png' icon: 'local.png'
shortcut: 'g' shortcut: 'g'
on_action: root.katrain("select-box",) on_action: root.katrain("select-box",)
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:insert") text: i18n._("analysis:insert")
icon: 'img/Insert-Move.png' icon: 'Insert-Move.png'
shortcut: 'i' shortcut: 'i'
on_action: root.katrain("insert-mode") on_action: root.katrain("insert-mode")
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:playtoend") text: i18n._("analysis:playtoend")
icon: 'img/Finish.png' icon: 'Finish.png'
shortcut: 'l' shortcut: 'l'
on_action: root.katrain("play-to-end") on_action: root.katrain("play-to-end")
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:game") text: i18n._("analysis:game")
icon: 'img/analysis.png' icon: 'analysis.png'
shortcut: '' shortcut: ''
on_action: root.open_game_analysis_popup() on_action: root.open_game_analysis_popup()
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:continuous") text: i18n._("analysis:continuous")
icon: 'img/play.png' icon: 'play.png'
shortcut: 'space' shortcut: 'space'
on_action: root.katrain.toggle_continuous_analysis() on_action: root.katrain.toggle_continuous_analysis()
AnalysisMenuItem: AnalysisMenuItem:
text: i18n._("analysis:aimove") text: i18n._("analysis:aimove")
icon: 'img/ai.png' icon: 'ai.png'
shortcut: 'enter' shortcut: 'enter'
on_action: root.katrain("ai-move") on_action: root.katrain("ai-move")
@@ -827,7 +810,7 @@
width: analysis_button.width width: analysis_button.width
size_hint: None, 1 size_hint: None, 1
AutoSizedRectangleButton: AutoSizedRectangleButton:
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
size_hint_y: 0.5 size_hint_y: 0.5
pos_hint: {'center_y': 0.5, 'x': 0} pos_hint: {'center_y': 0.5, 'x': 0}
id: analysis_button id: analysis_button
@@ -838,7 +821,7 @@
x: self.parent.x + 2 x: self.parent.x + 2
size_hint: None, 0.4 size_hint: None, 0.4
width: self.height/2 width: self.height/2
source: 'img/menu.png' source: 'menu.png'
mipmap: True mipmap: True
@@ -854,7 +837,7 @@
<MenuLabel@BackgroundMixin+Label>: <MenuLabel@BackgroundMixin+Label>:
background_color: BACKGROUND_COLOR background_color: Theme.BACKGROUND_COLOR
font_size: self.height * 0.5 font_size: self.height * 0.5
height: self.parent.item_height or 50 height: self.parent.item_height or 50
size_hint: 1,None size_hint: 1,None
@@ -934,7 +917,7 @@
<HamburgerMenuContents@MDBoxLayout+BackgroundMixin>: <HamburgerMenuContents@MDBoxLayout+BackgroundMixin>:
katrain: app.gui katrain: app.gui
player_setup: player_setup player_setup: player_setup
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
orientation: 'vertical' orientation: 'vertical'
item_height: self.height/20 item_height: self.height/20
MenuLabel: MenuLabel:
@@ -952,43 +935,43 @@
item_height: root.item_height item_height: root.item_height
MainMenuItem: MainMenuItem:
text: i18n._('menu:newgame') text: i18n._('menu:newgame')
icon: 'img/New-Game.png' icon: 'New-Game.png'
shortcut: 'Ctrl-N' shortcut: 'Ctrl-N'
on_action: root.katrain("new-game-popup") on_action: root.katrain("new-game-popup")
height: root.item_height height: root.item_height
MainMenuItem: MainMenuItem:
text: i18n._('menu:save') text: i18n._('menu:save')
icon: 'img/Save-Game.png' icon: 'Save-Game.png'
shortcut: 'Ctrl-S' shortcut: 'Ctrl-S'
on_action: root.katrain("save-game") on_action: root.katrain("save-game")
MainMenuItem: MainMenuItem:
text: i18n._('menu:save-as') text: i18n._('menu:save-as')
icon: 'img/Save-Game-As.png' icon: 'Save-Game-As.png'
shortcut: 'Ctrl-D' shortcut: 'Ctrl-D'
on_action: root.katrain("save-game-as-popup") on_action: root.katrain("save-game-as-popup")
MainMenuItem: MainMenuItem:
text: i18n._('menu:load') text: i18n._('menu:load')
icon: 'img/Load-Game.png' icon: 'Load-Game.png'
shortcut: 'Ctrl-L' shortcut: 'Ctrl-L'
on_action: root.katrain("analyze-sgf-popup") on_action: root.katrain("analyze-sgf-popup")
MainMenuItem: MainMenuItem:
text: i18n._('menu:clocksettings') text: i18n._('menu:clocksettings')
icon: 'img/Time-Settings.png' icon: 'Time-Settings.png'
shortcut: 'F5' shortcut: 'F5'
on_action: root.katrain('timer-popup') on_action: root.katrain('timer-popup')
MainMenuItem: MainMenuItem:
text: i18n._('menu:teachsettings') text: i18n._('menu:teachsettings')
icon: 'img/Teaching-Settings.png' icon: 'Teaching-Settings.png'
shortcut: 'F6' shortcut: 'F6'
on_action: root.katrain('teacher-popup') on_action: root.katrain('teacher-popup')
MainMenuItem: MainMenuItem:
text: i18n._('menu:aisettings') text: i18n._('menu:aisettings')
icon: 'img/AI-Settings.png' icon: 'AI-Settings.png'
shortcut: 'F7' shortcut: 'F7'
on_action: root.katrain('ai-popup') on_action: root.katrain('ai-popup')
MainMenuItem: MainMenuItem:
text: i18n._('menu:settings') text: i18n._('menu:settings')
icon: 'img/General-Settings.png' icon: 'General-Settings.png'
shortcut: 'F8' shortcut: 'F8'
on_action: root.katrain('config-popup') on_action: root.katrain('config-popup')
MenuLabel: MenuLabel:
@@ -998,28 +981,28 @@
height: 1.5 * root.item_height height: 1.5 * root.item_height
padding: CP_PADDING, 0 padding: CP_PADDING, 0
LangButton: LangButton:
icon: 'img/flags/flag-uk.png' icon: 'flags/flag-uk.png'
on_press: app.language = 'en' on_press: app.language = 'en'
LangButton: LangButton:
icon: 'img/flags/flag-de.png' icon: 'flags/flag-de.png'
on_press: app.language = 'de' on_press: app.language = 'de'
LangButton: LangButton:
icon: 'img/flags/flag-fr.png' icon: 'flags/flag-fr.png'
on_press: app.language = 'fr' on_press: app.language = 'fr'
# LangButton: # LangButton:
# icon: 'img/flags/flag-es.png' # icon: 'flags/flag-es.png'
# on_press: app.language = 'es' # on_press: app.language = 'es'
LangButton: LangButton:
icon: 'img/flags/flag-ru.png' icon: 'flags/flag-ru.png'
on_press: app.language = 'ru' on_press: app.language = 'ru'
LangButton: LangButton:
icon: 'img/flags/flag-cn.png' icon: 'flags/flag-cn.png'
on_press: app.language = 'cn' on_press: app.language = 'cn'
LangButton: LangButton:
icon: 'img/flags/flag-ko.png' icon: 'flags/flag-ko.png'
on_press: app.language = 'ko' on_press: app.language = 'ko'
LangButton: LangButton:
icon: 'img/flags/flag-jp.png' icon: 'flags/flag-jp.png'
on_press: app.language = 'jp' on_press: app.language = 'jp'
BoxLayout: BoxLayout:
size_hint: 1,None size_hint: 1,None
@@ -1037,10 +1020,10 @@
<PlayAnalyzeButton@SizedToggleButton>: <PlayAnalyzeButton@SizedToggleButton>:
inactive_background_color: BOX_BACKGROUND_COLOR inactive_background_color: Theme.BOX_BACKGROUND_COLOR
active_background_color: YELLOW active_background_color: Theme.PLAY_ANALYZE_TAB_COLOR
ripple_color: BACKGROUND_COLOR ripple_color: Theme.BACKGROUND_COLOR
text_color: self.inactive_background_color if self.state=='down' else self.active_background_color text_color: self.inactive_background_color if self.state=='down' else self.active_background_color
size_hint: 0.5,1 size_hint: 0.5,1
_font_size: self.height * 0.5 _font_size: self.height * 0.5
group: 'play_analyze' group: 'play_analyze'
@@ -1051,7 +1034,6 @@
play: play play: play
analyze: analyze analyze: analyze
OutlineBox: OutlineBox:
outline_color: WHITE
size_hint: 0.7,0.7 size_hint: 0.7,0.7
pos_hint: {'center_x':0.5,'center_y':0.45} pos_hint: {'center_x':0.5,'center_y':0.45}
PlayAnalyzeButton: PlayAnalyzeButton:
@@ -1079,7 +1061,7 @@
Screen: Screen:
name: 'gui' name: 'gui'
BGBoxLayout: BGBoxLayout:
background_color: BACKGROUND_COLOR background_color: Theme.BACKGROUND_COLOR
MDBoxLayout: MDBoxLayout:
orientation: 'vertical' orientation: 'vertical'
AnalysisControls: AnalysisControls:
@@ -1116,7 +1098,3 @@
on_state: if self.state=='close': root.update_state() # ai move etc on nav drawer close on_state: if self.state=='close': root.update_state() # ai move etc on nav drawer close
HamburgerMenuContents: HamburgerMenuContents:
id: nav_drawer_contents id: nav_drawer_contents
+67 -67
View File
@@ -34,13 +34,13 @@ from katrain.core.lang import i18n
from katrain.core.utils import evaluation_class, format_visits, var_to_grid from katrain.core.utils import evaluation_class, format_visits, var_to_grid
from katrain.gui.kivyutils import draw_circle, draw_text, cached_texture from katrain.gui.kivyutils import draw_circle, draw_text, cached_texture
from katrain.gui.popups import I18NPopup, ReAnalyzeGamePopup from katrain.gui.popups import I18NPopup, ReAnalyzeGamePopup
from katrain.gui.style import * from katrain.gui.theme import Theme
class BadukPanWidget(Widget): class BadukPanWidget(Widget):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(BadukPanWidget, self).__init__(**kwargs) super(BadukPanWidget, self).__init__(**kwargs)
self.stones_sounds = [SoundLoader.load(f"sounds/stone{i}.wav") for i in [1, 2, 3, 4, 5]] self.stones_sounds = [SoundLoader.load(file) for file in Theme.STONE_SOUNDS]
self.trainer_config = {} self.trainer_config = {}
self.ghost_stone = [] self.ghost_stone = []
self.gridpos_x = [] self.gridpos_x = []
@@ -208,16 +208,18 @@ class BadukPanWidget(Widget):
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - stone_size, self.gridpos_y[y] - stone_size), pos=(self.gridpos_x[x] - stone_size, self.gridpos_y[y] - stone_size),
size=(2 * stone_size, 2 * stone_size), size=(2 * stone_size, 2 * stone_size),
texture=cached_texture(f"img/{player}_stone.png"), texture=cached_texture(Theme.STONE_TEXTURE[player]),
) )
if evalcol: if evalcol:
eval_radius = math.sqrt(evalscale) # scale area by evalscale eval_radius = math.sqrt(evalscale) # scale area by evalscale
evalsize = self.stone_size * (EVAL_DOT_MIN_SIZE + eval_radius * (EVAL_DOT_MAX_SIZE - EVAL_DOT_MIN_SIZE)) evalsize = self.stone_size * (
Theme.EVAL_DOT_MIN_SIZE + eval_radius * (Theme.EVAL_DOT_MAX_SIZE - Theme.EVAL_DOT_MIN_SIZE)
)
Color(*evalcol) Color(*evalcol)
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - evalsize, self.gridpos_y[y] - evalsize), pos=(self.gridpos_x[x] - evalsize, self.gridpos_y[y] - evalsize),
size=(2 * evalsize, 2 * evalsize), size=(2 * evalsize, 2 * evalsize),
texture=cached_texture("img/dot.png"), texture=cached_texture(Theme.EVAL_DOT_TEXTURE),
) )
if innercol: if innercol:
Color(*innercol) Color(*innercol)
@@ -225,12 +227,12 @@ class BadukPanWidget(Widget):
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - inner_size, self.gridpos_y[y] - inner_size), pos=(self.gridpos_x[x] - inner_size, self.gridpos_y[y] - inner_size),
size=(2 * inner_size, 2 * inner_size), size=(2 * inner_size, 2 * inner_size),
texture=cached_texture("img/inner.png"), texture=cached_texture(Theme.LAST_MOVE_TEXTURE),
) )
def eval_color(self, points_lost, show_dots_for_class: List[bool] = None) -> Optional[List[float]]: def eval_color(self, points_lost, show_dots_for_class: List[bool] = None) -> Optional[List[float]]:
i = evaluation_class(points_lost, self.trainer_config["eval_thresholds"]) i = evaluation_class(points_lost, self.trainer_config["eval_thresholds"])
colors = EVAL_COLORS[self.trainer_config["theme"]] colors = Theme.EVAL_COLORS[self.trainer_config["theme"]]
if show_dots_for_class is None or show_dots_for_class[i]: if show_dots_for_class is None or show_dots_for_class[i]:
return colors[i] return colors[i]
@@ -256,7 +258,7 @@ class BadukPanWidget(Widget):
board_height_with_margins = y_grid_spaces * self.grid_size board_height_with_margins = y_grid_spaces * self.grid_size
extra_px_margin_x = (self.width - board_width_with_margins) / 2 extra_px_margin_x = (self.width - board_width_with_margins) / 2
extra_px_margin_y = (self.height - board_height_with_margins) / 2 extra_px_margin_y = (self.height - board_height_with_margins) / 2
self.stone_size = self.grid_size * STONE_SIZE self.stone_size = self.grid_size * Theme.STONE_SIZE
self.gridpos_x = [ self.gridpos_x = [
self.pos[0] + extra_px_margin_x + math.floor((grid_spaces_margin_x[0] + i) * self.grid_size + 0.5) self.pos[0] + extra_px_margin_x + math.floor((grid_spaces_margin_x[0] + i) * self.grid_size + 0.5)
@@ -268,19 +270,19 @@ class BadukPanWidget(Widget):
] ]
if katrain.game.insert_mode: if katrain.game.insert_mode:
Color(1, 1, 1, 0.75) # dreamy Color(*Theme.INSERT_BOARD_COLOR_TINT) # dreamy
else: else:
Color(1, 0.95, 0.8, 1) # image is a bit too light Color(*Theme.BOARD_COLOR_TINT) # image is a bit too light
Rectangle( Rectangle(
pos=( pos=(
self.gridpos_x[0] - self.grid_size * grid_spaces_margin_x[0], self.gridpos_x[0] - self.grid_size * grid_spaces_margin_x[0],
self.gridpos_y[0] - self.grid_size * grid_spaces_margin_y[0], self.gridpos_y[0] - self.grid_size * grid_spaces_margin_y[0],
), ),
size=(self.grid_size * x_grid_spaces, self.grid_size * y_grid_spaces), size=(self.grid_size * x_grid_spaces, self.grid_size * y_grid_spaces),
texture=cached_texture("img/board.png"), texture=cached_texture(Theme.BOARD_TEXTURE),
) )
Color(*LINE_COLOR) Color(*Theme.LINE_COLOR)
for i in range(board_size_x): for i in range(board_size_x):
Line(points=[(self.gridpos_x[i], self.gridpos_y[0]), (self.gridpos_x[i], self.gridpos_y[-1])]) Line(points=[(self.gridpos_x[i], self.gridpos_y[0]), (self.gridpos_x[i], self.gridpos_y[-1])])
for i in range(board_size_y): for i in range(board_size_y):
@@ -295,10 +297,10 @@ class BadukPanWidget(Widget):
[int(size / 2)] if size % 2 == 1 and size > 7 else [] [int(size / 2)] if size % 2 == 1 and size > 7 else []
) )
starpt_size = self.grid_size * STARPOINT_SIZE starpt_size = self.grid_size * Theme.STARPOINT_SIZE
for x in star_point_coords(board_size_x): for x in star_point_coords(board_size_x):
for y in star_point_coords(board_size_y): for y in star_point_coords(board_size_y):
draw_circle((self.gridpos_x[x], self.gridpos_y[y]), starpt_size, LINE_COLOR) draw_circle((self.gridpos_x[x], self.gridpos_y[y]), starpt_size, Theme.LINE_COLOR)
# coordinates # coordinates
if self.draw_coords_enabled: if self.draw_coords_enabled:
@@ -365,7 +367,7 @@ class BadukPanWidget(Widget):
evalcol = self.eval_color(points_lost, show_dots_for_class) evalcol = self.eval_color(points_lost, show_dots_for_class)
else: else:
evalcol = None evalcol = None
inner = STONE_COLORS[m.opponent] if i == 0 and not m in placements else None inner = Theme.STONE_COLORS[m.opponent] if i == 0 and m not in placements else None
drawn_stone[m.coords] = m.player drawn_stone[m.coords] = m.player
self.draw_stone( self.draw_stone(
x=m.coords[0], x=m.coords[0],
@@ -381,9 +383,9 @@ class BadukPanWidget(Widget):
for y in range(0, board_size_y): for y in range(0, board_size_y):
evalcol = self.eval_color(16 * y / board_size_y) evalcol = self.eval_color(16 * y / board_size_y)
self.draw_stone(0, y, "B", evalcol=evalcol, evalscale=y / (board_size_y - 1)) self.draw_stone(0, y, "B", evalcol=evalcol, evalscale=y / (board_size_y - 1))
self.draw_stone(1, y, "B", innercol=STONE_COLORS["W"], evalcol=evalcol) self.draw_stone(1, y, "B", innercol=Theme.STONE_COLORS["W"], evalcol=evalcol)
self.draw_stone(2, y, "W", evalcol=evalcol, evalscale=y / (board_size_y - 1)) self.draw_stone(2, y, "W", evalcol=evalcol, evalscale=y / (board_size_y - 1))
self.draw_stone(3, y, "W", innercol=STONE_COLORS["B"], evalcol=evalcol) self.draw_stone(3, y, "W", innercol=Theme.STONE_COLORS["B"], evalcol=evalcol)
# ownership - allow one move out of date for smooth animation # ownership - allow one move out of date for smooth animation
ownership = current_node.ownership or (current_node.parent and current_node.parent.ownership) ownership = current_node.ownership or (current_node.parent and current_node.parent.ownership)
@@ -404,7 +406,7 @@ class BadukPanWidget(Widget):
for x in range(board_size_x): for x in range(board_size_x):
loss = max(0, (-1 if current_node.children[-1].move.player == "B" else 1) * loss_grid[y][x]) loss = max(0, (-1 if current_node.children[-1].move.player == "B" else 1) * loss_grid[y][x])
if loss > 0: if loss > 0:
Color(*EVAL_COLORS[self.trainer_config["theme"]][1][:3], loss) Color(*Theme.EVAL_COLORS[self.trainer_config["theme"]][1][:3], loss)
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - rsz / 2, self.gridpos_y[y] - rsz / 2), size=(rsz, rsz) pos=(self.gridpos_x[x] - rsz / 2, self.gridpos_y[y] - rsz / 2), size=(rsz, rsz)
) )
@@ -414,7 +416,7 @@ class BadukPanWidget(Widget):
for x in range(board_size_x): for x in range(board_size_x):
ix_owner = "B" if ownership_grid[y][x] > 0 else "W" ix_owner = "B" if ownership_grid[y][x] > 0 else "W"
if ix_owner != (has_stone.get((x, y), -1)): if ix_owner != (has_stone.get((x, y), -1)):
Color(*STONE_COLORS[ix_owner][:3], abs(ownership_grid[y][x])) Color(*Theme.STONE_COLORS[ix_owner][:3], abs(ownership_grid[y][x]))
Rectangle( Rectangle(
pos=(self.gridpos_x[x] - rsz / 2, self.gridpos_y[y] - rsz / 2), size=(rsz, rsz) pos=(self.gridpos_x[x] - rsz / 2, self.gridpos_y[y] - rsz / 2), size=(rsz, rsz)
) )
@@ -436,42 +438,41 @@ class BadukPanWidget(Widget):
if katrain.analysis_controls.policy.active and policy: if katrain.analysis_controls.policy.active and policy:
policy_grid = var_to_grid(policy, (board_size_x, board_size_y)) policy_grid = var_to_grid(policy, (board_size_x, board_size_y))
best_move_policy = max(*policy) best_move_policy = max(*policy)
colors = EVAL_COLORS[self.trainer_config["theme"]] colors = Theme.EVAL_COLORS[self.trainer_config["theme"]]
text_lb = 0.01 * 0.01 text_lb = 0.01 * 0.01
for y in range(board_size_y - 1, -1, -1): for y in range(board_size_y - 1, -1, -1):
for x in range(board_size_x): for x in range(board_size_x):
move_policy = policy_grid[y][x] move_policy = policy_grid[y][x]
pol_order = 5 - int(-math.log10(max(1e-9, move_policy - 1e-9))) if move_policy < 0:
if pol_order >= 0: continue
if move_policy > text_lb: pol_order = max(0, 5 + int(math.log10(max(1e-9, move_policy - 1e-9))))
Color(0.95, 0.75, 0.47, 1) if move_policy > text_lb:
draw_circle(
(self.gridpos_x[x], self.gridpos_y[y]),
self.stone_size * HINT_SCALE * 0.98,
[0.95, 0.75, 0.47, 1],
)
scale = 0.95
else:
scale = 0.5
draw_circle( draw_circle(
(self.gridpos_x[x], self.gridpos_y[y]), (self.gridpos_x[x], self.gridpos_y[y]),
HINT_SCALE * self.stone_size * scale, self.stone_size * Theme.HINT_SCALE * 0.98,
(*colors[pol_order][:3], POLICY_ALPHA), Theme.APPROX_BOARD_COLOR,
)
scale = 0.95
else:
scale = 0.5
draw_circle(
(self.gridpos_x[x], self.gridpos_y[y]),
Theme.HINT_SCALE * self.stone_size * scale,
(*colors[pol_order][:3], Theme.POLICY_ALPHA),
)
if move_policy > text_lb:
Color(*Theme.HINT_TEXT_COLOR)
draw_text(
pos=(self.gridpos_x[x], self.gridpos_y[y]),
text=f"{100 * move_policy :.2f}"[:4] + "%",
font_name="Roboto",
halign="center",
)
if move_policy == best_move_policy:
Color(*Theme.TOP_MOVE_BORDER_COLOR[:3], Theme.POLICY_ALPHA)
Line(
circle=(self.gridpos_x[x], self.gridpos_y[y], self.stone_size - dp(1.2),), width=dp(2),
) )
if move_policy > text_lb:
Color(*BLACK)
draw_text(
pos=(self.gridpos_x[x], self.gridpos_y[y]),
text=f"{100 * move_policy :.2f}"[:4] + "%",
font_name="Roboto",
halign="center",
)
if move_policy == best_move_policy:
Color(*TOP_MOVE_BORDER_COLOR[:3], POLICY_ALPHA)
Line(
circle=(self.gridpos_x[x], self.gridpos_y[y], self.stone_size - dp(1.2),),
width=dp(2),
)
with pass_btn.canvas.after: with pass_btn.canvas.after:
move_policy = policy[-1] move_policy = policy[-1]
@@ -480,7 +481,7 @@ class BadukPanWidget(Widget):
draw_circle( draw_circle(
(pass_btn.pos[0] + pass_btn.width / 2, pass_btn.pos[1] + pass_btn.height / 2), (pass_btn.pos[0] + pass_btn.width / 2, pass_btn.pos[1] + pass_btn.height / 2),
pass_btn.height / 2, pass_btn.height / 2,
(*colors[pol_order][:3], GHOST_ALPHA), (*colors[pol_order][:3], Theme.GHOST_ALPHA),
) )
# pass circle # pass circle
@@ -491,18 +492,18 @@ class BadukPanWidget(Widget):
katrain.controls.timer.paused = True katrain.controls.timer.paused = True
else: else:
text = i18n._("board-pass") text = i18n._("board-pass")
Color(0.45, 0.05, 0.45, 0.7) Color(*Theme.PASS_CIRCLE_COLOR)
center = (self.gridpos_x[int(board_size_x / 2)], self.gridpos_y[int(board_size_y / 2)]) center = (self.gridpos_x[int(board_size_x / 2)], self.gridpos_y[int(board_size_y / 2)])
size = min(self.width, self.height) * 0.227 size = min(self.width, self.height) * 0.227
Ellipse(pos=(center[0] - size / 2, center[1] - size / 2), size=(size, size)) Ellipse(pos=(center[0] - size / 2, center[1] - size / 2), size=(size, size))
Color(0.85, 0.85, 0.85) Color(*Theme.PASS_CIRCLE_TEXT_COLOR)
draw_text(pos=center, text=text, font_size=size * 0.25, halign="center") draw_text(pos=center, text=text, font_size=size * 0.25, halign="center")
self.redraw_hover_contents_trigger() self.redraw_hover_contents_trigger()
def draw_roi_box(self, region_of_interest, width=2): def draw_roi_box(self, region_of_interest, width=2):
xmin, xmax, ymin, ymax = region_of_interest xmin, xmax, ymin, ymax = region_of_interest
Color(*LIGHTER_BACKGROUND_COLOR) Color(*Theme.REGION_BORDER_COLOR)
Line( Line(
rectangle=( rectangle=(
self.gridpos_x[xmin] - self.grid_size / 3, self.gridpos_x[xmin] - self.grid_size / 3,
@@ -514,7 +515,7 @@ class BadukPanWidget(Widget):
) )
def draw_hover_contents(self, *_args): def draw_hover_contents(self, *_args):
ghost_alpha = GHOST_ALPHA ghost_alpha = Theme.GHOST_ALPHA
katrain = self.katrain katrain = self.katrain
game_ended = katrain.game.end_result game_ended = katrain.game.end_result
current_node = katrain.game.current_node current_node = katrain.game.current_node
@@ -560,17 +561,17 @@ class BadukPanWidget(Widget):
move = Move.from_gtp(move_dict["move"]) move = Move.from_gtp(move_dict["move"])
if move.coords is not None: if move.coords is not None:
engine_best_move = move_dict.get("order", 99) == 0 engine_best_move = move_dict.get("order", 99) == 0
scale = HINT_SCALE scale = Theme.HINT_SCALE
text_on = True text_on = True
alpha = HINTS_ALPHA alpha = Theme.HINTS_ALPHA
if ( if (
move_dict["visits"] < low_visits_threshold move_dict["visits"] < low_visits_threshold
and not engine_best_move and not engine_best_move
and not move_dict["move"] in child_moves and not move_dict["move"] in child_moves
): ):
scale = UNCERTAIN_HINT_SCALE scale = Theme.UNCERTAIN_HINT_SCALE
text_on = False text_on = False
alpha = HINTS_LO_ALPHA alpha = Theme.HINTS_LO_ALPHA
if "pv" in move_dict: if "pv" in move_dict:
self.active_pv_moves.append((move.coords, move_dict["pv"], current_node)) self.active_pv_moves.append((move.coords, move_dict["pv"], current_node))
@@ -579,18 +580,17 @@ class BadukPanWidget(Widget):
evalsize = self.stone_size * scale evalsize = self.stone_size * scale
evalcol = self.eval_color(move_dict["pointsLost"]) evalcol = self.eval_color(move_dict["pointsLost"])
if text_on and top_moves_show: # remove grid lines using a board colored circle if text_on and top_moves_show: # remove grid lines using a board colored circle
Color(0.95, 0.75, 0.47, 1)
draw_circle( draw_circle(
(self.gridpos_x[move.coords[0]], self.gridpos_y[move.coords[1]]), (self.gridpos_x[move.coords[0]], self.gridpos_y[move.coords[1]]),
self.stone_size * scale * 0.98, self.stone_size * scale * 0.98,
[0.95, 0.75, 0.47, 1], Theme.APPROX_BOARD_COLOR,
) )
Color(*evalcol[:3], alpha) Color(*evalcol[:3], alpha)
Rectangle( Rectangle(
pos=(self.gridpos_x[move.coords[0]] - evalsize, self.gridpos_y[move.coords[1]] - evalsize), pos=(self.gridpos_x[move.coords[0]] - evalsize, self.gridpos_y[move.coords[1]] - evalsize),
size=(2 * evalsize, 2 * evalsize), size=(2 * evalsize, 2 * evalsize),
texture=cached_texture("img/topmove.png"), texture=cached_texture(Theme.TOP_MOVE_TEXTURE),
) )
if text_on and top_moves_show: # TODO: faster if not sized? if text_on and top_moves_show: # TODO: faster if not sized?
keys = {"size": self.grid_size / 3, "smallsize": self.grid_size / 3.33} keys = {"size": self.grid_size / 3, "smallsize": self.grid_size / 3.33}
@@ -614,7 +614,7 @@ class BadukPanWidget(Widget):
keys[TOP_MOVE_WINRATE] = f"{winrate*100:.1f}" keys[TOP_MOVE_WINRATE] = f"{winrate*100:.1f}"
keys[TOP_MOVE_DELTA_WINRATE] = f"{-move_dict['winrateLost']:+.1%}" keys[TOP_MOVE_DELTA_WINRATE] = f"{-move_dict['winrateLost']:+.1%}"
keys[TOP_MOVE_VISITS] = format_visits(move_dict["visits"]) keys[TOP_MOVE_VISITS] = format_visits(move_dict["visits"])
Color(*BLACK) Color(*Theme.HINT_TEXT_COLOR)
draw_text( draw_text(
pos=(self.gridpos_x[move.coords[0]], self.gridpos_y[move.coords[1]]), pos=(self.gridpos_x[move.coords[0]], self.gridpos_y[move.coords[1]]),
text=fmt.format(**keys), text=fmt.format(**keys),
@@ -626,7 +626,7 @@ class BadukPanWidget(Widget):
if engine_best_move: if engine_best_move:
top_move_coords = move.coords top_move_coords = move.coords
Color(*TOP_MOVE_BORDER_COLOR) Color(*Theme.TOP_MOVE_BORDER_COLOR)
Line( Line(
circle=( circle=(
self.gridpos_x[move.coords[0]], self.gridpos_x[move.coords[0]],
@@ -648,7 +648,7 @@ class BadukPanWidget(Widget):
if move.coords != top_move_coords: # for contrast if move.coords != top_move_coords: # for contrast
dashed_width = 18 dashed_width = 18
Color(*STONE_CONTRAST_COLORS[child_node.player]) Color(*Theme.NEXT_MOVE_DASH_CONTRAST_COLORS[child_node.player])
Line( Line(
circle=( circle=(
self.gridpos_x[move.coords[0]], self.gridpos_x[move.coords[0]],
@@ -659,7 +659,7 @@ class BadukPanWidget(Widget):
) )
else: else:
dashed_width = 10 dashed_width = 10
Color(*STONE_COLORS[child_node.player]) Color(*Theme.STONE_COLORS[child_node.player])
for s in range(0, 360, 30): for s in range(0, 360, 30):
Line( Line(
circle=( circle=(
@@ -727,9 +727,9 @@ class BadukPanWidget(Widget):
Rectangle( # not sure why the -1 here, but seems to center better Rectangle( # not sure why the -1 here, but seems to center better
pos=(board_coords[0] - stone_size - 1, board_coords[1] - stone_size), pos=(board_coords[0] - stone_size - 1, board_coords[1] - stone_size),
size=(2 * stone_size + 1, 2 * stone_size + 1), size=(2 * stone_size + 1, 2 * stone_size + 1),
texture=cached_texture(f"img/{move_player}_stone.png"), texture=cached_texture(Theme.STONE_TEXTURE[move_player]),
) )
Color(*STONE_TEXT_COLORS[move_player]) Color(*Theme.PV_TEXT_COLORS[move_player])
draw_text(pos=board_coords, text=str(i + 1), font_size=self.grid_size * sizefac / 1.45, font_name="Roboto") draw_text(pos=board_coords, text=str(i + 1), font_size=self.grid_size * sizefac / 1.45, font_name="Roboto")
def set_animating_pv(self, pv, node): def set_animating_pv(self, pv, node):
@@ -781,6 +781,6 @@ class AnalysisControls(MDBoxLayout):
class BadukPanControls(MDFloatLayout): class BadukPanControls(MDFloatLayout):
engine_status_col = ListProperty(ENGINE_DOWN_COL) engine_status_col = ListProperty(Theme.ENGINE_DOWN_COLOR)
engine_status_pondering = NumericProperty(-1) engine_status_pondering = NumericProperty(-1)
queries_remaining = NumericProperty(0) queries_remaining = NumericProperty(0)
+1 -1
View File
@@ -2,7 +2,7 @@ import time
from kivy.clock import Clock from kivy.clock import Clock
from kivy.core.audio import SoundLoader from kivy.core.audio import SoundLoader
from kivy.properties import NumericProperty, ObjectProperty, OptionProperty from kivy.properties import ObjectProperty, OptionProperty
from kivy.uix.boxlayout import BoxLayout from kivy.uix.boxlayout import BoxLayout
from kivymd.uix.floatlayout import MDFloatLayout from kivymd.uix.floatlayout import MDFloatLayout
+11 -11
View File
@@ -3,7 +3,7 @@ from kivy.core.image import Image
from kivy.core.text import Label as CoreLabel from kivy.core.text import Label as CoreLabel
from kivy.core.text.markup import MarkupLabel as CoreMarkupLabel from kivy.core.text.markup import MarkupLabel as CoreMarkupLabel
from kivy.core.window import Window from kivy.core.window import Window
from kivy.graphics import * from kivy.graphics import Color, Ellipse, Rectangle
from kivy.properties import ( from kivy.properties import (
BooleanProperty, BooleanProperty,
ListProperty, ListProperty,
@@ -30,7 +30,7 @@ from kivymd.uix.textfield import MDTextField
from katrain.core.constants import AI_STRATEGIES_RECOMMENDED_ORDER, GAME_TYPES, MODE_PLAY, PLAYER_AI from katrain.core.constants import AI_STRATEGIES_RECOMMENDED_ORDER, GAME_TYPES, MODE_PLAY, PLAYER_AI
from katrain.core.lang import i18n from katrain.core.lang import i18n
from katrain.gui.style import DEFAULT_FONT, WHITE from katrain.gui.theme import Theme
class BackgroundMixin(Widget): # -- mixins class BackgroundMixin(Widget): # -- mixins
@@ -69,14 +69,14 @@ class LeftButtonBehavior(ButtonBehavior): # stops buttons etc activating on rig
# -- resizeable buttons / avoid baserectangular for sizing # -- resizeable buttons / avoid baserectangular for sizing
class SizedButton(LeftButtonBehavior, RectangularRippleBehavior, BasePressedButton, BaseFlatButton, BackgroundMixin): class SizedButton(LeftButtonBehavior, RectangularRippleBehavior, BasePressedButton, BaseFlatButton, BackgroundMixin):
text = StringProperty("") text = StringProperty("")
text_color = ListProperty(WHITE) text_color = ListProperty(Theme.BUTTON_TEXT_COLOR)
text_size = ListProperty([100, 100]) text_size = ListProperty([100, 100])
halign = OptionProperty("center", options=["left", "center", "right", "justify", "auto"]) halign = OptionProperty("center", options=["left", "center", "right", "justify", "auto"])
label = ObjectProperty(None) label = ObjectProperty(None)
padding_x = NumericProperty(6) padding_x = NumericProperty(6)
padding_y = NumericProperty(0) padding_y = NumericProperty(0)
_font_size = NumericProperty(None) _font_size = NumericProperty(None)
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(Theme.DEFAULT_FONT)
class AutoSizedButton(SizedButton): class AutoSizedButton(SizedButton):
@@ -143,7 +143,7 @@ class StatsLabel(MDBoxLayout):
label = StringProperty("") label = StringProperty("")
color = ListProperty([1, 1, 1, 1]) color = ListProperty([1, 1, 1, 1])
hidden = BooleanProperty(False) hidden = BooleanProperty(False)
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(Theme.DEFAULT_FONT)
class MyNavigationDrawer(MDNavigationDrawer): class MyNavigationDrawer(MDNavigationDrawer):
@@ -214,7 +214,7 @@ class KeyValueSpinner(Spinner):
sync_height_frac = NumericProperty(1.0) sync_height_frac = NumericProperty(1.0)
value_refs = ListProperty() value_refs = ListProperty()
selected_index = NumericProperty(0) selected_index = NumericProperty(0)
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(Theme.DEFAULT_FONT)
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
@@ -283,7 +283,7 @@ class I18NSpinner(KeyValueSpinner):
sync_height_frac = NumericProperty(1.0) sync_height_frac = NumericProperty(1.0)
value_refs = ListProperty() value_refs = ListProperty()
selected_index = NumericProperty(0) selected_index = NumericProperty(0)
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(Theme.DEFAULT_FONT)
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
@@ -406,7 +406,7 @@ class TriStateMDCheckbox(MDCheckbox):
class AnalysisToggle(MDBoxLayout): class AnalysisToggle(MDBoxLayout):
text = StringProperty("") text = StringProperty("")
default_active = BooleanProperty(False) default_active = BooleanProperty(False)
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(Theme.DEFAULT_FONT)
disabled = BooleanProperty(False) disabled = BooleanProperty(False)
tri_state = BooleanProperty(False) tri_state = BooleanProperty(False)
@@ -426,7 +426,7 @@ class MenuItem(RectangularRippleBehavior, LeftButtonBehavior, MDBoxLayout, Backg
icon = StringProperty("") icon = StringProperty("")
text = StringProperty("") text = StringProperty("")
shortcut = StringProperty("") shortcut = StringProperty("")
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(Theme.DEFAULT_FONT)
content_width = NumericProperty(100) content_width = NumericProperty(100)
def on_left_release(self): def on_left_release(self):
@@ -466,8 +466,8 @@ class CollapsablePanel(MDBoxLayout):
closed_label = StringProperty("Closed Panel") closed_label = StringProperty("Closed Panel")
state = OptionProperty("open", options=["open", "close"]) state = OptionProperty("open", options=["open", "close"])
close_icon = "img/Previous-5.png" close_icon = "Previous-5.png"
open_icon = "img/Next-5.png" open_icon = "Next-5.png"
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.open_close_button, self.header = None, None self.open_close_button, self.header = None, None
+10 -9
View File
@@ -29,18 +29,19 @@ from katrain.core.constants import (
OUTPUT_ERROR, OUTPUT_ERROR,
OUTPUT_INFO, OUTPUT_INFO,
STATUS_INFO, STATUS_INFO,
DATA_FOLDER,
) )
from katrain.core.engine import KataGoEngine from katrain.core.engine import KataGoEngine
from katrain.core.lang import i18n, rank_label from katrain.core.lang import i18n, rank_label
from katrain.core.utils import PATHS, find_package_resource from katrain.core.utils import PATHS, find_package_resource
from katrain.gui.kivyutils import BackgroundMixin, I18NSpinner from katrain.gui.kivyutils import BackgroundMixin, I18NSpinner
from katrain.gui.style import DEFAULT_FONT, EVAL_COLORS from katrain.gui.theme import Theme
from katrain.gui.widgets.progress_loader import ProgressLoader from katrain.gui.widgets.progress_loader import ProgressLoader
class I18NPopup(Popup): class I18NPopup(Popup):
title_key = StringProperty("") title_key = StringProperty("")
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(Theme.DEFAULT_FONT)
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
@@ -326,14 +327,14 @@ class ConfigTeacherPopup(QuickConfigGui):
savesgfs = self.katrain.config("trainer/save_feedback") savesgfs = self.katrain.config("trainer/save_feedback")
show_dots = self.katrain.config("trainer/show_dots") show_dots = self.katrain.config("trainer/show_dots")
self.themes_spinner.value_refs = list(EVAL_COLORS.keys()) self.themes_spinner.value_refs = list(Theme.EVAL_COLORS.keys())
self.options_grid.clear_widgets() self.options_grid.clear_widgets()
for k in ["dot color", "point loss threshold", "num undos", "show dots", "save dots"]: for k in ["dot color", "point loss threshold", "num undos", "show dots", "save dots"]:
self.options_grid.add_widget(DescriptionLabel(text=i18n._(k), font_name=i18n.font_name, font_size=dp(17))) self.options_grid.add_widget(DescriptionLabel(text=i18n._(k), font_name=i18n.font_name, font_size=dp(17)))
for i, (color, threshold, undo, show_dot, savesgf) in enumerate( for i, (color, threshold, undo, show_dot, savesgf) in enumerate(
zip(EVAL_COLORS[theme], thresholds, undos, show_dots, savesgfs) zip(Theme.EVAL_COLORS[theme], thresholds, undos, show_dots, savesgfs)
): ):
self.add_option_widgets( self.add_option_widgets(
[ [
@@ -420,8 +421,8 @@ class ConfigAIPopup(QuickConfigGui):
class ConfigPopup(QuickConfigGui): class ConfigPopup(QuickConfigGui):
def __init__(self, katrain): def __init__(self, katrain):
super().__init__(katrain) super().__init__(katrain)
self.paths = [self.katrain.config("engine/model"), "katrain/models", "~/.katrain"] self.paths = [self.katrain.config("engine/model"), "katrain/models", DATA_FOLDER]
self.katago_paths = [self.katrain.config("engine/katago"), "~/.katrain"] self.katago_paths = [self.katrain.config("engine/katago"), DATA_FOLDER]
Clock.schedule_once(self.check_katas) Clock.schedule_once(self.check_katas)
MDApp.get_running_app().bind(language=self.check_models) MDApp.get_running_app().bind(language=self.check_models)
MDApp.get_running_app().bind(language=self.check_katas) MDApp.get_running_app().bind(language=self.check_katas)
@@ -560,7 +561,7 @@ class ConfigPopup(QuickConfigGui):
for name, url in self.MODELS.items(): for name, url in self.MODELS.items():
filename = os.path.split(url)[1] filename = os.path.split(url)[1]
if not any(os.path.split(f)[1] == filename for f in self.model_files.values): if not any(os.path.split(f)[1] == filename for f in self.model_files.values):
savepath = os.path.expanduser(os.path.join("~/.katrain", filename)) savepath = os.path.expanduser(os.path.join(DATA_FOLDER, filename))
savepath_tmp = savepath + ".part" savepath_tmp = savepath + ".part"
self.katrain.log(f"Downloading {name} model from {url} to {savepath_tmp}", OUTPUT_INFO) self.katrain.log(f"Downloading {name} model from {url} to {savepath_tmp}", OUTPUT_INFO)
progress = ProgressLoader( progress = ProgressLoader(
@@ -627,8 +628,8 @@ class ConfigPopup(QuickConfigGui):
filename = os.path.split(url)[1] filename = os.path.split(url)[1]
exe_name = unzipped_name(filename) exe_name = unzipped_name(filename)
if not any(os.path.split(f)[1] == exe_name for f in self.katago_files.values): if not any(os.path.split(f)[1] == exe_name for f in self.katago_files.values):
savepath_tmp = os.path.expanduser(os.path.join("~/.katrain", filename)) savepath_tmp = os.path.expanduser(os.path.join(DATA_FOLDER, filename))
exe_path_name = os.path.expanduser(os.path.join("~/.katrain", exe_name)) exe_path_name = os.path.expanduser(os.path.join(DATA_FOLDER, exe_name))
self.katrain.log(f"Downloading binary {name} from {url} to {savepath_tmp}", OUTPUT_INFO) self.katrain.log(f"Downloading binary {name} from {url} to {savepath_tmp}", OUTPUT_INFO)
progress = ProgressLoader( progress = ProgressLoader(
download_url=url, download_url=url,
-82
View File
@@ -1,82 +0,0 @@
def to_hexcol(kivycol):
return "#" + "".join(f"{round(c * 255):02x}" for c in kivycol[:3])
DEFAULT_FONT = "fonts/NotoSansCJKsc-Regular.otf"
# basic color definitions
WHITE = [0.95, 0.95, 0.95, 1]
BLACK = [0.05, 0.05, 0.05, 1]
LIGHTGREY = [0.7, 0.7, 0.7, 1]
GREY = [0.5, 0.5, 0.5, 1]
LIGHTERGREY = [0.85, 0.85, 0.85, 1]
RED = [0.8, 0.1, 0.1, 1]
GREEN = [0.1, 0.8, 0.1, 1]
YELLOW = [0.8, 0.8, 0.1, 1]
DARKRED = [0.3, 0.1, 0.1, 1]
ORANGE = [242 / 255, 96 / 255, 34 / 255, 1]
LIGHT_ORANGE = [1, 0.5, 0.25, 1]
# gui colors
BACKGROUND_COLOR = [36 / 255, 48 / 255, 62 / 255, 1]
BOX_BACKGROUND_COLOR = [46 / 255, 65 / 255, 88 / 255, 1]
LIGHTER_BACKGROUND_COLOR = [64 / 255, 85 / 255, 110 / 255, 1]
TEXT_COLOR = WHITE
SCORE_COLOR = [0.3, 0.7, 0.9, 1]
WINRATE_COLOR = GREEN
POINTLOSS_COLOR = YELLOW
# eval dots
EVAL_COLORS = {
"theme:normal": [
[0.447, 0.129, 0.42, 1],
[0.8, 0, 0, 1],
[0.9, 0.4, 0.1, 1],
[0.95, 0.95, 0, 1],
[0.67, 0.9, 0.18, 1],
[0.117, 0.588, 0, 1],
],
"theme:red-green-colourblind": [
[1, 0, 1, 1],
[1, 0, 0, 1],
[1, 0.5, 0, 1],
[1, 1, 0, 1],
[0, 1, 1, 1],
[0, 0, 1, 1],
],
}
EVAL_DOT_MAX_SIZE = 0.5
EVAL_DOT_MIN_SIZE = 0.25
STONE_COLORS = {"B": BLACK, "W": WHITE}
STONE_CONTRAST_COLORS = {"B": LIGHTERGREY, "W": GREY}
OUTLINE_COLORS = {"B": [0.3, 0.3, 0.3, 0.5], "W": [0.7, 0.7, 0.7, 0.5]}
STONE_TEXT_COLORS = {"W": BLACK, "B": WHITE}
# board
LINE_COLOR = [0, 0, 0]
STARPOINT_SIZE = 0.1
BOARD_COLOR = [0.85, 0.68, 0.40, 1]
STONE_SIZE = 0.505 # texture edge is transparent
GHOST_ALPHA = 0.6
POLICY_ALPHA = 0.5
HINTS_LO_ALPHA = 0.6
HINTS_ALPHA = 0.8
TOP_MOVE_BORDER_COLOR = [10 / 255, 200 / 255, 250 / 255, 1.0]
CHILD_SCALE = 0.95
HINT_SCALE = 0.98
UNCERTAIN_HINT_SCALE = 0.7
# ponder light
ENGINE_DOWN_COL = EVAL_COLORS["theme:normal"][1]
ENGINE_BUSY_COL = EVAL_COLORS["theme:normal"][2]
ENGINE_READY_COL = EVAL_COLORS["theme:normal"][-1]
# info
INFO_PV_COLOR = to_hexcol(YELLOW)
+145
View File
@@ -0,0 +1,145 @@
from kivy.metrics import sp
def to_hexcol(kivycol):
return "#" + "".join(f"{round(c * 255):02x}" for c in kivycol[:3])
# basic colors
WHITE = [0.95, 0.95, 0.95, 1]
BLACK = [0.05, 0.05, 0.05, 1]
LIGHT_GREY = [0.7, 0.7, 0.7, 1]
GREY = [0.5, 0.5, 0.5, 1]
LIGHTER_GREY = [0.85, 0.85, 0.85, 1]
RED = [0.8, 0.1, 0.1, 1]
GREEN = [0.1, 0.8, 0.1, 1]
YELLOW = [0.8, 0.8, 0.1, 1]
ORANGE = [242 / 255, 96 / 255, 34 / 255, 1]
LIGHT_ORANGE = [1, 0.5, 0.25, 1]
BLUE = [0.3, 0.7, 0.9, 1]
class Theme:
# font
DEFAULT_FONT = "fonts/NotoSansCJKsc-Regular.otf"
INPUT_FONT_SIZE = sp(25)
DESC_FONT_SIZE = sp(20)
NOTES_FONT_SIZE = sp(18)
# gui colors
BACKGROUND_COLOR = [36 / 255, 48 / 255, 62 / 255, 1]
BOX_BACKGROUND_COLOR = [46 / 255, 65 / 255, 88 / 255, 1]
LIGHTER_BACKGROUND_COLOR = [64 / 255, 85 / 255, 110 / 255, 1]
SCROLLBAR_COLOR = LIGHT_GREY
TEXT_COLOR = WHITE
SCORE_COLOR = BLUE # blue
WINRATE_COLOR = GREEN # green
POINTLOSS_COLOR = YELLOW # yellow
BUTTON_INACTIVE_COLOR = LIGHT_GREY
BUTTON_BORDER_COLOR = WHITE
BUTTON_TEXT_COLOR = WHITE
PAUSE_ACTIVE_COLOR = ORANGE
TIMER_TEXT_COLOR = GREEN
TIMER_TEXT_TIMEOUT_COLOR = ORANGE
CIRCLE_TEXT_COLORS = {"W": BLACK, "B": WHITE}
NOTES_TAB_FONT_COLOR = YELLOW
INFO_TAB_FONT_COLOR = WHITE
ERROR_BORDER_COLOR = RED
MENU_ITEM_FONT_COLOR = WHITE
MENU_ITEM_SHORTCUT_COLOR = LIGHT_GREY
PLAY_ANALYZE_TAB_COLOR = YELLOW
INPUT_FONT_COLOR = WHITE
# gui spacing
CP_SPACING = 6
CP_SMALL_SPACING = 3
CP_PADDING = 6
# textures
STONE_TEXTURE = {"B": "B_stone.png", "W": "W_stone.png"}
EVAL_DOT_TEXTURE = "dot.png"
LAST_MOVE_TEXTURE = "inner.png"
TOP_MOVE_TEXTURE = "topmove.png"
BOARD_TEXTURE = "board.png"
GRAPH_TEXTURE = "graph_bg.png"
# sounds
STONE_SOUNDS = [f"stone{i}.wav" for i in [1, 2, 3, 4, 5]]
# eval dots
EVAL_COLORS = {
"theme:normal": [
[0.447, 0.129, 0.42, 1],
[0.8, 0, 0, 1],
[0.9, 0.4, 0.1, 1],
[0.95, 0.95, 0, 1],
[0.67, 0.9, 0.18, 1],
[0.117, 0.588, 0, 1],
],
"theme:red-green-colourblind": [
[1, 0, 1, 1],
[1, 0, 0, 1],
[1, 0.5, 0, 1],
[1, 1, 0, 1],
[0, 1, 1, 1],
[0, 0, 1, 1],
],
}
EVAL_DOT_MAX_SIZE = 0.5
EVAL_DOT_MIN_SIZE = 0.25
# board theme
APPROX_BOARD_COLOR = [0.95, 0.75, 0.47, 1] # for drawing on top of / hiding what's under it
BOARD_COLOR_TINT = [1, 0.95, 0.8, 1] # multiplied by texture
HINT_TEXT_COLOR = BLACK
REGION_BORDER_COLOR = LIGHTER_BACKGROUND_COLOR
INSERT_BOARD_COLOR_TINT = [1, 1, 1, 0.75]
PASS_CIRCLE_COLOR = [0.45, 0.05, 0.45, 0.7]
PASS_CIRCLE_TEXT_COLOR = [0.85, 0.85, 0.85]
STONE_COLORS = {"B": BLACK, "W": WHITE}
NEXT_MOVE_DASH_CONTRAST_COLORS = {"B": LIGHTER_GREY, "W": GREY}
OUTLINE_COLORS = {"B": [0.3, 0.3, 0.3, 0.5], "W": [0.7, 0.7, 0.7, 0.5]}
PV_TEXT_COLORS = {"W": BLACK, "B": WHITE} # numbers in PV
# board
LINE_COLOR = [0, 0, 0]
STARPOINT_SIZE = 0.1
BOARD_COLOR = [0.85, 0.68, 0.40, 1]
STONE_SIZE = 0.505 # texture edge is transparent
GHOST_ALPHA = 0.6
POLICY_ALPHA = 0.5
HINTS_LO_ALPHA = 0.6
HINTS_ALPHA = 0.8
TOP_MOVE_BORDER_COLOR = [10 / 255, 200 / 255, 250 / 255, 1.0]
CHILD_SCALE = 0.95
HINT_SCALE = 0.98
UNCERTAIN_HINT_SCALE = 0.7
# ponder light
ENGINE_DOWN_COLOR = EVAL_COLORS["theme:normal"][1]
ENGINE_BUSY_COLOR = EVAL_COLORS["theme:normal"][2]
ENGINE_READY_COLOR = EVAL_COLORS["theme:normal"][-1]
ENGINE_PONDERING_COLOR = YELLOW
# info PV link
INFO_PV_COLOR = to_hexcol(YELLOW)
# graph
GRAPH_DOT_COLOR = [0.85, 0.3, 0.3, 1]
WINRATE_MARKER_COLOR = [0.05, 0.7, 0.05, 1]
SCORE_MARKER_COLOR = [0.2, 0.6, 0.8, 1]
# move tree
MOVE_TREE_LINE = LIGHT_GREY
MOVE_TREE_CURRENT = YELLOW
MOVE_TREE_SELECTED = RED
MOVE_TREE_INSERT_NODE_PARENT = GREEN
MOVE_TREE_INSERT_CURRENT = ORANGE
MOVE_TREE_INSERT_OTHER = LIGHT_ORANGE
MOVE_TREE_COLLAPSED = LIGHT_GREY
MOVE_TREE_STONE_OUTLINE_COLORS = {"W": BLACK, "B": WHITE}
+2 -2
View File
@@ -51,7 +51,7 @@ from kivy.uix.filechooser import FileChooserListLayout, FileChooserListView
from kivy.uix.treeview import TreeView, TreeViewLabel from kivy.uix.treeview import TreeView, TreeViewLabel
from kivy.utils import platform from kivy.utils import platform
from katrain.gui.style import DEFAULT_FONT from katrain.gui.theme import Theme
if platform == "win": if platform == "win":
from ctypes import windll, create_unicode_buffer from ctypes import windll, create_unicode_buffer
@@ -106,7 +106,7 @@ def get_drives():
class I18NFileChooserListView(FileChooserListView): class I18NFileChooserListView(FileChooserListView):
font_name = StringProperty(DEFAULT_FONT) font_name = StringProperty(Theme.DEFAULT_FONT)
show_hidden = BooleanProperty(True) # avoid errors show_hidden = BooleanProperty(True) # avoid errors
+14 -23
View File
@@ -7,10 +7,12 @@ from kivy.properties import BooleanProperty, Clock, ListProperty, NumericPropert
from kivy.uix.widget import Widget from kivy.uix.widget import Widget
from kivymd.app import MDApp from kivymd.app import MDApp
from katrain.gui.theme import Theme
class Graph(Widget): class Graph(Widget):
marker_font_size = NumericProperty(0) marker_font_size = NumericProperty(0)
background_image = StringProperty("img/graph_bg.png") background_image = StringProperty(Theme.GRAPH_TEXTURE)
background_color = ListProperty([1, 1, 1, 1]) background_color = ListProperty([1, 1, 1, 1])
highlighted_index = NumericProperty(0) highlighted_index = NumericProperty(0)
nodes = ListProperty([]) nodes = ListProperty([])
@@ -157,21 +159,10 @@ class ScoreGraph(Graph):
Builder.load_string( Builder.load_string(
""" """
#:set GRAPH_CENTER_COLOR [0.5,0.5,0.5] #:import Theme katrain.gui.theme.Theme
#:set GRAPH_DOT_COLOR [0.85,0.3,0.3,1]
#:set WINRATE_MARKER_COLOR [0.05, 0.7, 0.05, 1]
#:set SCORE_MARKER_COLOR [0.2, 0.6, 0.8, 1]
#:import LIGHTER_BACKGROUND_COLOR katrain.gui.style.LIGHTER_BACKGROUND_COLOR
#:import BOX_BACKGROUND_COLOR katrain.gui.style.BOX_BACKGROUND_COLOR
#:import SCORE_COLOR katrain.gui.style.SCORE_COLOR
#:import WINRATE_COLOR katrain.gui.style.WINRATE_COLOR
#:import BLACK katrain.gui.style.BLACK
#:import WHITE katrain.gui.style.WHITE
#:import YELLOW katrain.gui.style.YELLOW
<Graph>: <Graph>:
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
marker_font_size: 0.1 * self.height marker_font_size: 0.1 * self.height
canvas.before: canvas.before:
Color: Color:
@@ -189,12 +180,12 @@ Builder.load_string(
<ScoreGraph>: <ScoreGraph>:
canvas: canvas:
Color: Color:
rgba: SCORE_COLOR rgba: Theme.SCORE_COLOR
Line: Line:
points: root.score_points if root.show_score else [] points: root.score_points if root.show_score else []
width: dp(1.1) width: dp(1.1)
Color: Color:
rgba: WINRATE_COLOR rgba: Theme.WINRATE_COLOR
Line: Line:
points: root.winrate_points if root.show_winrate else [] points: root.winrate_points if root.show_winrate else []
width: dp(1.1) width: dp(1.1)
@@ -204,13 +195,13 @@ Builder.load_string(
points: root.navigate_move[1], root.y, root.navigate_move[1], root.y+root.height points: root.navigate_move[1], root.y, root.navigate_move[1], root.y+root.height
width: 1 width: 1
Color: Color:
rgba: GRAPH_DOT_COLOR rgba: Theme.GRAPH_DOT_COLOR
Ellipse: Ellipse:
id: score_dot id: score_dot
pos: [c - self.highlight_size / 2 for c in (self.score_dot_pos if not self.navigate_move[0] else [self.navigate_move[1],self.navigate_move[2]] ) ] pos: [c - self.highlight_size / 2 for c in (self.score_dot_pos if not self.navigate_move[0] else [self.navigate_move[1],self.navigate_move[2]] ) ]
size: (self.highlight_size,self.highlight_size) if root.show_score else (0.0001,0.0001) size: (self.highlight_size,self.highlight_size) if root.show_score else (0.0001,0.0001)
Color: Color:
rgba: GRAPH_DOT_COLOR rgba: Theme.GRAPH_DOT_COLOR
Ellipse: Ellipse:
id: winrate_dot id: winrate_dot
pos: [c - self.highlight_size / 2 for c in (self.winrate_dot_pos if not self.navigate_move[0] else [self.navigate_move[1],self.navigate_move[3]] ) ] pos: [c - self.highlight_size / 2 for c in (self.winrate_dot_pos if not self.navigate_move[0] else [self.navigate_move[1],self.navigate_move[3]] ) ]
@@ -218,32 +209,32 @@ Builder.load_string(
# score ticks # score ticks
GraphMarkerLabel: GraphMarkerLabel:
font_size: root.marker_font_size font_size: root.marker_font_size
color: SCORE_MARKER_COLOR color: Theme.SCORE_MARKER_COLOR
pos: root.x + root.width - self.width-1, root.pos[1]+root.height - self.font_size - 1 pos: root.x + root.width - self.width-1, root.pos[1]+root.height - self.font_size - 1
text: 'B+{}'.format(root.score_scale) text: 'B+{}'.format(root.score_scale)
opacity: int(root.show_score) opacity: int(root.show_score)
GraphMarkerLabel: GraphMarkerLabel:
font_size: root.marker_font_size font_size: root.marker_font_size
color: SCORE_MARKER_COLOR color: Theme.SCORE_MARKER_COLOR
pos: root.x + root.width - self.width-1, root.y + root.height*0.5 - self.height/2 + 2 pos: root.x + root.width - self.width-1, root.y + root.height*0.5 - self.height/2 + 2
text: i18n._('Jigo') text: i18n._('Jigo')
opacity: int(root.show_score) opacity: int(root.show_score)
GraphMarkerLabel: GraphMarkerLabel:
font_size: root.marker_font_size font_size: root.marker_font_size
color: SCORE_MARKER_COLOR color: Theme.SCORE_MARKER_COLOR
pos: root.x + root.width - self.width-1, root.pos[1] pos: root.x + root.width - self.width-1, root.pos[1]
text: 'W+' + str(int(root.score_scale)) text: 'W+' + str(int(root.score_scale))
opacity: int(root.show_score) opacity: int(root.show_score)
# wr ticks # wr ticks
GraphMarkerLabel: GraphMarkerLabel:
font_size: root.marker_font_size font_size: root.marker_font_size
color: WINRATE_MARKER_COLOR color: Theme.WINRATE_MARKER_COLOR
pos: root.pos[0]+1, root.pos[1] + root.height - self.font_size - 1 pos: root.pos[0]+1, root.pos[1] + root.height - self.font_size - 1
text: "{}%".format(50 + root.winrate_scale) text: "{}%".format(50 + root.winrate_scale)
opacity: int(root.show_winrate) opacity: int(root.show_winrate)
GraphMarkerLabel: GraphMarkerLabel:
font_size: root.marker_font_size font_size: root.marker_font_size
color: WINRATE_MARKER_COLOR color: Theme.WINRATE_MARKER_COLOR
pos:root.pos[0]+1, root.pos[1] pos:root.pos[0]+1, root.pos[1]
text: "{}%".format(50 - root.winrate_scale) text: "{}%".format(50 - root.winrate_scale)
opacity: int(root.show_winrate) opacity: int(root.show_winrate)
+23 -18
View File
@@ -11,7 +11,7 @@ from kivy.uix.widget import Widget
from kivymd.app import MDApp from kivymd.app import MDApp
from katrain.gui.kivyutils import BackgroundMixin, draw_circle, draw_text from katrain.gui.kivyutils import BackgroundMixin, draw_circle, draw_text
from katrain.gui.style import GREEN, LIGHT_ORANGE, LIGHTGREY, ORANGE, RED, STONE_COLORS, STONE_TEXT_COLORS, YELLOW from katrain.gui.theme import Theme
class MoveTreeDropdown(DropDown): class MoveTreeDropdown(DropDown):
@@ -155,8 +155,8 @@ class MoveTreeCanvas(Widget):
stack += children[move][::-1] # stack, so push top child last to process first stack += children[move][::-1] # stack, so push top child last to process first
def draw_stone(pos, player, special_color=None): def draw_stone(pos, player, special_color=None):
draw_circle(pos, self.move_size / 2 - 0.5, (special_color or STONE_COLORS[player])) draw_circle(pos, self.move_size / 2 - 0.5, (special_color or Theme.STONE_COLORS[player]))
Color(*STONE_TEXT_COLORS[player]) Color(*Theme.MOVE_TREE_STONE_OUTLINE_COLORS[player])
Line(circle=(*pos, self.move_size / 2), width=1) Line(circle=(*pos, self.move_size / 2), width=1)
def coord_pos(coord): def coord_pos(coord):
@@ -170,18 +170,20 @@ class MoveTreeCanvas(Widget):
self.move_xy_pos = {n: xy_pos(x, y) for n, (x, y) in self.move_pos.items()} self.move_xy_pos = {n: xy_pos(x, y) for n, (x, y) in self.move_pos.items()}
special_nodes = {current_node: YELLOW, self.menu_selected_node: RED} special_nodes = {current_node: Theme.MOVE_TREE_CURRENT, self.menu_selected_node: Theme.MOVE_TREE_SELECTED}
if insert_node: if insert_node:
special_nodes[insert_node.parent] = GREEN special_nodes[insert_node.parent] = Theme.MOVE_TREE_INSERT_NODE_PARENT
insert_path = current_node insert_path = current_node
while insert_path != insert_node.parent and insert_path.parent: while insert_path != insert_node.parent and insert_path.parent:
special_nodes[insert_path] = ORANGE if insert_path == current_node else LIGHT_ORANGE special_nodes[insert_path] = (
Theme.MOVE_TREE_INSERT_CURRENT if insert_path == current_node else Theme.MOVE_TREE_INSERT_OTHER
)
insert_path = insert_path.parent insert_path = insert_path.parent
with self.canvas: with self.canvas:
self.canvas.clear() self.canvas.clear()
Color(*LIGHTGREY) Color(*Theme.MOVE_TREE_LINE)
for node, (x, y) in self.move_xy_pos.items(): for node, (x, y) in self.move_xy_pos.items():
for ci, c in enumerate(children[node]): for ci, c in enumerate(children[node]):
cx, cy = self.move_xy_pos[c] cx, cy = self.move_xy_pos[c]
@@ -194,10 +196,10 @@ class MoveTreeCanvas(Widget):
pos=[c - self.move_size / 2 - spacing / 2 for c in self.move_xy_pos[node]], pos=[c - self.move_size / 2 - spacing / 2 for c in self.move_xy_pos[node]],
size=(self.move_size + spacing, self.move_size + spacing), size=(self.move_size + spacing, self.move_size + spacing),
) )
collapsed_color = LIGHTGREY if node.shortcut_from else None collapsed_color = Theme.MOVE_TREE_COLLAPSED if node.shortcut_from else None
draw_stone(pos, node.player, collapsed_color) draw_stone(pos, node.player, collapsed_color)
text = str(node.depth) text = str(node.depth)
Color(*STONE_COLORS["W" if node.player == "B" and not node.shortcut_from else "B"]) Color(*Theme.STONE_COLORS["W" if node.player == "B" and not node.shortcut_from else "B"])
draw_text(pos=pos, text=text, font_size=self.move_size * 1.75 / (1 + 1 * len(text)), font_name="Roboto") draw_text(pos=pos, text=text, font_size=self.move_size * 1.75 / (1 + 1 * len(text)), font_name="Roboto")
if current_node in self.move_xy_pos: if current_node in self.move_xy_pos:
@@ -245,8 +247,11 @@ class MoveTree(ScrollView, BackgroundMixin):
Builder.load_string( Builder.load_string(
""" """
#:import Theme katrain.gui.theme.Theme
#:import WHITE katrain.gui.theme.WHITE
#:import LIGHT_GREY katrain.gui.theme.LIGHT_GREY
<MoveTree>: <MoveTree>:
background_color: BOX_BACKGROUND_COLOR background_color: Theme.BOX_BACKGROUND_COLOR
move_tree_canvas: move_tree_canvas move_tree_canvas: move_tree_canvas
scroll_distance: 0 # scroll wheel is for forward/backward scroll_distance: 0 # scroll wheel is for forward/backward
MoveTreeCanvas: MoveTreeCanvas:
@@ -261,7 +266,7 @@ Builder.load_string(
Line: Line:
points: self.x, self.y, self.x, self.y+self.height points: self.x, self.y, self.x, self.y+self.height
Color: Color:
rgba: LIGHTGREY rgba: LIGHT_GREY
Line Line
points: self.x,self.y,self.x+self.width,self.y points: self.x,self.y,self.x+self.width,self.y
width: 1 width: 1
@@ -270,27 +275,27 @@ Builder.load_string(
katrain: app.gui katrain: app.gui
MoveTreeDropdownItem: MoveTreeDropdownItem:
text: i18n._("Delete Node") text: i18n._("Delete Node")
icon: 'img/delete.png' icon: 'delete.png'
shortcut: 'Ctrl+Del' shortcut: 'Ctrl+Del'
on_action: root.katrain.controls.move_tree.delete_selected_node() on_action: root.katrain.controls.move_tree.delete_selected_node()
-background_color: LIGHTER_BACKGROUND_COLOR -background_color: Theme.LIGHTER_BACKGROUND_COLOR
-height: dp(45) -height: dp(45)
-width_margin: 1.6 -width_margin: 1.6
MoveTreeDropdownItem: MoveTreeDropdownItem:
text: i18n._("Make Main Branch") text: i18n._("Make Main Branch")
icon: 'img/Branch.png' icon: 'Branch.png'
shortcut: 'PgUp' shortcut: 'PgUp'
on_action: root.katrain.controls.move_tree.make_selected_node_main_branch() on_action: root.katrain.controls.move_tree.make_selected_node_main_branch()
-background_color: LIGHTER_BACKGROUND_COLOR -background_color: Theme.LIGHTER_BACKGROUND_COLOR
-height: dp(45) -height: dp(45)
-width_margin: 1.6 -width_margin: 1.6
MoveTreeDropdownItem: MoveTreeDropdownItem:
text: i18n._("Toggle Collapse Branch") text: i18n._("Toggle Collapse Branch")
icon: 'img/Collapse.png' icon: 'Collapse.png'
shortcut: 'c' shortcut: 'c'
on_action: root.katrain.controls.move_tree.toggle_selected_node_collapse() on_action: root.katrain.controls.move_tree.toggle_selected_node_collapse()
-background_color: LIGHTER_BACKGROUND_COLOR -background_color: Theme.LIGHTER_BACKGROUND_COLOR
-height: dp(45) -height: dp(45)
-width_margin: 1.6 -width_margin: 1.6
""" """
) )
File renamed without changes.
File renamed without changes.
+33 -37
View File
@@ -4,16 +4,12 @@
#:import abspath os.path.abspath #:import abspath os.path.abspath
#:import kivy_version kivy.__version__ #:import kivy_version kivy.__version__
#:set CP_SPACING 6 #:import Theme katrain.gui.theme.Theme
#:set CP_SMALL_SPACING 3 #:import LIGHT_GREY katrain.gui.theme.LIGHT_GREY
#:set CP_PADDING 6
#:set INPUT_FONT_SIZE sp(25) #:set CP_SPACING Theme.CP_SPACING
#:set DESC_FONT_SIZE sp(20) #:set CP_SMALL_SPACING Theme.CP_SMALL_SPACING
#:set CP_PADDING Theme.CP_PADDING
#:import BACKGROUND_COLOR katrain.gui.style.BACKGROUND_COLOR
#:import LIGHTGREY katrain.gui.style.LIGHTGREY
#:import TEXT_COLOR katrain.gui.style.TEXT_COLOR
#:import TOP_MOVE_OPTIONS katrain.core.constants.TOP_MOVE_OPTIONS #:import TOP_MOVE_OPTIONS katrain.core.constants.TOP_MOVE_OPTIONS
@@ -22,15 +18,15 @@
# hint_text: "Helper text on focus" # hint_text: "Helper text on focus"
# helper_text: "This will disappear when you click off" # helper_text: "This will disappear when you click off"
# helper_text_mode: "on_focus" # helper_text_mode: "on_focus"
# font_size: INPUT_FONT_SIZE # font_size: Theme.INPUT_FONT_SIZE
<LabelledTextInput>: <LabelledTextInput>:
font_size: INPUT_FONT_SIZE font_size: Theme.INPUT_FONT_SIZE
helper_text_mode: "none" helper_text_mode: "none"
hint_text: "" hint_text: ""
helper_text: "" helper_text: ""
color_mode: 'custom' color_mode: 'custom'
line_color_focus: TEXT_COLOR line_color_focus: Theme.TEXT_COLOR
<LabelledPathInput>: <LabelledPathInput>:
font_size: sp(16) font_size: sp(16)
@@ -39,27 +35,27 @@
helper_text: i18n._("file not found") helper_text: i18n._("file not found")
<LabelledIntInput>: <LabelledIntInput>:
font_size: INPUT_FONT_SIZE font_size: Theme.INPUT_FONT_SIZE
size_hint: 0.5, None size_hint: 0.5, None
<LabelledFloatInput>: <LabelledFloatInput>:
font_size: INPUT_FONT_SIZE font_size: Theme.INPUT_FONT_SIZE
size_hint: 0.5, None size_hint: 0.5, None
<LabelledCheckBox>: <LabelledCheckBox>:
size: DESC_FONT_SIZE*2,DESC_FONT_SIZE*2 size: Theme.DESC_FONT_SIZE*2,Theme.DESC_FONT_SIZE*2
size_hint: None,None size_hint: None,None
<QuickInputButton@SizedRectangleButton> <QuickInputButton@SizedRectangleButton>
on_left_press: if self.target: self.target.text = self.text on_left_press: if self.target: self.target.text = self.text
size: DESC_FONT_SIZE * 2, DESC_FONT_SIZE * 2 size: Theme.DESC_FONT_SIZE * 2, Theme.DESC_FONT_SIZE * 2
font_size: DESC_FONT_SIZE font_size: Theme.DESC_FONT_SIZE
<I18NPopup>: <I18NPopup>:
background_color: BACKGROUND_COLOR background_color: Theme.BACKGROUND_COLOR
background: self.background if kivy_version < '2.0' else '' background: self.background if kivy_version < '2.0' else ''
title: i18n._(self.title_key) title: i18n._(self.title_key)
title_size: DESC_FONT_SIZE title_size: Theme.DESC_FONT_SIZE
title_font: self.font_name title_font: self.font_name
size_hint: None, None size_hint: None, None
@@ -69,14 +65,14 @@
padding: 4*CP_PADDING,CP_PADDING, 4*CP_PADDING, CP_PADDING padding: 4*CP_PADDING,CP_PADDING, 4*CP_PADDING, CP_PADDING
<DescriptionLabel>: <DescriptionLabel>:
font_size: DESC_FONT_SIZE font_size: Theme.DESC_FONT_SIZE
multiline: True multiline: True
text_size: self.width, None text_size: self.width, None
halign: 'center' halign: 'center'
valign: 'center' valign: 'center'
<SmallDescriptionLabel@DescriptionLabel> <SmallDescriptionLabel@DescriptionLabel>
font_size: DESC_FONT_SIZE * 0.66 font_size: Theme.DESC_FONT_SIZE * 0.66
<ConfigPopup>: <ConfigPopup>:
configfile: configfile configfile: configfile
@@ -88,7 +84,7 @@
katago_download_progress_box: katago_download_progress_box katago_download_progress_box: katago_download_progress_box
DescriptionLabel: DescriptionLabel:
text: i18n._('katago settings') text: i18n._('katago settings')
font_size: DESC_FONT_SIZE * 1.5 font_size: Theme.DESC_FONT_SIZE * 1.5
GridLayout: GridLayout:
cols: 2 cols: 2
rows: 6 rows: 6
@@ -123,7 +119,7 @@
size_hint_y: 0.7 size_hint_y: 0.7
sync_height_frac: 1.0 sync_height_frac: 1.0
-font_size: self.height * 0.5 -font_size: self.height * 0.5
-background_color: [*[c*255/88 for c in BOX_BACKGROUND_COLOR[:3]], 1] # compensate for texture -background_color: [*[c*255/88 for c in Theme.BOX_BACKGROUND_COLOR[:3]], 1] # compensate for texture
DescriptionLabel: DescriptionLabel:
text: i18n._("engine:config") text: i18n._("engine:config")
size_hint: 0.33, 1 size_hint: 0.33, 1
@@ -158,7 +154,7 @@
size_hint_y: 0.7 size_hint_y: 0.7
sync_height_frac: 1.0 sync_height_frac: 1.0
-font_size: self.height * 0.5 -font_size: self.height * 0.5
-background_color: [*[c*255/88 for c in BOX_BACKGROUND_COLOR[:3]], 1] # compensate for texture -background_color: [*[c*255/88 for c in Theme.BOX_BACKGROUND_COLOR[:3]], 1] # compensate for texture
DescriptionLabel: DescriptionLabel:
text: i18n._("engine:altcommand") text: i18n._("engine:altcommand")
size_hint: 0.33, 1 size_hint: 0.33, 1
@@ -171,9 +167,9 @@
orientation: 'horizontal' orientation: 'horizontal'
DescriptionLabel: DescriptionLabel:
text: i18n._('general settings') text: i18n._('general settings')
font_size: DESC_FONT_SIZE * 1.5 font_size: Theme.DESC_FONT_SIZE * 1.5
DescriptionLabel: DescriptionLabel:
font_size: DESC_FONT_SIZE * 1.5 font_size: Theme.DESC_FONT_SIZE * 1.5
text: i18n._('engine settings') text: i18n._('engine settings')
BoxLayout: BoxLayout:
size_hint: 1,5 size_hint: 1,5
@@ -206,7 +202,7 @@
id: configfile id: configfile
text: app.gui.config_file text: app.gui.config_file
BackgroundMixin: BackgroundMixin:
background_color: LIGHTGREY background_color: LIGHT_GREY
size_hint: None, 1 size_hint: None, 1
width: 2 width: 2
GridLayout: GridLayout:
@@ -281,7 +277,7 @@
padding: 4 padding: 4
size_hint: 0.95, 1 size_hint: 0.95, 1
DescriptionLabel: DescriptionLabel:
font_size: DESC_FONT_SIZE * 0.8 font_size: Theme.DESC_FONT_SIZE * 0.8
text: i18n._("theme") text: i18n._("theme")
AnchorLayout: AnchorLayout:
LabelledSpinner: LabelledSpinner:
@@ -289,7 +285,7 @@
input_property: 'trainer/theme' input_property: 'trainer/theme'
id: themes_spinner id: themes_spinner
DescriptionLabel: DescriptionLabel:
font_size: DESC_FONT_SIZE * 0.8 font_size: Theme.DESC_FONT_SIZE * 0.8
text: i18n._("stats on top move") text: i18n._("stats on top move")
AnchorLayout: AnchorLayout:
BoxLayout: BoxLayout:
@@ -307,19 +303,19 @@
value_refs: TOP_MOVE_OPTIONS value_refs: TOP_MOVE_OPTIONS
input_property: "trainer/top_moves_show_secondary" input_property: "trainer/top_moves_show_secondary"
DescriptionLabel: DescriptionLabel:
font_size: DESC_FONT_SIZE * 0.8 font_size: Theme.DESC_FONT_SIZE * 0.8
text: i18n._("show stats if") text: i18n._("show stats if")
AnchorLayout: AnchorLayout:
LabelledIntInput: LabelledIntInput:
input_property: "trainer/low_visits" input_property: "trainer/low_visits"
DescriptionLabel: DescriptionLabel:
font_size: DESC_FONT_SIZE * 0.8 font_size: Theme.DESC_FONT_SIZE * 0.8
text: i18n._("show ai dots") text: i18n._("show ai dots")
AnchorLayout: AnchorLayout:
LabelledCheckBox: LabelledCheckBox:
input_property: "trainer/eval_show_ai" input_property: "trainer/eval_show_ai"
DescriptionLabel: DescriptionLabel:
font_size: DESC_FONT_SIZE * 0.8 font_size: Theme.DESC_FONT_SIZE * 0.8
text: i18n._("cache analysis to sgf") text: i18n._("cache analysis to sgf")
AnchorLayout: AnchorLayout:
LabelledCheckBox: LabelledCheckBox:
@@ -472,7 +468,7 @@
LabelledSpinner: LabelledSpinner:
size_hint: 0.8,0.8 size_hint: 0.8,0.8
input_property: 'game/rules' input_property: 'game/rules'
font_size: DESC_FONT_SIZE font_size: Theme.DESC_FONT_SIZE
id: rules_spinner id: rules_spinner
Label: Label:
DescriptionLabel: DescriptionLabel:
@@ -509,7 +505,7 @@
size_hint: 2.25,1 size_hint: 2.25,1
values: root.values values: root.values
on_change: textbox.text = str(slider.value) on_change: textbox.text = str(slider.value)
track_color: LIGHTGREY track_color: LIGHT_GREY
thumb_color: (0.6, 0.7, 0.9, 1) if root.key_option else (0.3, 0.4, 0.6, 1) thumb_color: (0.6, 0.7, 0.9, 1) if root.key_option else (0.3, 0.4, 0.6, 1)
LabelledFloatInput: LabelledFloatInput:
id: textbox id: textbox
@@ -522,7 +518,7 @@
BoxLayout: BoxLayout:
size_hint: 1,1 size_hint: 1,1
DescriptionLabel: DescriptionLabel:
font_size: DESC_FONT_SIZE font_size: Theme.DESC_FONT_SIZE
text: i18n._('Select AI') text: i18n._('Select AI')
AnchorLayout: AnchorLayout:
I18NSpinner: I18NSpinner:
@@ -531,12 +527,12 @@
BoxLayout: BoxLayout:
size_hint: 1,1 size_hint: 1,1
DescriptionLabel: DescriptionLabel:
font_size: DESC_FONT_SIZE font_size: Theme.DESC_FONT_SIZE
size_hint: 1, 1 size_hint: 1, 1
text: i18n._('estimated strength') text: i18n._('estimated strength')
DescriptionLabel: DescriptionLabel:
size_hint: 0.8, 1 size_hint: 0.8, 1
font_size: DESC_FONT_SIZE font_size: Theme.DESC_FONT_SIZE
id: estimated_rank_label id: estimated_rank_label
text: '?' + i18n._('strength:kyu') text: '?' + i18n._('strength:kyu')
SmallDescriptionLabel: SmallDescriptionLabel: