This commit is contained in:
Sander Land committed 2020-05-27 17:09:47 +02:00
1 parent b062d78dba
commit 01f0663ac1
22 files changed
+584 -166

No files matched your search

Generated
+9 -9
View File
@@ -112,17 +112,17 @@
},
"more-itertools": {
"hashes": [
"sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c",
"sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"
"sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be",
"sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982"
],
"version": "==8.2.0"
"version": "==8.3.0"
},
"packaging": {
"hashes": [
"sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3",
"sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752"
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
"version": "==20.3"
"version": "==20.4"
},
"pluggy": {
"hashes": [
@@ -155,10 +155,10 @@
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"version": "==1.14.0"
"version": "==1.15.0"
},
"wcwidth": {
"hashes": [
+5 -5
View File
@@ -102,10 +102,10 @@ Keyboard shortcuts are shown with **[key]**.
* **[w]**: All dots: Show all evaluation dots instead of the last few.
* You can configure how many are shown with this setting off, and whether they are shown for AIs under 'Play/Configure Teacher'.
* **[e]**: Top moves: Show the next moves KataGo considered, colored by their expected point loss. Small dots indicate high uncertainty. Hover over any of them to see the principal variation.
* **[r]**: Show owner: Show expected ownership of each intersection.
* **[t]**: NN Policy: Show KataGo's policy network evaluation, i.e. where it thinks the best next move is purely from the position, and in the absence of any 'reading'.
* **[r]**: NN Policy: Show KataGo's policy network evaluation, i.e. where it thinks the best next move is purely from the position, and in the absence of any 'reading'.
* **[t]**: Show owner: Show expected ownership of each intersection.
* The analysis buttons are used for:
* The analysis options are used for deeper evaluation of the position:
* **[a]**: Extra: Re-evaluate the position using more visits, usually resulting in a more accurate evaluation.
* **[s]**: Equalize: Re-evaluate all currently shown next moves with the same visits as the current top move. Useful to increase confidence in the suggestions with high uncertainty.
* **[d]**: Sweep: Evaluate all possible next moves. This can take a bit of time even though 'fast_visits' is used, but the result is nothing if not colourful.
@@ -114,7 +114,7 @@ Keyboard shortcuts are shown with **[key]**.
In addition to shortcuts mentioned above, there are:
* **[Tab]**: to switch between analysis and play modes. (NB. keyboard shortcuts function regardless)
* **[Tab]**: to switch between analysis and play modes.
* **[~]** or **[`]** or **[m]**: Hide side panel UI and only show the board.
* **[enter]**: AI Move
* **[p]**: Pass
@@ -130,7 +130,7 @@ In addition to shortcuts mentioned above, there are:
* **[Ctrl-l]**: Load SGF from file and do a normal analysis.
* **[Ctrl-s]**: Save SGF with automated review to file.
* **[Ctrl-n]**: Load SGF from clipboard
* **[F5]**: Open settings
## Configuration
+258
View File
@@ -0,0 +1,258 @@
import kivy
from kivy.app import App
from kivy.core.text import Label as CoreLabel
from kivy.lang import Builder
from kivy.properties import (
BooleanProperty,
ListProperty,
NumericProperty,
ObjectProperty,
OptionProperty,
StringProperty,
)
from kivy.uix.behaviors import ButtonBehavior, ToggleButtonBehavior
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.spinner import Spinner
from kivy.uix.widget import Widget
from kivymd.app import MDApp
from kivymd.uix.behaviors import CircularRippleBehavior, RectangularRippleBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import BaseFlatButton, BasePressedButton
from kivymd.uix.navigationdrawer import MDNavigationDrawer
WHITE = [1, 1, 1, 1]
class TestBox(GridLayout):
pass
class BackgroundMixin(Widget):
background_color = ListProperty([0, 0, 0, 0])
background_radius = NumericProperty(0)
outline_color = ListProperty([0, 0, 0, 0])
outline_width = NumericProperty(1)
background_radius = NumericProperty(0)
class LeftButtonBehavior(ButtonBehavior): # stops buttons etc activating on right click
def __init__(self, **kwargs):
self.register_event_type("on_left_release")
self.register_event_type("on_left_press")
super().__init__(**kwargs)
def on_touch_down(self, touch):
return super().on_touch_down(touch)
def on_release(self):
if not self.last_touch or self.last_touch.button == "left":
self.dispatch("on_left_release")
return super().on_release()
def on_press(self):
if not self.last_touch or self.last_touch.button == "left":
self.dispatch("on_left_press")
return super().on_press()
def on_left_release(self):
pass
def on_left_press(self):
pass
# -- resizeable buttons
class SizedButton(LeftButtonBehavior, RectangularRippleBehavior, BasePressedButton, BaseFlatButton, BackgroundMixin): # avoid baserectangular for sizing
text = StringProperty("")
text_color = ListProperty(WHITE)
text_size = ListProperty([100, 100])
halign = OptionProperty("center", options=["left", "center", "right", "justify", "auto"])
label = ObjectProperty(None)
padding_x = NumericProperty(6)
padding_y = NumericProperty(0)
fsz = NumericProperty(None)
class AutoSizedButton(SizedButton):
pass
class ToggleButtonMixin(ToggleButtonBehavior):
inactive_outline_color = ListProperty([0.5, 0.5, 0.5, 0])
active_outline_color = ListProperty([1, 1, 1, 0])
inactive_background_color = ListProperty([0.5, 0.5, 0.5, 1])
active_background_color = ListProperty([1, 1, 1, 1])
class SizedRectangleButton(SizedButton):
pass
class SizedRectangleToggleButton(ToggleButtonMixin, SizedRectangleButton):
pass
class AutoSizedRectangleButton(AutoSizedButton):
pass
class AutoSizedRectangleToggleButton(ToggleButtonMixin, AutoSizedRectangleButton):
pass
class TransparentIconButton(CircularRippleBehavior, Button):
icon_size = ListProperty([25, 25])
icon = StringProperty("")
KV = """
#:set LIGHTGREY [0.7,0.7,0.7,1]
#:set WHITE [1,1,1,1]
#:set BUTTON_INACTIVE_COLOR LIGHTGREY
#:set BACKGROUND_COLOR [16/255,18/255,32/255,1]
#:set BOX_BACKGROUND_COLOR [46/255,65/255,88/255,1]
#:set TEXT_COLOR WHITE
<Label>:
canvas.before:
Color:
rgba: [1,0,0,1]
Line:
rectangle: (*self.pos,*self.size)
width: 1
# mixins
<BackgroundMixin>:
canvas.before:
Color:
rgba: root.background_color
RoundedRectangle:
size: self.size
pos: self.pos
radius: [root.background_radius, ]
Color:
rgba: root.outline_color
Line:
rounded_rectangle: (*self.pos,self.width,self.height,root.background_radius)
width: root.outline_width
<SizedButton>:
ripple_duration_in_slow: 0.6
text_color: WHITE
background_color: BOX_BACKGROUND_COLOR
label: label
text_size: root.size
onlytext: ''
on_press: print('size: {}\\ntexture_size: {}\\ntext_size: {} - font {} - {} - {} ?\\npos: {} - halign {}'.format(label.size,label.texture_size,label.text_size,label.font_size,root.font_size,0.6 * self.height / len(self.label.text.split('\\n')),label.pos,label.halign),self.label.text.split('\\n'))
Label:
id: label
padding: [root.padding_x,root.padding_y]
color: root.text_color
font_size: 0.6 * self.height / len(self.text.split('\\n')) if root.fsz is None else root.fsz
on_font_size: print(args,self.font_size,self.text)
halign: root.halign
text: 'size: {}\\ntexture_size: {}\\ntext_size: {}\\npos: {}halign {}\\n{}'.format(self.size,self.texture_size,self.text_size,self.pos,self.halign,root.text) if not root.onlytext else root.onlytext
<AutoSizedButton>:
width: root.label.texture_size[0]
<SizedToggleButton>
inactive_background_color: BACKGROUND_COLOR
active_background_color: BOX_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
<SizedRectangleButton>:
outline_color: WHITE
text_color: self.outline_color
<SizedRectangleToggleButton>:
inactive_outline_color: BUTTON_INACTIVE_COLOR
active_outline_color: WHITE
text_color: self.outline_color
<AutoSizedRectangleButton>: # only wants height
outline_color: WHITE
text_color: self.outline_color
<AutoSizedRoundedRectangleButton@AutoSizedRectangleButton>:
background_radius: self.height/3.5
halign: 'left'
<PassBtn@AutoSizedRoundedRectangleButton>:
id: pass_btn
onlytext: 'Pass'
background_radius: 10
height: 50
<BGBoxLayout@BoxLayout+BackgroundMixin>
background_color: BACKGROUND_COLOR
padding: 100
<TransparentIconButton>:
background_normal: ''
background_color: (0,0,0,0)
image: image
Image:
id: image
size: root.icon_size
pos: [root.pos[i] + (root.size[i] - root.icon_size[i])/2 for i in [0,1]]
source: root.icon
mipmap: True
<TestBox>:
canvas.before:
Color:
rgba: [0,1,0,1]
Line:
rectangle: (*self.pos,*self.size)
width: 1
cols: 2
rows: 3
BGBoxLayout:
SizedButton:
text: 'SizedButton'
height: 125
width: 300
BGBoxLayout:
AutoSizedRectangleButton:
text: 'AutoSizedRectangleButton'
size_hint_y: None
height: 150
BGBoxLayout:
SizedRectangleButton:
text: 'SizedRectangleButton'
height: 125
width: 300
halign: 'left'
background_radius: 25
BGBoxLayout:
AutoSizedRoundedRectangleButton:
text: 'AutoSizedRectangleButton'
halign: 'left'
size_hint_y: None
height: 150
BGBoxLayout:
PassBtn:
"""
Builder.load_string(KV)
class MyApp(MDApp):
def __init__(self):
super().__init__()
def build(self):
layout = TestBox()
return layout
MyApp().run()
View File
File renamed without changes.
+64
View File
@@ -0,0 +1,64 @@
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
class TestBox(GridLayout):
pass
KV = """
<Label>:
valign: 'top'
halign: 'right'
xtext: ''
text: 'size: {}\\ntexture_size: {}\\ntext_size: {}\\npos: {}\\n{}'.format(self.size,self.texture_size,self.text_size,self.pos,root.xtext)
canvas.before:
Color:
rgba: [1,0,0,1]
Line:
rectangle: (*self.pos,*self.size)
width: 1
<TestBox>:
canvas.before:
Color:
rgba: [0,1,0,1]
Line:
rectangle: (*self.pos,*self.size)
width: 1
cols: 2
rows: 2
Label:
xtext: 'size=texturesize'
size: self.texture_size
Label:
xtext: 'texturesize=size'
texture_size: self.size
Label:
xtext: 'textsize=size'
text_size: self.size
Label:
xtext: 'texturesize=textsize=size'
texture_size: self.size
text_size: self.size
"""
Builder.load_string(KV)
class MyApp(App):
def build(self):
layout = TestBox()
return layout
MyApp().run()
+9
View File
@@ -0,0 +1,9 @@
from datetime import datetime
import yappi
from katrain.__main__ import run_app
yappi.start()
run_app()
yappi.get_func_stats().save("callgrind.out." + datetime.now().isoformat(), "CALLGRIND")
View File
File renamed without changes.
+15 -7
View File
@@ -204,6 +204,10 @@ class KaTrainGui(Screen):
ai_move(self.game, mode, settings)
def _do_undo(self, n_times=1):
if n_times == "smart":
n_times = 1
if self.play_analyze_mode == "play" and self.game.last_player.ai and self.game.next_player.human:
n_times = 2
self.board_gui.animating_pv = None
self.game.undo(n_times)
@@ -299,15 +303,14 @@ class KaTrainGui(Screen):
self("undo")
return super().on_touch_up(touch)
def _on_keyboard_down(self, _keyboard, keycode, _text, modifiers):
if isinstance(App.get_running_app().root_window.children[0], Popup) or self.controls.note.focus:
return # if in new game or load, don't allow keyboard shortcuts
shortcuts = {
@property
def shortcuts(self):
return {
"q": self.analysis_controls.show_children,
"w": self.analysis_controls.eval,
"e": self.analysis_controls.hints,
"r": self.analysis_controls.ownership,
"t": self.analysis_controls.policy,
"t": self.analysis_controls.ownership,
"r": self.analysis_controls.policy,
"enter": ("ai-move",),
"a": ("analyze-extra", "extra"),
"s": ("analyze-extra", "equalize"),
@@ -316,6 +319,11 @@ class KaTrainGui(Screen):
"right": ("switch-branch", 1),
"left": ("switch-branch", -1),
}
def _on_keyboard_down(self, _keyboard, keycode, _text, modifiers):
if isinstance(App.get_running_app().root_window.children[0], Popup) or self.controls.note.focus:
return # if in new game or load, don't allow keyboard shortcuts
shortcuts = self.shortcuts
if keycode[1] in shortcuts.keys():
shortcut = shortcuts[keycode[1]]
if isinstance(shortcut, Widget):
+19 -15
View File
@@ -5,7 +5,7 @@ import threading
from datetime import datetime
from typing import Dict, List, Union
from katrain.core.utils import var_to_grid, OUTPUT_INFO, OUTPUT_DEBUG
from katrain.core.utils import var_to_grid, OUTPUT_INFO, OUTPUT_DEBUG, i18n
from katrain.core.engine import KataGoEngine
from katrain.core.game_node import GameNode
from katrain.core.sgf_parser import SGF, Move
@@ -291,32 +291,36 @@ class Game:
def analyze_extra(self, mode):
stones = {s.coords for s in self.stones}
cn = self.current_node
if not cn.analysis_ready:
self.katrain.controls.set_status("Wait for initial analysis to complete before doing a board-sweep or refinement", self.current_node)
return
engine = self.engines[cn.next_player]
if mode == "extra":
visits = cn.analysis["root"]["visits"] + engine.config["max_visits"]
visits = cn.analysis_visits_requested + engine.config["max_visits"]
self.katrain.controls.set_status(f"Performing additional analysis to {visits} visits")
cn.analyze(engine, visits=visits, priority=-1_000, time_limit=False)
return
elif mode == "sweep":
board_size_x, board_size_y = self.board_size
policy_grid = var_to_grid(self.current_node.policy, size=(board_size_x, board_size_y)) if self.current_node.policy else None
analyze_moves = sorted(
[
Move(coords=(x, y), player=cn.next_player)
for x in range(board_size_x)
for y in range(board_size_y)
if (policy_grid is None and (x, y) not in stones) or policy_grid[y][x] >= 0
],
key=lambda mv: -policy_grid[mv.coords[1]][mv.coords[0]],
)
if cn.analysis_ready:
policy_grid = var_to_grid(self.current_node.policy, size=(board_size_x, board_size_y)) if self.current_node.policy else None
analyze_moves = sorted(
[
Move(coords=(x, y), player=cn.next_player)
for x in range(board_size_x)
for y in range(board_size_y)
if (policy_grid is None and (x, y) not in stones) or policy_grid[y][x] >= 0
],
key=lambda mv: -policy_grid[mv.coords[1]][mv.coords[0]],
)
else:
analyze_moves = [Move(coords=(x, y), player=cn.next_player) for x in range(board_size_x) for y in range(board_size_y) if (x, y) not in stones]
visits = engine.config["fast_visits"]
self.katrain.controls.set_status(f"Refining analysis of entire board to {visits} visits")
priority = -1_000_000_000
else: # mode=='equalize':
if not cn.analysis_ready:
self.katrain.controls.set_status(i18n._("wait-before-equalize"), self.current_node)
return
analyze_moves = [Move.from_gtp(gtp, player=cn.next_player) for gtp, _ in cn.analysis["moves"].items()]
visits = max(d["visits"] for d in cn.analysis["moves"].values())
self.katrain.controls.set_status(f"Equalizing analysis of candidate moves to {visits} visits")
+3
View File
@@ -20,6 +20,7 @@ class GameNode(SGFNode):
self.note = ""
self.move_number = 0
self.time_used = 0
self.analysis_visits_requested = 0
self.undo_threshold = random.random() # for fractional undos, store the random threshold in the move itself for consistency
self._favourite_child = None
@@ -62,6 +63,8 @@ class GameNode(SGFNode):
# various analysis functions
def analyze(self, engine, priority=0, visits=None, time_limit=True, refine_move=None, analyze_fast=False):
if visits and not refine_move:
self.analysis_visits_requested = max(self.analysis_visits_requested, engine.config["max_visits"])
engine.request_analysis(
self, lambda result: self.set_analysis(result, refine_move), priority=priority, visits=visits, analyze_fast=analyze_fast, time_limit=time_limit, next_move=refine_move
)
+105 -62
View File
@@ -32,6 +32,11 @@
#:set NOTES_FONT_SIZE dp(18)
#<DefaultLabel@Label>:
# font_name: 'Roboto'
#<Label>:
# font_name: i18n._('LANG_FONT')
<I18NLabel>:
font_name: i18n._('LANG_FONT')
canvas.before:
@@ -45,16 +50,7 @@
font_name: i18n._('LANG_FONT')
# mixins
<OutlineColor>:
canvas.before:
Color:
rgba: root.outline_color
Line:
rectangle: (*self.pos,*self.size)
rounded_rectangle: (*self.pos,*self.size,root.background_radius,root.background_radius,root.background_radius,root.background_radius,self.height)
width: root.outline_width
<BackgroundColor>:
<BackgroundMixin>:
canvas.before:
Color:
rgba: root.background_color
@@ -62,6 +58,11 @@
size: self.size
pos: self.pos
radius: [root.background_radius, ]
Color:
rgba: root.outline_color
Line:
rounded_rectangle: (*self.pos,self.width,self.height,root.background_radius)
width: root.outline_width or 1
<ToolTipLabel>: # TODO
@@ -84,45 +85,55 @@
# new widgets
<SizedMDButton>:
<SizedButton>:
ripple_duration_in_slow: 0.6
text_color: WHITE
background_color: BOX_BACKGROUND_COLOR
label: label
text_size: root.size
I18NLabel:
id: label
text: root.text
size: self.texture_size
size_hint_x: None
padding: [root.padding_x,root.padding_y]
color: root.text_color
font_size: root.font_size
font_size: 0.6 * self.height / len(self.text.split('\\n')) if root._font_size is None else root._font_size
halign: root.halign
text: root.text
<SizedMDToggleButton>
<AutoSizedButton>:
width: root.label.texture_size[0]
<ToggleButtonMixin>:
inactive_background_color: BACKGROUND_COLOR
active_background_color: BOX_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
<SizedMDRectangleButton>:
<SizedRectangleButton>:
outline_color: WHITE
text_color: self.outline_color
<SizedMDRectangleToggleButton>:
<AutoSizedRectangleButton>:
outline_color: WHITE
text_color: self.outline_color
<SizedRectangleToggleButton>:
inactive_outline_color: BUTTON_INACTIVE_COLOR
active_outline_color: WHITE
text_color: self.outline_color
<AutoSizedMDRectangleButton>: # only wants height
width: self.label.texture_size[0] + self.hor_padding * 2
font_size: 0.7 * self.height / len(self.label.text.split('\n'))
<AutoSizedRectangleToggleButton>:
inactive_outline_color: BUTTON_INACTIVE_COLOR
active_outline_color: WHITE
text_color: self.outline_color
<AutoSizedMDRectangleToggleButton>: # only wants height
width: self.label.texture_size[0] + self.hor_padding * 2
font_size: 0.7 * self.height / len(self.label.text.split('\n'))
<SizedRoundedRectangleButton@SizedRectangleButton>:
background_radius: self.height/3.5
<AutoSizedRoundedRectangleButton@AutoSizedRectangleButton>:
background_radius: self.height/3.5
<BGMDBoxLayout@MDBoxLayout+BackgroundColor>:
<BGMDBoxLayout@MDBoxLayout+BackgroundMixin>:
background_color: BOX_BACKGROUND_COLOR
<TransparentIconButton>:
@@ -251,7 +262,9 @@
Label:
id: lbl
text: root.text
text_size: None, root.min_size
bold: True
valign: 'center'
color: BLACK if root.player=='White' else WHITE
pos: (image.x, image.y)
size: image.size
@@ -275,10 +288,9 @@
mid_circles_container: mid_circles_container
pass_btn: pass_btn
adaptive_height: True
AutoSizedMDRectangleButton:
AutoSizedRoundedRectangleButton:
id: pass_btn
text: i18n._('pass-button-text')
background_radius: 10
size_hint: None, 0.5
pos_hint: {'center_x': 0.15,'center_y':0.5}
on_left_release: root.katrain("play",None)
@@ -298,7 +310,7 @@
icon: 'img/flaticon/previous.png'
on_press: root.katrain('undo')
MDFloatLayout:
size_hint: 0.065, 1
size_hint: 0.05, 1
id: mid_circles_container
CircleWithText:
size_hint: 5/8, 1
@@ -323,6 +335,7 @@
on_press: root.katrain('redo',999)
NavIconButton:
icon: 'img/flaticon/route.png'
on_press: root.katrain('switch-branch',1)
ClickableLabel:
pos_hint: {'center_x': 0.85,'center_y':0.5}
size_hint: None, 1
@@ -389,22 +402,34 @@
<Timer@BGMDBoxLayout>:
state: (30,5,1)
spacing: CP_SPACING
padding: CP_PADDING * 2
padding: CP_PADDING * 4, CP_PADDING * 2
paused: True
TimerLabel:
size_hint: 0.5,1
id: timer
halign: 'right'
valign: 'middle'
font_size: 0.85*self.size[1]
text: f"{root.state[0]//60}:{int(root.state[0] % 60):02d}"
TimerLabel:
size_hint: 0.2,1
id: periods
text: f"x{root.state[1]}"
font_size: self.width*0.45
multiline: False
valign: 'top'
MDBoxLayout:
size_hint: 0.7,1
TimerLabel:
size_hint: 0.4,1
id: timer
text_size: self.size
halign: 'right'
valign: 'middle'
font_size: 0.85*self.size[1]
text: f"{root.state[0]//60}:{int(root.state[0] % 60):02d}"
TimerLabel:
size_hint: 0.05,1
text: "x"
font_size: self.height * 0.3
text_size: self.width, self.height * 0.95
halign: 'right'
valign: 'top'
TimerLabel:
size_hint: 0.1,1
font_size: self.height * 0.35
text_size: self.size
halign: 'left'
valign: 'top'
id: periods
text: str(root.state[1])
multiline: False
MDBoxLayout:
size_hint: 0.2,1
PauseButton:
@@ -504,12 +529,18 @@
size_hint: 0.6,1
id: desc
text: root.label if not root.hidden else ''
font_size: self.size[1] * 0.8
font_size: self.size[1] * 0.7
text_size: self.size
valign: 'middle'
halign: 'center'
I18NLabel:
size_hint: 0.4,1
text: root.text if not root.hidden else ''
color: root.color
font_size: desc.font_size
text_size: self.size
valign: 'middle'
halign: 'center'
id: value
bold: True
@@ -569,6 +600,7 @@
#:set CP_SMALL_SPACING 3
#:set CP_PADDING 6
<CollapsablePanelHeader>:
canvas.after:
Color:
@@ -593,7 +625,7 @@
player_box_height: max(50, 0.075 * root.height)
timer_box_height: max(50, 0.1 * root.height)
graph_box_height: max(100, 0.2 * root.height)
stats_box_height: max(50, 0.125 * root.height)
stats_box_height: max(50, 0.15 * root.height)
tab_option_height: max(15,root.height / 30)
BoxLayout: # -- Players
size_hint_y: None
@@ -611,10 +643,19 @@
Timer:
id: timer
size_hint_x: 0.7
BoxLayout:
MDBoxLayout:
orientation: 'vertical'
padding: 2*CP_PADDING,CP_PADDING
spacing: CP_SPACING
size_hint_x: 0.3
Label:
text: '??'
SizedRoundedRectangleButton:
text: i18n._('pass-button-text')
size_hint: 1, 0.5
on_left_release: root.katrain("play",None)
SizedRoundedRectangleButton:
text: i18n._('undo-button-text')
size_hint: 1, 0.5
on_left_release: root.katrain("undo","smart")
CollapsablePanel:
height_open: root.graph_box_height
closed_label: 'closedlabel:scoregraph'
@@ -670,11 +711,12 @@
size_hint_y: 1e-9 # takes up remaining space if info is closed
<AnalysisToggle@MDBoxLayout>:
<AnalysisToggle>:
adaptive_width: True
spacing: CP_SPACING
size_hint: None, 0.7
pos_hint: {'center_y':0.5}
checkbox: checkbox
I18NLabel:
id: label
text: root.text
@@ -688,7 +730,7 @@
size_hint: None,0.5
pos_hint: {'center_y': 0.5}
width: self.height
active: root.active
active: root.default_active
selected_color: WHITE
unselected_color: LIGHTGREY
on_active: if app.gui: app.gui.update_state()
@@ -733,20 +775,20 @@
AnalysisToggle:
id: show_children
text: i18n._("analysis:nextmoves")
active: True
default_active: True
AnalysisToggle:
id: eval
text: i18n._("analysis:dots")
AnalysisToggle:
id: ownership
text: i18n._('analysis:territory')
id: hints
text: i18n._("analysis:topmoves")
AnalysisToggle:
id: policy
text: i18n._("analysis:policy")
AnalysisToggle:
id: hints
text: 'Top\nMoves'
AutoSizedMDRectangleButton:
id: ownership
text: i18n._('analysis:territory')
AutoSizedRoundedRectangleButton:
background_color: BOX_BACKGROUND_COLOR
size_hint_y: 0.5
id: analysis_button
@@ -789,13 +831,13 @@
size_hint: 1,1
icon_size: self.height * 0.9, self.height * 0.9
<MenuLabel@I18NLabel+BackgroundColor>:
<MenuLabel@I18NLabel+BackgroundMixin>:
background_color: BACKGROUND_COLOR
font_size: self.height * 0.5
height: self.parent.item_height or 50
size_hint: 1,None
<ContentNavigationDrawer@MDBoxLayout+BackgroundColor>:
<ContentNavigationDrawer@MDBoxLayout+BackgroundMixin>:
katrain: app.gui
background_color: BOX_BACKGROUND_COLOR
orientation: 'vertical'
@@ -831,7 +873,6 @@
on_action: root.katrain('config-popup')
MenuLabel:
text: i18n._('menu:lang')
size_hint_y: None
MDBoxLayout:
size_hint: 1, None
height: 1.5 * root.item_height
@@ -844,17 +885,19 @@
LangButton:
icon: 'img/flaticon/flag-ko.png'
on_press: app.language = 'ko'
MenuLabel:
text: i18n._('menu:support')
<OutlineBox@MDBoxLayout+OutlineColor>:
<OutlineBox@MDBoxLayout+BackgroundMixin>:
<PlayAnalyzeButton@SizedMDToggleButton>:
<PlayAnalyzeButton@SizedToggleButton>:
inactive_background_color: BOX_BACKGROUND_COLOR
active_background_color: YELLOW
ripple_color: BACKGROUND_COLOR
text_color: self.inactive_background_color if self.state=='down' else self.active_background_color
size_hint: 0.5,1
font_size: self.height * 0.5
_font_size: self.height * 0.5
group: 'play_analyze'
allow_no_selection: False
+3 -3
View File
@@ -5,7 +5,7 @@ from kivymd.uix.gridlayout import MDGridLayout
from kivymd.uix.label import MDLabel
from katrain.core.utils import OUTPUT_DEBUG, OUTPUT_ERROR
from katrain.gui.kivyutils import SizedMDButton, LightHelpLabel, ScaledLightLabel, StyledSpinner
from katrain.gui.kivyutils import SizedButton, LightHelpLabel, ScaledLightLabel, StyledSpinner
from katrain.gui.popups import QuickConfigGui, ConfigPopup, InputParseError
KV = """
@@ -117,9 +117,9 @@ class ConfigAIPopupContents(QuickConfigGui):
self.options_grid = MDGridLayout(cols=2, rows=max(len(v) for v in self.settings.values()) - 1, size_hint=(1, 7.5), spacing=1) # -1 for help in 1 col
bottom_bl = MDBoxLayout(spacing=2)
self.info_label = MDLabel()
bottom_bl.add_widget(SizedMDButton(text=f"Apply", on_press=lambda _: self.update_config(False))) # raised?
bottom_bl.add_widget(SizedButton(text=f"Apply", on_press=lambda _: self.update_config(False))) # raised?
bottom_bl.add_widget(self.info_label)
bottom_bl.add_widget(SizedMDButton(text=f"Apply and Save", on_press=lambda _: self.update_config(True)))
bottom_bl.add_widget(SizedButton(text=f"Apply and Save", on_press=lambda _: self.update_config(True)))
self.add_widget(self.options_grid)
self.add_widget(bottom_bl)
self.build_ai_options(ais[0])
+14 -13
View File
@@ -9,13 +9,14 @@ from kivy.metrics import sp
from kivy.properties import ListProperty, StringProperty, NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.menu import MDDropdownMenu, RightContent
from katrain.core.utils import OUTPUT_DEBUG, evaluation_class, i18n, MODE_PLAY
from katrain.core.game import Move
from katrain.gui.kivyutils import draw_circle, draw_text, BackgroundColor
from katrain.gui.kivyutils import draw_circle, draw_text
from katrain.core.utils import var_to_grid
from kivy.core.window import Window
@@ -205,9 +206,9 @@ class BadukPanWidget(Widget):
Color(0.25, 0.25, 0.25)
coord_offset = self.grid_size * 1.5 / 2
for i in range(board_size_x):
draw_text(pos=(self.gridpos_x[i], self.gridpos_y[0] - coord_offset), text=Move.GTP_COORD[i], font_size=self.grid_size / 1.5)
draw_text(pos=(self.gridpos_x[i], self.gridpos_y[0] - coord_offset), text=Move.GTP_COORD[i], font_size=self.grid_size / 1.5, font_name="Roboto")
for i in range(board_size_y):
draw_text(pos=(self.gridpos_x[0] - coord_offset, self.gridpos_y[i]), text=str(i + 1), font_size=self.grid_size / 1.5)
draw_text(pos=(self.gridpos_x[0] - coord_offset, self.gridpos_y[i]), text=str(i + 1), font_size=self.grid_size / 1.5, font_name="Roboto")
def draw_board_contents(self, *_args):
if not (self.katrain and self.katrain.game):
@@ -402,7 +403,7 @@ class BadukPanWidget(Widget):
draw_circle(board_coords, self.stone_size, stone_color[move_player])
Color(*stone_color[opp_player])
draw_text(pos=board_coords, text=str(i + 1), font_size=self.grid_size / 1.45)
draw_text(pos=board_coords, text=str(i + 1), font_size=self.grid_size / 1.45, font_name="Roboto")
def set_animating_pv(self, pv, node):
if node is not None and (not self.animating_pv or not (self.animating_pv[0] == pv and self.animating_pv[1] == node)):
@@ -422,7 +423,7 @@ class AnalysisDropdownMenu(MDDropdownMenu):
class AnalysisControls(MDFloatLayout):
ANALYSIS_OPTIONS = ["analysis:extra", "analysis:equalize", "analysis:sweep", "analysis:aimove"]
ANALYSIS_SHORTCUTS = ["a", "s", "d", "Enter"]
ANALYSIS_SHORTCUTS = ["a", "s", "d", "enter"]
def __init__(self, **kwargs):
super().__init__(**kwargs)
@@ -434,14 +435,14 @@ class AnalysisControls(MDFloatLayout):
{"text": i18n._(text) + f" ({shortcut})"} # , "right_content_cls": AnalysisDropdownMenuRightContent(text=shortcut)}
for text, shortcut in zip(self.ANALYSIS_OPTIONS, self.ANALYSIS_SHORTCUTS)
]
self.analysis_menu = AnalysisDropdownMenu(
caller=self.analysis_button,
items=menu_items,
width_mult=5,
use_icon_item=False,
callback=lambda item: print(item, item.icon, item.text, item.children[-1].children, item.ids),
)
print(self.analysis_menu.use_icon_item)
self.analysis_menu = AnalysisDropdownMenu(caller=self.analysis_button, items=menu_items, width_mult=5, use_icon_item=False, callback=self.action)
def action(self, item):
katrain = MDApp.get_running_app().gui
shortcuts = katrain.shortcuts
for text, shortcut in zip(self.ANALYSIS_OPTIONS, self.ANALYSIS_SHORTCUTS):
if item.text.startswith(i18n._(text)):
katrain(*shortcuts[shortcut])
def open_analysis_menu(self):
while not self.analysis_menu:
+2 -2
View File
@@ -2,10 +2,10 @@ import math
from kivy.properties import ListProperty, NumericProperty, BooleanProperty
from katrain.gui.kivyutils import BackgroundColor
from katrain.gui.kivyutils import BackgroundMixin
class ScoreGraph(BackgroundColor):
class ScoreGraph(BackgroundMixin):
show_score = BooleanProperty(True)
show_winrate = BooleanProperty(True)
+41 -25
View File
@@ -9,6 +9,7 @@ from kivy.properties import BooleanProperty, ListProperty, NumericProperty, Stri
from kivy.uix.behaviors import ToggleButtonBehavior, ButtonBehavior
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.filechooser import FileChooserLayout, FileChooserListLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
@@ -31,12 +32,10 @@ from katrain.core.utils import i18n, I18NTextInput, I18NLabel
from katrain.gui.style import WHITE
class BackgroundColor(Widget):
background_color = ListProperty([1, 1, 1, 0])
class BackgroundMixin(Widget):
background_color = ListProperty([0, 0, 0, 0])
background_radius = NumericProperty(0)
class OutlineColor(Widget):
outline_color = ListProperty([1, 1, 1, 0])
outline_color = ListProperty([0, 0, 0, 0])
outline_width = NumericProperty(1)
background_radius = NumericProperty(0)
@@ -68,35 +67,46 @@ class LeftButtonBehavior(ButtonBehavior): # stops buttons etc activating on rig
# -- resizeable buttons
class SizedMDButton(LeftButtonBehavior, RectangularRippleBehavior, BasePressedButton, BaseFlatButton, BackgroundColor, OutlineColor): # avoid baserectangular for sizing
class SizedButton(LeftButtonBehavior, RectangularRippleBehavior, BasePressedButton, BaseFlatButton, BackgroundMixin): # avoid baserectangular for sizing
text = StringProperty("")
text_color = ListProperty(WHITE)
text_size = ListProperty([100, 100])
halign = OptionProperty("center", options=["left", "center", "right", "justify", "auto"])
label = ObjectProperty(None)
height = NumericProperty(33)
padding_x = NumericProperty(6)
padding_y = NumericProperty(0)
_font_size = NumericProperty(None)
class SizedMDToggleButton(ToggleButtonBehavior, SizedMDButton):
class AutoSizedButton(SizedButton):
pass
class SizedRectangleButton(SizedButton):
pass
class AutoSizedRectangleButton(AutoSizedButton):
pass
class ToggleButtonMixin(ToggleButtonBehavior):
inactive_outline_color = ListProperty([0.5, 0.5, 0.5, 0])
active_outline_color = ListProperty([1, 1, 1, 0])
inactive_background_color = ListProperty([0.5, 0.5, 0.5, 1])
active_background_color = ListProperty([1, 1, 1, 1])
class SizedMDRectangleButton(SizedMDButton):
class SizedToggleButton(ToggleButtonMixin, SizedButton):
pass
class SizedMDRectangleToggleButton(SizedMDToggleButton):
class SizedRectangleToggleButton(ToggleButtonMixin, SizedRectangleButton):
pass
class AutoSizedMDRectangleButton(SizedMDRectangleButton):
hor_padding = NumericProperty(6)
class AutoSizedMDRectangleToggleButton(SizedMDRectangleToggleButton):
hor_padding = NumericProperty(6)
class AutoSizedRectangleToggleButton(ToggleButtonMixin, AutoSizedRectangleButton):
pass
class TransparentIconButton(CircularRippleBehavior, Button):
@@ -136,7 +146,7 @@ class CircleWithText(Widget):
# -- new gui elements
class PlayerInfo(MDBoxLayout, BackgroundColor, OutlineColor):
class PlayerInfo(MDBoxLayout, BackgroundMixin):
captures = NumericProperty(0)
player = OptionProperty("Black", options=["Black", "White"])
player_type = StringProperty("Player")
@@ -146,10 +156,17 @@ class PlayerInfo(MDBoxLayout, BackgroundColor, OutlineColor):
class AnalysisToggle(MDBoxLayout):
text = StringProperty("")
active = BooleanProperty(False)
default_active = BooleanProperty(False)
def trigger_action(self, *args, **kwargs):
return self.checkbox.trigger_action(*args, **kwargs)
@property
def active(self):
return self.checkbox.active
class MainMenuItem(RectangularRippleBehavior, LeftButtonBehavior, MDBoxLayout, BackgroundColor):
class MainMenuItem(RectangularRippleBehavior, LeftButtonBehavior, MDBoxLayout, BackgroundMixin):
__events__ = ["on_action"]
icon = StringProperty("")
text = StringProperty("")
@@ -168,7 +185,7 @@ class CollapsablePanelHeader(MDBoxLayout):
pass
class CollapsablePanelTab(AutoSizedMDRectangleToggleButton):
class CollapsablePanelTab(AutoSizedRectangleToggleButton):
pass
@@ -271,13 +288,12 @@ class CollapsablePanel(MDBoxLayout):
pass
class StatsBox(MDBoxLayout, BackgroundColor):
class StatsBox(MDBoxLayout, BackgroundMixin):
winrate = StringProperty("...")
score = StringProperty("...")
points_lost = NumericProperty(None, allownone=True)
player = StringProperty("")
# --- not checked
@@ -332,7 +348,7 @@ class LightHelpLabel(ScaledLightLabel):
pass
class ScrollableLabel(ScrollView, BackgroundColor):
class ScrollableLabel(ScrollView, BackgroundMixin):
__events__ = ["on_ref_press"]
outline_color = ListProperty([0, 0, 0, 0]) # mixin not working for some reason
text = StringProperty("")
@@ -504,8 +520,8 @@ class LabelledIntInput(LabelledTextInput):
return int(self.text)
def draw_text(pos, text, **kw):
label = CoreLabel(text=text, bold=True, font_name=i18n._("Roboto"), **kw) #
def draw_text(pos, text, font_name=None, **kw):
label = CoreLabel(text=text, bold=True, font_name=font_name or i18n.font, **kw) #
label.refresh()
Rectangle(texture=label.texture, pos=(pos[0] - label.texture.size[0] / 2, pos[1] - label.texture.size[1] / 2), size=label.texture.size)
+2 -2
View File
@@ -12,7 +12,6 @@ from katrain.core.utils import OUTPUT_DEBUG, OUTPUT_ERROR
from katrain.core.engine import KataGoEngine
from katrain.core.game import Game, GameNode
from katrain.gui.kivyutils import (
BackgroundColor,
LabelledCheckBox,
LabelledFloatInput,
LabelledIntInput,
@@ -23,6 +22,7 @@ from katrain.gui.kivyutils import (
ScaledLightLabel,
StyledButton,
StyledSpinner,
BackgroundMixin,
)
@@ -215,7 +215,7 @@ class ConfigTeacherPopup(QuickConfigGui):
for i, (thr, undos, color) in enumerate(zip(thresholds, undos, colors)):
thrbox.add_widget(LabelledFloatInput(text=str(thr), input_property=f"eval_thresholds::{i}"))
thrbox.add_widget(LabelledFloatInput(text=str(undos), input_property=f"num_undo_prompts::{i}"))
thrbox.add_widget(BackgroundColor(background_color=color[:3]))
thrbox.add_widget(BackgroundMixin(background_color=color[:3]))
thrbox.add_widget(LabelledCheckBox(text=str(color[3] == 1), input_property=f"alpha::{i}"))
thrbox.add_widget(LabelledCheckBox(size_hint=(0.5, 1), text=str(self.sgf_settings["save_feedback"][i]), input_property=f"save_feedback::{i}"))
self.add_widget(thrbox)
Binary file not shown.
+11 -1
View File
@@ -29,6 +29,9 @@ msgstr "General Settings"
msgid "menu:lang"
msgstr "Language"
msgid "menu:support"
msgstr "Support or Contribute"
#. buttons at the top - should all include a line break
msgid "analysis:nextmoves"
msgstr ""
@@ -78,6 +81,10 @@ msgstr ""
"(processing queries). It turns red when the engine has crashed or fails to "
"start."
#. undo button on the right
msgid "undo-button-text"
msgstr "Undo"
#. tabs and labels on the right panel
msgid "tab:score"
msgstr "Score"
@@ -181,6 +188,9 @@ msgid "SGF Notes Hint"
msgstr "Your SGF notes for this position here."
#. misc errors and info messages
msgid "wait-before-equalize"
msgstr "Wait for initial analysis to complete before requesting a refinement."
msgid "Copied SGF to clipboard."
msgstr "Copied SGF to clipboard."
@@ -200,7 +210,7 @@ msgstr ""
msgid "Starting Kata failed"
msgstr ""
"Starting kata with command '{command}' failed with error {error}. Please "
"Starting KataGo with command '{command}' failed with error {error}. Please "
"make sure the 'katago' value under 'engine' in settings points to the "
"correct KataGo executable."
Binary file not shown.
+12 -9
View File
@@ -10,15 +10,6 @@ msgstr ""
msgid "LANG_FONT"
msgstr "fonts/NotoSansKR-Regular.otf"
msgid "English"
msgstr "영어"
msgid "Dutch"
msgstr "네덜란드어"
msgid "Korean"
msgstr "한국어"
msgid "pass-button-text"
msgstr "패스"
@@ -229,3 +220,15 @@ msgstr "Force AI Move"
#. TODO
msgid "analysis:extra"
msgstr "Deeper analysis"
#. TODO
msgid "undo-button-text"
msgstr "Undo"
#. TODO
msgid "menu:support"
msgstr "Support or Contribute"
#. TODO
msgid "wait-before-equalize"
msgstr "Wait for initial analysis to complete before requesting a refinement."
Binary file not shown.
+12 -13
View File
@@ -7,17 +7,8 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
#. This is the font used, files ONLY relative to 'katrain' directory
msgid "Roboto"
msgstr "Roboto"
msgid "English"
msgstr "Engels"
msgid "Dutch"
msgstr "Nederlands"
msgid "Korean"
msgstr "Koreaans"
msgid "LANG_FONT"
msgstr "fonts/NotoSans-Regular.ttf"
msgid "pass-button-text"
msgstr "Pas"
@@ -262,5 +253,13 @@ msgid "analysis:extra"
msgstr "Deeper analysis"
#. TODO
msgid "LANG_FONT"
msgstr "fonts/NotoSans-Regular.ttf"
msgid "undo-button-text"
msgstr "Undo"
#. TODO
msgid "menu:support"
msgstr "Support or Contribute"
#. TODO
msgid "wait-before-equalize"
msgstr "Wait for initial analysis to complete before requesting a refinement."