Created
June 20, 2015 18:49
-
-
Save tito/c8b4de40158ccf6114e8 to your computer and use it in GitHub Desktop.
Console (inspector reboot)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding=utf-8 | |
""" | |
Console | |
======= | |
Reboot of the old inspector, designed to be modular and keep concerns separated. | |
Open with control+e (or cmd+e on OSX). | |
.. versionadded:: 1.9.1 | |
""" | |
__all__ = ("start", "stop", "create_console") | |
import kivy | |
kivy.require('1.0.9') | |
import weakref | |
from functools import partial | |
from itertools import chain | |
from kivy.logger import Logger | |
from kivy.uix.widget import Widget | |
from kivy.uix.button import Button | |
from kivy.uix.togglebutton import ToggleButton | |
from kivy.uix.label import Label | |
from kivy.uix.textinput import TextInput | |
from kivy.uix.image import Image | |
from kivy.uix.treeview import TreeViewNode, TreeView | |
from kivy.uix.gridlayout import GridLayout | |
from kivy.uix.relativelayout import RelativeLayout | |
from kivy.uix.boxlayout import BoxLayout | |
from kivy.uix.modalview import ModalView | |
from kivy.graphics import Color, Rectangle, PushMatrix, PopMatrix | |
from kivy.graphics.context_instructions import Transform | |
from kivy.graphics.transformation import Matrix | |
from kivy.properties import ObjectProperty, BooleanProperty, ListProperty, \ | |
NumericProperty, StringProperty, OptionProperty, \ | |
ReferenceListProperty, AliasProperty, VariableListProperty | |
from kivy.graphics.texture import Texture | |
from kivy.clock import Clock | |
from kivy.lang import Builder | |
Builder.load_string(""" | |
<Console>: | |
size_hint: (1, None) if self.mode == "docked" else (None, None) | |
height: dp(250) | |
canvas: | |
Color: | |
rgb: .185, .18, .18 | |
Rectangle: | |
size: self.size | |
Color: | |
rgb: .3, .3, .3 | |
Rectangle: | |
pos: 0, self.height - dp(48) | |
size: self.width, dp(48) | |
GridLayout: | |
cols: 1 | |
id: layout | |
GridLayout: | |
id: toolbar | |
rows: 1 | |
height: "48dp" | |
size_hint_y: None | |
padding: "4dp" | |
spacing: "4dp" | |
RelativeLayout: | |
id: content | |
<ConsoleToolSeparator>: | |
size_hint_x: None | |
width: "10dp" | |
<ConsoleButton,ConsoleToggleButton,ConsoleLabel>: | |
size_hint_x: None | |
width: self.texture_size[0] + dp(20) | |
<ConsoleToolBreadcrumbView>: | |
size_hint_y: None | |
height: "48dp" | |
canvas: | |
Color: | |
rgb: .3, .3, .3 | |
Rectangle: | |
size: self.size | |
ScrollView: | |
id: sv | |
do_scroll_y: False | |
GridLayout: | |
id: stack | |
rows: 1 | |
size_hint_x: None | |
width: self.minimum_width | |
padding: "4dp" | |
spacing: "4dp" | |
<TreeViewProperty>: | |
height: max(dp(48), max(lkey.texture_size[1], ltext.texture_size[1])) | |
Label: | |
id: lkey | |
text: root.key | |
text_size: (self.width, None) | |
width: 150 | |
size_hint_x: None | |
Label: | |
id: ltext | |
text: [repr(getattr(root.widget, root.key, '')), root.refresh][0]\ | |
if root.widget else '' | |
text_size: (self.width, None) | |
<ConsoleToolWidgetTreeView>: | |
ScrollView: | |
scroll_type: ['bars', 'content'] | |
bar_width: 10 | |
ConsoleToolWidgetTreeImpl: | |
id: widgettree | |
hide_root: True | |
size_hint: None, None | |
height: self.minimum_height | |
width: max(self.parent.width, self.minimum_width) | |
selected_widget: root.widget | |
on_select_widget: root.console.highlight_widget(args[1]) | |
<-TreeViewWidget>: | |
height: self.texture_size[1] + sp(4) | |
size_hint_x: None | |
width: self.texture_size[0] + sp(4) | |
canvas.before: | |
Color: | |
rgba: self.color_selected if self.is_selected else (0, 0, 0, 0) | |
Rectangle: | |
pos: self.pos | |
size: self.size | |
Color: | |
rgba: 1, 1, 1, int(not self.is_leaf) | |
Rectangle: | |
source: 'atlas://data/images/defaulttheme/tree_%s' % ('opened' if self.is_open else 'closed') | |
size: 16, 16 | |
pos: self.x - 20, self.center_y - 8 | |
canvas: | |
Color: | |
rgba: self.disabled_color if self.disabled else (self.color if not self.markup else (1, 1, 1, 1)) | |
Rectangle: | |
texture: self.texture | |
size: self.texture_size | |
pos: int(self.center_x - self.texture_size[0] / 2.), int(self.center_y - self.texture_size[1] / 2.) | |
""") | |
def ignore_exception(f): | |
def f2(*args, **kwargs): | |
try: | |
return f(*args, **kwargs) | |
except: | |
pass | |
return f2 | |
class TreeViewProperty(BoxLayout, TreeViewNode): | |
key = ObjectProperty(None, allownone=True) | |
refresh = BooleanProperty(False) | |
widget_ref = ObjectProperty(None, allownone=True) | |
def _get_widget(self): | |
wr = self.widget_ref | |
if wr is None: | |
return None | |
wr = wr() | |
if wr is None: | |
self.widget_ref = None | |
return None | |
return wr | |
widget = AliasProperty(_get_widget, None, bind=('widget_ref', )) | |
class ConsoleButton(Button): | |
pass | |
class ConsoleToggleButton(ToggleButton): | |
pass | |
class ConsoleLabel(Label): | |
pass | |
class ConsoleToolSeparator(Widget): | |
pass | |
class ConsoleTool(object): | |
def __init__(self, console): | |
super(ConsoleTool, self).__init__() | |
self.console = console | |
self.init() | |
def init(self): | |
pass | |
def activate(self): | |
pass | |
def deactivate(self): | |
pass | |
class ConsoleToolMode(ConsoleTool): | |
def init(self): | |
btn = ConsoleToggleButton(text=u"Docked") | |
self.console.add_toolbar_widget(btn) | |
class ConsoleToolSelect(ConsoleTool): | |
def init(self): | |
self.btn = ConsoleToggleButton(text=u"Select") | |
self.btn.bind(state=self.on_button_state) | |
self.console.add_toolbar_widget(self.btn) | |
self.console.bind(inspect_enabled=self.on_inspect_enabled) | |
def on_inspect_enabled(self, instance, value): | |
self.btn.state = "down" if value else "normal" | |
def on_button_state(self, instance, value): | |
self.console.inspect_enabled = (value == "down") | |
class ConsoleToolFps(ConsoleTool): | |
def init(self): | |
self.lbl = ConsoleLabel(text="0 Fps") | |
self.console.add_toolbar_widget(self.lbl, right=True) | |
def activate(self): | |
Clock.schedule_interval(self.update_fps, 1 / 2.) | |
def deactivated(self): | |
Clock.unschedule(self.update_fps) | |
def update_fps(self, *args): | |
fps = Clock.get_fps() | |
self.lbl.text = "{} Fps".format(int(fps)) | |
class ConsoleToolBreadcrumbView(RelativeLayout): | |
widget = ObjectProperty(None, allownone=True) | |
parents = [] | |
def on_widget(self, instance, value): | |
stack = self.ids.stack | |
# determine if we can just highlight the current one | |
# or if we need to rebuild the breadcrumb | |
prefs = [btn.widget_ref() for btn in self.parents] | |
if value in prefs: | |
# ok, so just toggle this one instead. | |
index = prefs.index(value) | |
for btn in self.parents: | |
btn.state = "normal" | |
self.parents[index].state = "down" | |
return | |
# we need to rebuild the breadcrumb. | |
stack.clear_widgets() | |
if not value: | |
return | |
widget = value | |
parents = [] | |
while True: | |
btn = ConsoleButton(text=widget.__class__.__name__) | |
btn.widget_ref = weakref.ref(widget) | |
btn.bind(on_release=self.highlight_widget) | |
parents.append(btn) | |
if widget == widget.parent: | |
break | |
widget = widget.parent | |
for btn in reversed(parents): | |
stack.add_widget(btn) | |
self.ids.sv.scroll_x = 1 | |
self.parents = parents | |
btn.state = "down" | |
def highlight_widget(self, instance): | |
self.console.widget = instance.widget_ref() | |
class ConsoleToolBreadcrumb(ConsoleTool): | |
def init(self): | |
self.view = ConsoleToolBreadcrumbView() | |
self.view.console = self.console | |
self.console.ids.layout.add_widget(self.view) | |
def activate(self): | |
self.console.bind(widget=self.update_content) | |
self.update_content() | |
def deactivate(self): | |
self.console.unbind(widget=self.update_content) | |
def update_content(self, *args): | |
self.view.widget = self.console.widget | |
class ConsoleToolWidgetPanel(ConsoleTool): | |
def init(self): | |
self.console.add_panel("Properties", self.panel_activate, | |
self.deactivate) | |
def panel_activate(self): | |
self.console.bind(widget=self.update_content) | |
self.update_content() | |
def deactivate(self): | |
self.console.unbind(widget=self.update_content) | |
def update_content(self, *args): | |
widget = self.console.widget | |
if not widget: | |
return | |
from kivy.uix.scrollview import ScrollView | |
self.root = root = BoxLayout() | |
self.sv = sv = ScrollView(scroll_type=["bars", "content"]) | |
treeview = TreeView(hide_root=True, size_hint_y=None) | |
treeview.bind(minimum_height=treeview.setter("height")) | |
keys = list(widget.properties().keys()) | |
keys.sort() | |
node = None | |
wk_widget = weakref.ref(widget) | |
for key in keys: | |
text = '%s' % key | |
node = TreeViewProperty(text=text, key=key, widget_ref=wk_widget) | |
node.bind(is_selected=self.show_property) | |
try: | |
widget.bind(**{ | |
key: partial(self.update_node_content, weakref.ref(node)) | |
}) | |
except: | |
pass | |
treeview.add_node(node) | |
root.add_widget(sv) | |
sv.add_widget(treeview) | |
self.console.set_content(root) | |
def show_property(self, instance, value, key=None, index=-1, *l): | |
# normal call: (tree node, focus, ) | |
# nested call: (widget, prop value, prop key, index in dict/list) | |
if value is False: | |
return | |
console = self.console | |
content = None | |
if key is None: | |
# normal call | |
nested = False | |
widget = instance.widget | |
key = instance.key | |
prop = widget.property(key) | |
value = getattr(widget, key) | |
else: | |
# nested call, we might edit subvalue | |
nested = True | |
widget = instance | |
prop = None | |
dtype = None | |
if isinstance(prop, AliasProperty) or nested: | |
# trying to resolve type dynamicly | |
if type(value) in (str, str): | |
dtype = 'string' | |
elif type(value) in (int, float): | |
dtype = 'numeric' | |
elif type(value) in (tuple, list): | |
dtype = 'list' | |
if isinstance(prop, NumericProperty) or dtype == 'numeric': | |
content = TextInput(text=str(value) or '', multiline=False) | |
content.bind( | |
text=partial(self.save_property_numeric, widget, key, index)) | |
elif isinstance(prop, StringProperty) or dtype == 'string': | |
content = TextInput(text=value or '', multiline=True) | |
content.bind( | |
text=partial(self.save_property_text, widget, key, index)) | |
elif (isinstance(prop, ListProperty) or | |
isinstance(prop, ReferenceListProperty) or | |
isinstance(prop, VariableListProperty) or dtype == 'list'): | |
content = GridLayout(cols=1, size_hint_y=None) | |
content.bind(minimum_height=content.setter('height')) | |
for i, item in enumerate(value): | |
button = Button(text=repr(item), size_hint_y=None, height=44) | |
if isinstance(item, Widget): | |
button.bind(on_release=partial(console.highlight_widget, | |
item, False)) | |
else: | |
button.bind(on_release=partial(self.show_property, widget, | |
item, key, i)) | |
content.add_widget(button) | |
elif isinstance(prop, OptionProperty): | |
content = GridLayout(cols=1, size_hint_y=None) | |
content.bind(minimum_height=content.setter('height')) | |
for option in prop.options: | |
button = ToggleButton( | |
text=option, | |
state='down' if option == value else 'normal', | |
group=repr(content.uid), | |
size_hint_y=None, | |
height=44) | |
button.bind( | |
on_press=partial(self.save_property_option, widget, key)) | |
content.add_widget(button) | |
elif isinstance(prop, ObjectProperty): | |
if isinstance(value, Widget): | |
content = Button(text=repr(value)) | |
content.bind( | |
on_release=partial(console.highlight_widget, value)) | |
elif isinstance(value, Texture): | |
content = Image(texture=value) | |
else: | |
content = Label(text=repr(value)) | |
elif isinstance(prop, BooleanProperty): | |
state = 'down' if value else 'normal' | |
content = ToggleButton(text=key, state=state) | |
content.bind(on_release=partial(self.save_property_boolean, widget, | |
key, index)) | |
self.root.clear_widgets() | |
self.root.add_widget(self.sv) | |
if content: | |
self.root.add_widget(content) | |
@ignore_exception | |
def save_property_numeric(self, widget, key, index, instance, value): | |
if index >= 0: | |
getattr(widget, key)[index] = float(instance.text) | |
else: | |
setattr(widget, key, float(instance.text)) | |
@ignore_exception | |
def save_property_text(self, widget, key, index, instance, value): | |
if index >= 0: | |
getattr(widget, key)[index] = instance.text | |
else: | |
setattr(widget, key, instance.text) | |
@ignore_exception | |
def save_property_boolean(self, widget, key, index, instance, ): | |
value = instance.state == 'down' | |
if index >= 0: | |
getattr(widget, key)[index] = value | |
else: | |
setattr(widget, key, value) | |
@ignore_exception | |
def save_property_option(self, widget, key, instance, *l): | |
setattr(widget, key, instance.text) | |
class TreeViewWidget(Label, TreeViewNode): | |
widget = ObjectProperty(None) | |
class ConsoleToolWidgetTreeImpl(TreeView): | |
selected_widget = ObjectProperty(None, allownone=True) | |
__events__ = ('on_select_widget', ) | |
def __init__(self, **kwargs): | |
super(ConsoleToolWidgetTreeImpl, self).__init__(**kwargs) | |
self.update_scroll = Clock.create_trigger(self._update_scroll) | |
def find_node_by_widget(self, widget): | |
for node in self.iterate_all_nodes(): | |
if not node.parent_node: | |
continue | |
try: | |
if node.widget == widget: | |
return node | |
except ReferenceError: | |
pass | |
return None | |
def update_selected_widget(self, widget): | |
if widget: | |
node = self.find_node_by_widget(widget) | |
if node: | |
self.select_node(node, False) | |
while node and isinstance(node, TreeViewWidget): | |
if not node.is_open: | |
self.toggle_node(node) | |
node = node.parent_node | |
def on_selected_widget(self, inst, widget): | |
if widget: | |
self.update_selected_widget(widget) | |
self.update_scroll() | |
def select_node(self, node, select_widget=True): | |
super(ConsoleToolWidgetTreeImpl, self).select_node(node) | |
if select_widget: | |
try: | |
self.dispatch("on_select_widget", node.widget.__self__) | |
except ReferenceError: | |
pass | |
def on_select_widget(self, widget): | |
pass | |
def _update_scroll(self, *args): | |
node = self._selected_node | |
if not node: | |
return | |
self.parent.scroll_to(node) | |
class ConsoleToolWidgetTreeView(RelativeLayout): | |
widget = ObjectProperty(None, allownone=True) | |
_window_node = None | |
def _update_widget_tree_node(self, node, widget, is_open=False): | |
tree = self.ids.widgettree | |
update_nodes = [] | |
nodes = {} | |
for cnode in node.nodes[:]: | |
try: | |
nodes[cnode.widget] = cnode | |
except ReferenceError: | |
# widget no longer exists, just remove it | |
pass | |
tree.remove_node(cnode) | |
for child in widget.children: | |
if isinstance(child, Console): | |
continue | |
if child in nodes: | |
cnode = tree.add_node(nodes[child], node) | |
else: | |
cnode = tree.add_node( | |
TreeViewWidget(text=child.__class__.__name__, | |
widget=child.proxy_ref, | |
is_open=is_open), node) | |
update_nodes.append((cnode, child)) | |
return update_nodes | |
def update_widget_tree(self, *args): | |
win = self.console.win | |
if not self._window_node: | |
self._window_node = self.ids.widgettree.add_node( | |
TreeViewWidget(text="Window", | |
widget=win, | |
is_open=True)) | |
nodes = self._update_widget_tree_node(self._window_node, win, | |
is_open=True) | |
while nodes: | |
ntmp = nodes[:] | |
nodes = [] | |
for node in ntmp: | |
nodes += self._update_widget_tree_node(*node) | |
self.ids.widgettree.update_selected_widget(self.widget) | |
class ConsoleToolWidgetTree(ConsoleTool): | |
def init(self): | |
self.content = None | |
self.console.add_panel("Tree", self.panel_activate, self.deactivate, | |
self.panel_refresh) | |
def panel_activate(self): | |
self.console.bind(widget=self.update_content) | |
self.update_content() | |
def deactivate(self): | |
if self.content: | |
self.content.widget = None | |
self.content.console = None | |
self.console.unbind(widget=self.update_content) | |
def update_content(self, *args): | |
widget = self.console.widget | |
if not self.content: | |
self.content = ConsoleToolWidgetTreeView() | |
self.content.console = self.console | |
self.content.widget = widget | |
self.content.update_widget_tree() | |
self.console.set_content(self.content) | |
def panel_refresh(self): | |
if self.content: | |
self.content.update_widget_tree() | |
class Console(RelativeLayout): | |
addons = [ # ConsoleToolMode, | |
ConsoleToolSelect, | |
ConsoleToolFps, | |
ConsoleToolWidgetPanel, | |
ConsoleToolWidgetTree, | |
ConsoleToolBreadcrumb | |
] | |
mode = OptionProperty("docked", options=["docked", "floated"]) | |
widget = ObjectProperty(None, allownone=True) | |
inspect_enabled = BooleanProperty(False) | |
activated = BooleanProperty(False) | |
def __init__(self, **kwargs): | |
super(Console, self).__init__(**kwargs) | |
self.avoid_bring_to_top = False | |
self.win = kwargs.get('win') | |
with self.canvas.before: | |
self.gcolor = Color(1, 0, 0, .25) | |
PushMatrix() | |
self.gtransform = Transform(Matrix()) | |
self.grect = Rectangle(size=(0, 0)) | |
PopMatrix() | |
Clock.schedule_interval(self.update_widget_graphics, 0) | |
self._toolbar = {"left": [], "panels": [], "right": []} | |
self._addons = [] | |
self._panel = None | |
for addon in self.addons: | |
instance = addon(self) | |
self._addons.append(instance) | |
self._init_toolbar() | |
# select the first panel | |
self._panel = self._toolbar["panels"][0] | |
self._panel.state = "down" | |
self._panel.cb_activate() | |
def _init_toolbar(self): | |
toolbar = self.ids.toolbar | |
for key in ("left", "panels", "right"): | |
if key == "right": | |
toolbar.add_widget(Widget()) | |
for el in self._toolbar[key]: | |
toolbar.add_widget(el) | |
if key != "right": | |
toolbar.add_widget(ConsoleToolSeparator()) | |
def add_toolbar_widget(self, widget, right=False): | |
key = "right" if right else "left" | |
self._toolbar[key].append(widget) | |
def remove_toolbar_widget(self, widget): | |
self.ids.toolbar.remove_widget(widget) | |
def add_panel(self, name, cb_activate, cb_deactivate, cb_refresh=None): | |
btn = ConsoleToggleButton(text=name) | |
btn.cb_activate = cb_activate | |
btn.cb_deactivate = cb_deactivate | |
btn.cb_refresh = cb_refresh | |
btn.bind(on_press=self._activate_panel) | |
self._toolbar["panels"].append(btn) | |
def _activate_panel(self, instance): | |
if self._panel != instance: | |
self._panel.cb_deactivate() | |
self._panel.state = "normal" | |
self.ids.content.clear_widgets() | |
self._panel = instance | |
self._panel.cb_activate() | |
self._panel.state = "down" | |
else: | |
self._panel.state = "down" | |
if self._panel.cb_refresh: | |
self._panel.cb_refresh() | |
def set_content(self, content): | |
self.ids.content.clear_widgets() | |
self.ids.content.add_widget(content) | |
def on_touch_down(self, touch): | |
ret = super(Console, self).on_touch_down(touch) | |
if (('button' not in touch.profile or touch.button == 'left') and | |
not ret and self.inspect_enabled): | |
self.highlight_at(*touch.pos) | |
if touch.is_double_tap: | |
self.inspect_enabled = False | |
ret = True | |
else: | |
ret = self.collide_point(*touch.pos) | |
return ret | |
def on_touch_move(self, touch): | |
ret = super(Console, self).on_touch_move(touch) | |
if not ret and self.inspect_enabled: | |
self.highlight_at(*touch.pos) | |
ret = True | |
return ret | |
def on_touch_up(self, touch): | |
ret = super(Console, self).on_touch_up(touch) | |
if not ret and self.inspect_enabled: | |
ret = True | |
return ret | |
def on_window_children(self, win, children): | |
if self.avoid_bring_to_top: | |
return | |
self.avoid_bring_to_top = True | |
win.remove_widget(self) | |
win.add_widget(self) | |
self.avoid_bring_to_top = False | |
def highlight_at(self, x, y): | |
widget = None | |
# reverse the loop - look at children on top first and | |
# modalviews before others | |
win_children = self.win.children | |
children = chain((c for c in reversed(win_children) | |
if isinstance(c, ModalView)), | |
(c for c in reversed(win_children) | |
if not isinstance(c, ModalView))) | |
for child in children: | |
if child is self: | |
continue | |
widget = self.pick(child, x, y) | |
if widget: | |
break | |
self.highlight_widget(widget) | |
def highlight_widget(self, widget, *largs): | |
# no widget to highlight, reduce rectangle to 0, 0 | |
self.widget = widget | |
if not widget: | |
self.grect.size = 0, 0 | |
def update_widget_graphics(self, *l): | |
if not self.activated: | |
return | |
if self.widget is None: | |
self.grect.size = 0, 0 | |
return | |
self.grect.size = self.widget.size | |
matrix = self.widget.get_window_matrix() | |
if self.gtransform.matrix.get() != matrix.get(): | |
self.gtransform.matrix = matrix | |
def pick(self, widget, x, y): | |
ret = None | |
# try to filter widgets that are not visible (invalid inspect target) | |
if (hasattr(widget, 'visible') and not widget.visible): | |
return ret | |
if widget.collide_point(x, y): | |
ret = widget | |
x2, y2 = widget.to_local(x, y) | |
# reverse the loop - look at children on top first | |
for child in reversed(widget.children): | |
ret = self.pick(child, x2, y2) or ret | |
return ret | |
def on_activated(self, instance, activated): | |
if activated: | |
self._activate_console() | |
else: | |
self._deactivate_console() | |
def _activate_console(self): | |
if not self in self.win.children: | |
self.win.add_widget(self) | |
self.y = 0 | |
for addon in self._addons: | |
addon.activate() | |
Logger.info('Console: console activated') | |
def _deactivate_console(self): | |
for addon in self._addons: | |
addon.deactivate() | |
self.grect.size = 0, 0 | |
self.y = -self.height | |
self.widget = None | |
self.inspect_enabled = False | |
#self.win.remove_widget(self) | |
self._window_node = None | |
Logger.info('Console: console deactivated') | |
def keyboard_shortcut(self, win, scancode, *largs): | |
modifiers = largs[-1] | |
if scancode == 101 and modifiers == ['ctrl']: | |
self.activated = not self.activated | |
if self.activated: | |
self.inspect_enabled = True | |
return True | |
elif scancode == 27: | |
if self.inspect_enabled: | |
self.inspect_enabled = False | |
return True | |
if self.activated: | |
self.activated = False | |
return True | |
if not self.activated or not self.widget: | |
return | |
if scancode == 273: # top | |
self.widget = self.widget.parent | |
elif scancode == 274: # down | |
filtered_children = [c for c in self.widget.children if not isinstance(c, Console)] | |
if filtered_children: | |
self.widget = filtered_children[0] | |
elif scancode == 276: # left | |
parent = self.widget.parent | |
filtered_children = [c for c in parent.children if not isinstance(c, Console)] | |
index = filtered_children.index(self.widget) | |
index = max(0, index - 1) | |
self.widget = filtered_children[index] | |
elif scancode == 275: # right | |
parent = self.widget.parent | |
filtered_children = [c for c in parent.children if not isinstance(c, Console)] | |
index = filtered_children.index(self.widget) | |
index = min(len(filtered_children) - 1, index + 1) | |
self.widget = filtered_children[index] | |
def create_console(win, ctx, *l): | |
ctx.console = Console(win=win) | |
win.bind(children=ctx.console.on_window_children, | |
on_keyboard=ctx.console.keyboard_shortcut) | |
def start(win, ctx): | |
Clock.schedule_once(partial(create_console, win, ctx)) | |
def stop(win, ctx): | |
if hasattr(ctx, "console"): | |
win.unbind(children=ctx.console.on_window_children, | |
on_keyboard=ctx.console.keyboard_shortcut) | |
win.remove_widget(ctx.console) | |
del ctx.console |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment