slider
This commit is contained in:
1 parent
94c5362a01
commit
71b1dc2d70
5 files changed
+193
-20
No files matched your search
+2
-2
@@ -65,7 +65,7 @@ from katrain.core.engine import KataGoEngine
|
||||
from katrain.core.game import Game, IllegalMoveException, KaTrainSGF
|
||||
from katrain.core.sgf_parser import Move, ParseError
|
||||
from katrain.gui.kivyutils import *
|
||||
from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, AIPopup
|
||||
from katrain.gui.popups import ConfigPopup, LoadSGFPopup, NewGamePopup, ConfigAIPopup
|
||||
from katrain.gui.style import ENGINE_BUSY_COL, ENGINE_DOWN_COL, ENGINE_READY_COL, LIGHTGREY
|
||||
from katrain.gui.widgets.graph import ScoreGraph
|
||||
from katrain.gui.widgets.movetree import MoveTree
|
||||
@@ -311,7 +311,7 @@ class KaTrainGui(Screen, KaTrainBase):
|
||||
self.controls.timer.paused = True
|
||||
if not self.ai_settings_popup:
|
||||
self.ai_settings_popup = I18NPopup(
|
||||
title_key="ai settings", size=[dp(600), dp(600)], content=AIPopup(self)
|
||||
title_key="ai settings", size=[dp(600), dp(600)], content=ConfigAIPopup(self)
|
||||
).__self__
|
||||
self.ai_settings_popup.content.popup = self.ai_settings_popup
|
||||
self.ai_settings_popup.open()
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
VERSION = "1.2.1"
|
||||
VERSION = "1.3.0"
|
||||
HOMEPAGE = "https://github.com/sanderland/katrain"
|
||||
CONFIG_MIN_VERSION = "1.2.0"
|
||||
CONFIG_MIN_VERSION = "1.2.0" # keep config files from this version
|
||||
|
||||
OUTPUT_ERROR = -1
|
||||
OUTPUT_KATAGO_STDERR = -0.5
|
||||
OUTPUT_INFO = 0
|
||||
OUTPUT_DEBUG = 1
|
||||
OUTPUT_EXTRA_DEBUG = 2
|
||||
|
||||
PLAYER_HUMAN, PLAYER_AI = "player:human", "player:ai"
|
||||
PLAYER_TYPES = [PLAYER_HUMAN, PLAYER_AI]
|
||||
@@ -43,7 +49,7 @@ AI_STRATEGIES_RECOMMENDED_ORDER = [
|
||||
]
|
||||
|
||||
|
||||
AI_STRENGTH = {
|
||||
AI_STRENGTH = { # not used
|
||||
AI_DEFAULT: "9d",
|
||||
AI_POLICY: "4d",
|
||||
AI_JIGO: "?d",
|
||||
@@ -57,9 +63,7 @@ AI_STRENGTH = {
|
||||
AI_RANK: "15k - 3d",
|
||||
}
|
||||
|
||||
|
||||
OUTPUT_ERROR = -1
|
||||
OUTPUT_KATAGO_STDERR = -0.5
|
||||
OUTPUT_INFO = 0
|
||||
OUTPUT_DEBUG = 1
|
||||
OUTPUT_EXTRA_DEBUG = 2
|
||||
AI_OPTION_VALUES = {
|
||||
"kyu_rank": [(k, f"{k}[strength:kyu]") for k in range(15, 0, -1)]
|
||||
+ [(k, f"{1-k}[strength:dan]") for k in range(0, -3, -1)]
|
||||
}
|
||||
+33
-8
@@ -4,7 +4,7 @@ import re
|
||||
from typing import Any, Dict, List, Tuple, Union
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.properties import BooleanProperty, NumericProperty, StringProperty
|
||||
from kivy.properties import BooleanProperty, NumericProperty, StringProperty, ListProperty
|
||||
from kivy.uix.anchorlayout import AnchorLayout
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.label import Label
|
||||
@@ -21,12 +21,14 @@ from katrain.core.constants import (
|
||||
OUTPUT_DEBUG,
|
||||
OUTPUT_ERROR,
|
||||
OUTPUT_INFO,
|
||||
AI_OPTION_VALUES,
|
||||
)
|
||||
from katrain.core.engine import KataGoEngine
|
||||
from katrain.core.lang import i18n
|
||||
from katrain.core.utils import find_package_resource
|
||||
from katrain.gui.kivyutils import BackgroundMixin, I18NSpinner
|
||||
from katrain.gui.style import DEFAULT_FONT, EVAL_COLORS
|
||||
from katrain.gui.widgets.selection_slider import SelectionSlider
|
||||
|
||||
|
||||
class I18NPopup(Popup):
|
||||
@@ -129,6 +131,21 @@ class LabelledIntInput(LabelledTextInput):
|
||||
return int(self.text or "0")
|
||||
|
||||
|
||||
class LabelledSelectionSlider(BoxLayout):
|
||||
input_property = StringProperty("")
|
||||
values = ListProperty([(0, "")]) # (value:numeric,label:string) pairs
|
||||
|
||||
def set_value(self, v):
|
||||
self.slider.set_value(v)
|
||||
self.textbox.text = str(v)
|
||||
|
||||
@property
|
||||
def input_value(self):
|
||||
if self.textbox.text:
|
||||
return float(self.textbox.text)
|
||||
return self.slider.values[self.silder.index][0]
|
||||
|
||||
|
||||
class InputParseError(Exception):
|
||||
pass
|
||||
|
||||
@@ -183,12 +200,14 @@ class QuickConfigGui(MDBoxLayout):
|
||||
return self._set_properties_subtree(self)
|
||||
|
||||
def _set_properties_subtree(self, widget):
|
||||
if isinstance(widget, (LabelledTextInput, LabelledSpinner, LabelledCheckBox)) and getattr(
|
||||
widget, "input_property", None
|
||||
):
|
||||
if isinstance(
|
||||
widget, (LabelledTextInput, LabelledSpinner, LabelledCheckBox, LabelledSelectionSlider)
|
||||
) and getattr(widget, "input_property", None):
|
||||
value = self.get_setting(widget.input_property)[0]
|
||||
if isinstance(widget, LabelledCheckBox):
|
||||
widget.active = value is True
|
||||
elif isinstance(widget, LabelledSelectionSlider):
|
||||
widget.set_value(value)
|
||||
elif isinstance(widget, LabelledSpinner):
|
||||
selected = 0
|
||||
try:
|
||||
@@ -281,7 +300,7 @@ class DescriptionLabel(Label):
|
||||
pass
|
||||
|
||||
|
||||
class AIPopup(QuickConfigGui):
|
||||
class ConfigAIPopup(QuickConfigGui):
|
||||
max_options = NumericProperty(6)
|
||||
|
||||
def __init__(self, katrain):
|
||||
@@ -300,9 +319,15 @@ class AIPopup(QuickConfigGui):
|
||||
self.help_label.text = i18n._(strategy.replace("ai:", "aihelp:"))
|
||||
for k, v in sorted(mode_settings.items(), key=lambda kv: kv[0]):
|
||||
self.options_grid.add_widget(DescriptionLabel(text=k))
|
||||
self.options_grid.add_widget(
|
||||
wrap_anchor(LabelledFloatInput(text=str(v), input_property=f"ai/{strategy}/{k}"))
|
||||
)
|
||||
if k in AI_OPTION_VALUES:
|
||||
values = [(v, re.sub(r"\[(.*?)\]", lambda m: i18n._(m[1]), l)) for v, l in AI_OPTION_VALUES[k]]
|
||||
widget = LabelledSelectionSlider(values=values, input_property=f"ai/{strategy}/{k}")
|
||||
widget.set_value(v)
|
||||
self.options_grid.add_widget(wrap_anchor(widget))
|
||||
else:
|
||||
self.options_grid.add_widget(
|
||||
wrap_anchor(LabelledFloatInput(text=str(v), input_property=f"ai/{strategy}/{k}"))
|
||||
)
|
||||
for _ in range((self.max_options - len(mode_settings)) * 2):
|
||||
self.options_grid.add_widget(Label())
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
from kivy.lang import Builder
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.properties import (
|
||||
NumericProperty,
|
||||
AliasProperty,
|
||||
ListProperty,
|
||||
BooleanProperty,
|
||||
)
|
||||
from kivymd.uix.label import MDLabel
|
||||
from kivymd.uix.selectioncontrol import Thumb
|
||||
from katrain.gui.style import LIGHTER_BACKGROUND_COLOR
|
||||
|
||||
|
||||
class SelectionSlider(Widget):
|
||||
__events__ = ["on_select"]
|
||||
active = BooleanProperty(False)
|
||||
hint = BooleanProperty(True)
|
||||
|
||||
index = NumericProperty(0) # selected index
|
||||
values = ListProperty([(0, "")]) # (value:numeric,label:string) pairs
|
||||
normalized_pos = NumericProperty(0) # slider relative pos from 0-1
|
||||
px_pos = NumericProperty(0) # actual px pos
|
||||
padding = NumericProperty("16sp")
|
||||
|
||||
track_color = ListProperty([1, 1, 1, 0.3])
|
||||
thumb_color = LIGHTER_BACKGROUND_COLOR
|
||||
|
||||
def __init__(self,**kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.bind(size=self.set_index_and_positions,pos=self.set_index_and_positions,values=self.set_index_and_positions,index=self.set_index_and_positions)
|
||||
|
||||
def set_index_and_positions(self, *_args):
|
||||
self.index = max(0, min(self.index, len(self.values) - 1))
|
||||
self.normalized_pos = self.index / (len(self.values)-1)
|
||||
self.px_pos = self.x + self.padding + self.normalized_pos * (self.width - 2 * self.padding)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.values[self.index][0]
|
||||
|
||||
def set_value(self, set_value): # set to closest value
|
||||
eq_value = sorted([(abs(v - set_value), i) for i, (v, l) in enumerate(self.values)])
|
||||
self.index = eq_value[0][1]
|
||||
|
||||
def set_from_pos(self,pos):
|
||||
norm_value = (pos[0] - self.x - self.padding) / (self.width - 2 * self.padding)
|
||||
self.index = round(norm_value * (len(self.values) - 1))
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
if self.disabled or not self.collide_point(*touch.pos):
|
||||
return
|
||||
if touch.is_mouse_scrolling:
|
||||
if "down" in touch.button or "left" in touch.button:
|
||||
self.index += 1
|
||||
if "up" in touch.button or "right" in touch.button:
|
||||
self.index -= 1
|
||||
else:
|
||||
touch.grab(self)
|
||||
self.active = True
|
||||
self.set_from_pos(touch.pos)
|
||||
return True
|
||||
|
||||
def on_touch_move(self, touch):
|
||||
if touch.grab_current == self:
|
||||
self.set_from_pos(touch.pos)
|
||||
return True
|
||||
|
||||
def on_touch_up(self, touch):
|
||||
if touch.grab_current == self:
|
||||
self.set_from_pos(touch.pos)
|
||||
self.active = False
|
||||
self.dispatch("on_select", self.value)
|
||||
return True
|
||||
|
||||
def on_select(self,value):
|
||||
pass
|
||||
|
||||
|
||||
KV = """
|
||||
<SelectionSlider>:
|
||||
id: slider
|
||||
canvas:
|
||||
Clear
|
||||
Color:
|
||||
rgba:
|
||||
self.track_color
|
||||
Rectangle:
|
||||
size:
|
||||
(self.width - self.padding*2, dp(4))
|
||||
pos:
|
||||
(self.x + self.padding, self.center_y - dp(4))
|
||||
Color:
|
||||
rgba:
|
||||
self.thumb_color
|
||||
Rectangle:
|
||||
size:
|
||||
((self.width-self.padding*2)*self.normalized_pos, sp(4))
|
||||
pos:
|
||||
(self.x + self.padding, self.center_y - dp(4))
|
||||
|
||||
Thumb:
|
||||
id: thumb
|
||||
size_hint: None, None
|
||||
size:
|
||||
((dp(24), dp(24)) if root.active else (dp(16), dp(16)))
|
||||
pos:
|
||||
(slider.px_pos - dp(8), slider.center_y - thumb.height/2 - dp(2))
|
||||
color:
|
||||
root.thumb_color
|
||||
elevation:
|
||||
4 if root.active else 2
|
||||
|
||||
MDCard:
|
||||
id: hint_box
|
||||
size_hint: None, None
|
||||
md_bg_color: [1, 1, 1, 1] if root.active else [0, 0, 0, 0]
|
||||
elevation: 4 if root.active else 0
|
||||
size:
|
||||
(dp(28), dp(28) if root.active else (dp(20), dp(20)))
|
||||
pos:
|
||||
(slider.px_pos - dp(9), slider.center_y - hint_box.height / 2 + dp(30))
|
||||
|
||||
MDLabel:
|
||||
text: slider.values[slider.index][1]
|
||||
font_style: "Caption"
|
||||
halign: "center"
|
||||
color: root.thumb_color if root.active else [0, 0, 0, 0]
|
||||
|
||||
|
||||
"""
|
||||
|
||||
Builder.load_string(KV)
|
||||
+13
-1
@@ -366,8 +366,20 @@
|
||||
on_press: root.update_config(True)
|
||||
|
||||
|
||||
<LabelledSelectionSlider>:
|
||||
slider: slider
|
||||
textbox: textbox
|
||||
SelectionSlider:
|
||||
id: slider
|
||||
size_hint: 3,1
|
||||
values: root.values
|
||||
on_select: textbox.text = str(slider.value)
|
||||
LabelledFloatInput:
|
||||
id: textbox
|
||||
|
||||
<AIPopup>
|
||||
|
||||
|
||||
<ConfigAIPopup>:
|
||||
options_grid: options_grid
|
||||
help_label: help_label
|
||||
ai_select: ai_select
|
||||
|
||||
Reference in new issue
Block a user