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