Ajout du GUI
This commit is contained in:
755
kivy/modules/inspector.py
Normal file
755
kivy/modules/inspector.py
Normal file
@@ -0,0 +1,755 @@
|
||||
'''
|
||||
Inspector
|
||||
=========
|
||||
|
||||
.. versionadded:: 1.0.9
|
||||
|
||||
.. warning::
|
||||
|
||||
This module is highly experimental, use it with care.
|
||||
|
||||
The Inspector is a tool for finding a widget in the widget tree by clicking or
|
||||
tapping on it.
|
||||
Some keyboard shortcuts are activated:
|
||||
|
||||
* "Ctrl + e": activate / deactivate the inspector view
|
||||
* "Escape": cancel widget lookup first, then hide the inspector view
|
||||
|
||||
Available inspector interactions:
|
||||
|
||||
* tap once on a widget to select it without leaving inspect mode
|
||||
* double tap on a widget to select and leave inspect mode (then you can
|
||||
manipulate the widget again)
|
||||
|
||||
Some properties can be edited live. However, due to the delayed usage of
|
||||
some properties, it might crash if you don't handle all the cases.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
For normal module usage, please see the :mod:`~kivy.modules` documentation.
|
||||
|
||||
The Inspector, however, can also be imported and used just like a normal
|
||||
python module. This has the added advantage of being able to activate and
|
||||
deactivate the module programmatically::
|
||||
|
||||
from kivy.core.window import Window
|
||||
from kivy.app import App
|
||||
from kivy.uix.button import Button
|
||||
from kivy.modules import inspector
|
||||
|
||||
class Demo(App):
|
||||
def build(self):
|
||||
button = Button(text="Test")
|
||||
inspector.create_inspector(Window, button)
|
||||
return button
|
||||
|
||||
Demo().run()
|
||||
|
||||
To remove the Inspector, you can do the following::
|
||||
|
||||
inspector.stop(Window, button)
|
||||
|
||||
'''
|
||||
|
||||
__all__ = ('start', 'stop', 'create_inspector')
|
||||
|
||||
import weakref
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
|
||||
from kivy.animation import Animation
|
||||
from kivy.logger import Logger
|
||||
from kivy.graphics.transformation import Matrix
|
||||
from kivy.clock import Clock
|
||||
from kivy.lang import Builder
|
||||
from kivy.factory import Factory
|
||||
from kivy.weakproxy import WeakProxy
|
||||
from kivy.properties import (
|
||||
ObjectProperty, BooleanProperty, ListProperty,
|
||||
NumericProperty, StringProperty, OptionProperty,
|
||||
ReferenceListProperty, AliasProperty, VariableListProperty
|
||||
)
|
||||
|
||||
Builder.load_string('''
|
||||
<Inspector>:
|
||||
layout: layout
|
||||
widgettree: widgettree
|
||||
treeview: treeview
|
||||
content: content
|
||||
BoxLayout:
|
||||
orientation: 'vertical'
|
||||
id: layout
|
||||
size_hint_y: None
|
||||
height: 250
|
||||
padding: 5
|
||||
spacing: 5
|
||||
top: 0
|
||||
|
||||
canvas:
|
||||
Color:
|
||||
rgb: .4, .4, .4
|
||||
Rectangle:
|
||||
pos: self.x, self.top
|
||||
size: self.width, 1
|
||||
Color:
|
||||
rgba: .185, .18, .18, .95
|
||||
Rectangle:
|
||||
pos: self.pos
|
||||
size: self.size
|
||||
|
||||
# Top Bar
|
||||
BoxLayout:
|
||||
size_hint_y: None
|
||||
height: 50
|
||||
spacing: 5
|
||||
Button:
|
||||
text: 'Move to Top'
|
||||
on_release: root.toggle_position(args[0])
|
||||
size_hint_x: None
|
||||
width: 120
|
||||
|
||||
ToggleButton:
|
||||
text: 'Inspect'
|
||||
on_state: root.inspect_enabled = args[1] == 'down'
|
||||
size_hint_x: None
|
||||
state: 'down' if root.inspect_enabled else 'normal'
|
||||
width: 80
|
||||
|
||||
Button:
|
||||
text: 'Parent'
|
||||
on_release:
|
||||
root.highlight_widget(root.widget.parent) if root.widget \
|
||||
else None
|
||||
size_hint_x: None
|
||||
width: 80
|
||||
|
||||
Button:
|
||||
text: '%r' % root.widget
|
||||
on_release: root.show_widget_info()
|
||||
|
||||
Button:
|
||||
text: 'X'
|
||||
size_hint_x: None
|
||||
width: 50
|
||||
on_release: root.activated = False
|
||||
|
||||
# Bottom Bar
|
||||
BoxLayout:
|
||||
ScrollView:
|
||||
scroll_type: ['bars', 'content']
|
||||
bar_width: 10
|
||||
size_hint_x: 0.0001
|
||||
|
||||
WidgetTree:
|
||||
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.highlight_widget(args[1])
|
||||
|
||||
Splitter:
|
||||
sizeable_from: 'left'
|
||||
min_size: self.parent.width / 2
|
||||
max_size: self.parent.width
|
||||
|
||||
BoxLayout:
|
||||
ScrollView:
|
||||
scroll_type: ['bars', 'content']
|
||||
bar_width: 10
|
||||
TreeView:
|
||||
id: treeview
|
||||
size_hint_y: None
|
||||
hide_root: True
|
||||
height: self.minimum_height
|
||||
|
||||
Splitter:
|
||||
sizeable_from: 'left'
|
||||
keep_within_parent: True
|
||||
rescale_with_parent: True
|
||||
max_size: self.parent.width / 2
|
||||
min_size: 0
|
||||
|
||||
ScrollView:
|
||||
id: content
|
||||
|
||||
<TreeViewProperty>:
|
||||
height: 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)
|
||||
|
||||
<-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.))
|
||||
''')
|
||||
|
||||
|
||||
class TreeViewProperty(Factory.BoxLayout, Factory.TreeViewNode):
|
||||
|
||||
widget_ref = ObjectProperty(None, allownone=True)
|
||||
|
||||
def _get_widget(self):
|
||||
wr = self.widget_ref
|
||||
if wr is None:
|
||||
return
|
||||
wr = wr()
|
||||
if wr is None:
|
||||
self.widget_ref = None
|
||||
return
|
||||
return wr
|
||||
widget = AliasProperty(_get_widget, None, bind=('widget_ref', ))
|
||||
|
||||
key = ObjectProperty(None, allownone=True)
|
||||
|
||||
inspector = ObjectProperty(None)
|
||||
|
||||
refresh = BooleanProperty(False)
|
||||
|
||||
|
||||
class TreeViewWidget(Factory.Label, Factory.TreeViewNode):
|
||||
widget = ObjectProperty(None)
|
||||
|
||||
|
||||
class WidgetTree(Factory.TreeView):
|
||||
selected_widget = ObjectProperty(None, allownone=True)
|
||||
|
||||
__events__ = ('on_select_widget',)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(WidgetTree, 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
|
||||
|
||||
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(WidgetTree, 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 Inspector(Factory.FloatLayout):
|
||||
|
||||
widget = ObjectProperty(None, allownone=True)
|
||||
|
||||
layout = ObjectProperty(None)
|
||||
|
||||
widgettree = ObjectProperty(None)
|
||||
|
||||
treeview = ObjectProperty(None)
|
||||
|
||||
inspect_enabled = BooleanProperty(False)
|
||||
|
||||
activated = BooleanProperty(False)
|
||||
|
||||
widget_info = BooleanProperty(False)
|
||||
|
||||
content = ObjectProperty(None)
|
||||
|
||||
at_bottom = BooleanProperty(True)
|
||||
|
||||
_update_widget_tree_ev = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.win = kwargs.pop('win', None)
|
||||
super(Inspector, self).__init__(**kwargs)
|
||||
self.avoid_bring_to_top = False
|
||||
with self.canvas.before:
|
||||
self.gcolor = Factory.Color(1, 0, 0, .25)
|
||||
Factory.PushMatrix()
|
||||
self.gtransform = Factory.Transform(Matrix())
|
||||
self.grect = Factory.Rectangle(size=(0, 0))
|
||||
Factory.PopMatrix()
|
||||
Clock.schedule_interval(self.update_widget_graphics, 0)
|
||||
|
||||
def on_touch_down(self, touch):
|
||||
ret = super(Inspector, 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
|
||||
self.show_widget_info()
|
||||
ret = True
|
||||
return ret
|
||||
|
||||
def on_touch_move(self, touch):
|
||||
ret = super(Inspector, 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(Inspector, 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 or not self.activated:
|
||||
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 win_children if isinstance(c, Factory.ModalView)),
|
||||
(
|
||||
c for c in reversed(win_children)
|
||||
if not isinstance(c, Factory.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, info=True, *largs):
|
||||
# no widget to highlight, reduce rectangle to 0, 0
|
||||
self.widget = widget
|
||||
if not widget:
|
||||
self.grect.size = 0, 0
|
||||
if self.widget_info and info:
|
||||
self.show_widget_info()
|
||||
|
||||
def update_widget_graphics(self, *largs):
|
||||
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 toggle_position(self, button):
|
||||
to_bottom = button.text == 'Move to Bottom'
|
||||
|
||||
if to_bottom:
|
||||
button.text = 'Move to Top'
|
||||
if self.widget_info:
|
||||
Animation(top=250, t='out_quad', d=.3).start(self.layout)
|
||||
else:
|
||||
Animation(top=60, t='out_quad', d=.3).start(self.layout)
|
||||
|
||||
bottom_bar = self.layout.children[1]
|
||||
self.layout.remove_widget(bottom_bar)
|
||||
self.layout.add_widget(bottom_bar)
|
||||
else:
|
||||
button.text = 'Move to Bottom'
|
||||
if self.widget_info:
|
||||
Animation(top=self.height, t='out_quad', d=.3).start(
|
||||
self.layout)
|
||||
else:
|
||||
Animation(y=self.height - 60, t='out_quad', d=.3).start(
|
||||
self.layout)
|
||||
|
||||
bottom_bar = self.layout.children[1]
|
||||
self.layout.remove_widget(bottom_bar)
|
||||
self.layout.add_widget(bottom_bar)
|
||||
self.at_bottom = to_bottom
|
||||
|
||||
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 not activated:
|
||||
self.grect.size = 0, 0
|
||||
if self.at_bottom:
|
||||
anim = Animation(top=0, t='out_quad', d=.3)
|
||||
else:
|
||||
anim = Animation(y=self.height, t='out_quad', d=.3)
|
||||
anim.bind(on_complete=self.animation_close)
|
||||
anim.start(self.layout)
|
||||
self.widget = None
|
||||
self.widget_info = False
|
||||
else:
|
||||
self.win.add_widget(self)
|
||||
Logger.info('Inspector: inspector activated')
|
||||
if self.at_bottom:
|
||||
Animation(top=60, t='out_quad', d=.3).start(self.layout)
|
||||
else:
|
||||
Animation(y=self.height - 60, t='out_quad', d=.3).start(
|
||||
self.layout)
|
||||
ev = self._update_widget_tree_ev
|
||||
if ev is None:
|
||||
ev = self._update_widget_tree_ev = Clock.schedule_interval(
|
||||
self.update_widget_tree, 1)
|
||||
else:
|
||||
ev()
|
||||
self.update_widget_tree()
|
||||
|
||||
def animation_close(self, instance, value):
|
||||
if not self.activated:
|
||||
self.inspect_enabled = False
|
||||
self.win.remove_widget(self)
|
||||
self.content.clear_widgets()
|
||||
treeview = self.treeview
|
||||
for node in list(treeview.iterate_all_nodes()):
|
||||
node.widget_ref = None
|
||||
treeview.remove_node(node)
|
||||
|
||||
self._window_node = None
|
||||
if self._update_widget_tree_ev is not None:
|
||||
self._update_widget_tree_ev.cancel()
|
||||
|
||||
widgettree = self.widgettree
|
||||
for node in list(widgettree.iterate_all_nodes()):
|
||||
widgettree.remove_node(node)
|
||||
Logger.info('Inspector: inspector deactivated')
|
||||
|
||||
def show_widget_info(self):
|
||||
self.content.clear_widgets()
|
||||
widget = self.widget
|
||||
treeview = self.treeview
|
||||
for node in list(treeview.iterate_all_nodes())[:]:
|
||||
node.widget_ref = None
|
||||
treeview.remove_node(node)
|
||||
if not widget:
|
||||
if self.at_bottom:
|
||||
Animation(top=60, t='out_quad', d=.3).start(self.layout)
|
||||
else:
|
||||
Animation(y=self.height - 60, t='out_quad', d=.3).start(
|
||||
self.layout)
|
||||
self.widget_info = False
|
||||
return
|
||||
self.widget_info = True
|
||||
if self.at_bottom:
|
||||
Animation(top=250, t='out_quad', d=.3).start(self.layout)
|
||||
else:
|
||||
Animation(top=self.height, t='out_quad', d=.3).start(self.layout)
|
||||
for node in list(treeview.iterate_all_nodes())[:]:
|
||||
treeview.remove_node(node)
|
||||
|
||||
keys = list(widget.properties().keys())
|
||||
keys.sort()
|
||||
node = None
|
||||
if type(widget) is WeakProxy:
|
||||
wk_widget = widget.__ref__
|
||||
else:
|
||||
wk_widget = weakref.ref(widget)
|
||||
for key in keys:
|
||||
node = TreeViewProperty(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)
|
||||
|
||||
def update_node_content(self, node, *largs):
|
||||
node = node()
|
||||
if node is None:
|
||||
return
|
||||
node.refresh = True
|
||||
node.refresh = False
|
||||
|
||||
def keyboard_shortcut(self, win, scancode, *largs):
|
||||
modifiers = largs[-1]
|
||||
if scancode == 101 and set(modifiers) & {'ctrl'} and not set(
|
||||
modifiers) & {'shift', 'alt', 'meta'}:
|
||||
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
|
||||
|
||||
def show_property(self, instance, value, key=None, index=-1, *largs):
|
||||
# normal call: (tree node, focus, )
|
||||
# nested call: (widget, prop value, prop key, index in dict/list)
|
||||
if value is False:
|
||||
return
|
||||
|
||||
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 dynamically
|
||||
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 = Factory.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 = Factory.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 = Factory.GridLayout(cols=1, size_hint_y=None)
|
||||
content.bind(minimum_height=content.setter('height'))
|
||||
for i, item in enumerate(value):
|
||||
button = Factory.Button(
|
||||
text=repr(item),
|
||||
size_hint_y=None,
|
||||
height=44
|
||||
)
|
||||
if isinstance(item, Factory.Widget):
|
||||
button.bind(on_release=partial(self.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 = Factory.GridLayout(cols=1, size_hint_y=None)
|
||||
content.bind(minimum_height=content.setter('height'))
|
||||
for option in prop.options:
|
||||
button = Factory.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, Factory.Widget):
|
||||
content = Factory.Button(text=repr(value))
|
||||
content.bind(on_release=partial(self.highlight_widget, value))
|
||||
elif isinstance(value, Factory.Texture):
|
||||
content = Factory.Image(texture=value)
|
||||
else:
|
||||
content = Factory.Label(text=repr(value))
|
||||
|
||||
elif isinstance(prop, BooleanProperty):
|
||||
state = 'down' if value else 'normal'
|
||||
content = Factory.ToggleButton(text=key, state=state)
|
||||
content.bind(on_release=partial(self.save_property_boolean, widget,
|
||||
key, index))
|
||||
|
||||
self.content.clear_widgets()
|
||||
if content:
|
||||
self.content.add_widget(content)
|
||||
|
||||
def save_property_numeric(self, widget, key, index, instance, value):
|
||||
try:
|
||||
if index >= 0:
|
||||
getattr(widget, key)[index] = float(instance.text)
|
||||
else:
|
||||
setattr(widget, key, float(instance.text))
|
||||
except:
|
||||
pass
|
||||
|
||||
def save_property_text(self, widget, key, index, instance, value):
|
||||
try:
|
||||
if index >= 0:
|
||||
getattr(widget, key)[index] = instance.text
|
||||
else:
|
||||
setattr(widget, key, instance.text)
|
||||
except:
|
||||
pass
|
||||
|
||||
def save_property_boolean(self, widget, key, index, instance, ):
|
||||
try:
|
||||
value = instance.state == 'down'
|
||||
if index >= 0:
|
||||
getattr(widget, key)[index] = value
|
||||
else:
|
||||
setattr(widget, key, value)
|
||||
except:
|
||||
pass
|
||||
|
||||
def save_property_option(self, widget, key, instance, *largs):
|
||||
try:
|
||||
setattr(widget, key, instance.text)
|
||||
except:
|
||||
pass
|
||||
|
||||
def _update_widget_tree_node(self, node, widget, is_open=False):
|
||||
tree = self.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 child is self:
|
||||
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):
|
||||
if not hasattr(self, '_window_node') or not self._window_node:
|
||||
self._window_node = self.widgettree.add_node(
|
||||
TreeViewWidget(text='Window', widget=self.win, is_open=True))
|
||||
|
||||
nodes = self._update_widget_tree_node(self._window_node, self.win,
|
||||
is_open=True)
|
||||
while nodes:
|
||||
ntmp = nodes[:]
|
||||
nodes = []
|
||||
for node in ntmp:
|
||||
nodes += self._update_widget_tree_node(*node)
|
||||
|
||||
self.widgettree.update_selected_widget(self.widget)
|
||||
|
||||
|
||||
def create_inspector(win, ctx, *largs):
|
||||
'''Create an Inspector instance attached to the *ctx* and bound to the
|
||||
Window's :meth:`~kivy.core.window.WindowBase.on_keyboard` event for
|
||||
capturing the keyboard shortcut.
|
||||
|
||||
:Parameters:
|
||||
`win`: A :class:`Window <kivy.core.window.WindowBase>`
|
||||
The application Window to bind to.
|
||||
`ctx`: A :class:`~kivy.uix.widget.Widget` or subclass
|
||||
The Widget to be inspected.
|
||||
|
||||
'''
|
||||
# Dunno why, but if we are creating inspector within the start(), no lang
|
||||
# rules are applied.
|
||||
ctx.inspector = Inspector(win=win)
|
||||
win.bind(children=ctx.inspector.on_window_children,
|
||||
on_keyboard=ctx.inspector.keyboard_shortcut)
|
||||
|
||||
|
||||
def start(win, ctx):
|
||||
ctx.ev_late_create = Clock.schedule_once(
|
||||
partial(create_inspector, win, ctx))
|
||||
|
||||
|
||||
def stop(win, ctx):
|
||||
'''Stop and unload any active Inspectors for the given *ctx*.'''
|
||||
if hasattr(ctx, 'ev_late_create'):
|
||||
ctx.ev_late_create.cancel()
|
||||
del ctx.ev_late_create
|
||||
if hasattr(ctx, 'inspector'):
|
||||
win.unbind(children=ctx.inspector.on_window_children,
|
||||
on_keyboard=ctx.inspector.keyboard_shortcut)
|
||||
win.remove_widget(ctx.inspector)
|
||||
del ctx.inspector
|
||||
Reference in New Issue
Block a user