2012-10-18 21:29:42 +00:00
|
|
|
import kivy
|
|
|
|
kivy.require('1.4.2')
|
2012-10-16 17:44:34 +00:00
|
|
|
import os
|
2012-10-18 01:26:10 +00:00
|
|
|
import sys
|
2012-10-16 17:44:34 +00:00
|
|
|
from kivy.app import App
|
|
|
|
from kivy.factory import Factory
|
|
|
|
from kivy.lang import Builder, Parser, ParserException
|
|
|
|
from kivy.properties import ObjectProperty
|
2012-10-18 21:29:42 +00:00
|
|
|
from kivy.config import Config
|
2012-10-16 17:44:34 +00:00
|
|
|
|
|
|
|
from kivy.uix.boxlayout import BoxLayout
|
|
|
|
from kivy.uix.popup import Popup
|
|
|
|
from kivy.uix.label import Label
|
2012-10-23 19:24:59 +00:00
|
|
|
from kivy.uix.codeinput import CodeInput
|
|
|
|
from kivy.animation import Animation
|
|
|
|
from kivy.clock import Clock
|
2012-10-16 17:44:34 +00:00
|
|
|
|
2012-12-11 13:07:04 +00:00
|
|
|
CATALOG_ROOT = os.path.dirname(__file__)
|
|
|
|
|
2012-10-18 21:29:42 +00:00
|
|
|
Config.set('graphics', 'width', '1024')
|
|
|
|
Config.set('graphics', 'height', '768')
|
|
|
|
|
2012-10-16 17:44:34 +00:00
|
|
|
'''List of classes that need to be instantiated in the factory from .kv files.
|
|
|
|
'''
|
2012-12-11 13:07:04 +00:00
|
|
|
CONTAINER_KVS = os.path.join(CATALOG_ROOT, 'container_kvs')
|
|
|
|
CONTAINER_CLASSES = [c[:-3] for c in os.listdir(CONTAINER_KVS)
|
2012-10-16 17:44:34 +00:00
|
|
|
if c.endswith('.kv')]
|
|
|
|
|
|
|
|
|
|
|
|
class Container(BoxLayout):
|
|
|
|
'''A container is essentially a class that loads its root from a known
|
|
|
|
.kv file.
|
|
|
|
|
|
|
|
The name of the .kv file is taken from the Container's class.
|
|
|
|
We can't just use kv rules because the class may be edited
|
|
|
|
in the interface and reloaded by the user.
|
|
|
|
See :meth: change_kv where this happens.
|
|
|
|
'''
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super(Container, self).__init__(**kwargs)
|
|
|
|
parser = Parser(content=file(self.kv_file).read())
|
|
|
|
widget = Factory.get(parser.root.name)()
|
|
|
|
Builder._apply_rule(widget, parser.root, parser.root)
|
|
|
|
self.add_widget(widget)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def kv_file(self):
|
2012-10-23 19:24:59 +00:00
|
|
|
'''Get the name of the kv file, a lowercase version of the class
|
|
|
|
name.
|
|
|
|
'''
|
2012-12-11 13:07:04 +00:00
|
|
|
return os.path.join(CONTAINER_KVS, self.__class__.__name__ + '.kv')
|
2012-10-16 17:44:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
for class_name in CONTAINER_CLASSES:
|
|
|
|
globals()[class_name] = type(class_name, (Container,), {})
|
|
|
|
|
|
|
|
|
2012-10-23 19:24:59 +00:00
|
|
|
class KivyRenderTextInput(CodeInput):
|
2012-10-18 01:26:10 +00:00
|
|
|
def _keyboard_on_key_down(self, window, keycode, text, modifiers):
|
|
|
|
is_osx = sys.platform == 'darwin'
|
|
|
|
# Keycodes on OSX:
|
|
|
|
ctrl, cmd = 64, 1024
|
|
|
|
key, key_str = keycode
|
|
|
|
|
|
|
|
if text and not key in (self.interesting_keys.keys() + [27]):
|
|
|
|
# This allows *either* ctrl *or* cmd, but not both.
|
|
|
|
if modifiers == ['ctrl'] or (is_osx and modifiers == ['meta']):
|
|
|
|
if key == ord('s'):
|
2012-12-15 03:41:20 +00:00
|
|
|
self.catalog.change_kv(True)
|
2012-10-18 01:26:10 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
super(KivyRenderTextInput, self)._keyboard_on_key_down(
|
|
|
|
window, keycode, text, modifiers)
|
|
|
|
|
|
|
|
|
2012-10-16 17:44:34 +00:00
|
|
|
class Catalog(BoxLayout):
|
|
|
|
'''Catalog of widgets. This is the root widget of the app. It contains
|
|
|
|
a tabbed pain of widgets that can be displayed and a textbox where .kv
|
|
|
|
language files for widgets being demoed can be edited.
|
|
|
|
|
2012-10-23 19:24:59 +00:00
|
|
|
The entire interface for the Catalog is defined in kivycatalog.kv,
|
|
|
|
although individual containers are defined in the container_kvs
|
|
|
|
directory.
|
2012-10-16 17:44:34 +00:00
|
|
|
|
|
|
|
To add a container to the catalog,
|
|
|
|
first create the .kv file in container_kvs
|
|
|
|
The name of the file (sans .kv) will be the name of the widget available
|
|
|
|
inside the kivycatalog.kv
|
|
|
|
Finally modify kivycatalog.kv to add an AccordionItem
|
|
|
|
to hold the new widget.
|
|
|
|
Follow the examples in kivycatalog.kv to ensure the item
|
|
|
|
has an appropriate id and the class has been referenced.
|
|
|
|
|
|
|
|
You do not need to edit any python code, just .kv language files!
|
|
|
|
'''
|
|
|
|
language_box = ObjectProperty()
|
2012-10-18 03:48:34 +00:00
|
|
|
screen_manager = ObjectProperty()
|
2012-10-16 17:44:34 +00:00
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super(Catalog, self).__init__(**kwargs)
|
2012-10-18 03:48:34 +00:00
|
|
|
self.show_kv(None)
|
2012-10-16 17:44:34 +00:00
|
|
|
|
2012-10-18 03:48:34 +00:00
|
|
|
def show_kv(self, object):
|
2012-10-16 17:44:34 +00:00
|
|
|
'''Called when an accordionitem is collapsed or expanded. If it
|
|
|
|
was expanded, we need to show the .kv language file associated with
|
|
|
|
the newly revealed container.'''
|
2012-10-18 03:48:34 +00:00
|
|
|
|
|
|
|
# if object is not passed, it's initialization, we just need to load
|
|
|
|
# the file
|
|
|
|
if object:
|
|
|
|
# one button must always be pressed, even if user presses it again
|
|
|
|
if object.state == "normal":
|
|
|
|
object.state = "down"
|
|
|
|
|
|
|
|
self.screen_manager.current = object.text
|
|
|
|
|
2013-03-31 09:59:37 +00:00
|
|
|
with open(self.screen_manager.current_screen.children[0].kv_file) as file:
|
2012-10-18 03:48:34 +00:00
|
|
|
self.language_box.text = file.read()
|
2012-11-03 21:18:35 +00:00
|
|
|
# reset undo/redo history
|
|
|
|
self.language_box.reset_undo()
|
2012-10-16 17:44:34 +00:00
|
|
|
|
2012-10-23 19:24:59 +00:00
|
|
|
def schedule_reload(self):
|
|
|
|
if self.auto_reload:
|
|
|
|
Clock.unschedule(self.change_kv)
|
|
|
|
Clock.schedule_once(self.change_kv, 2)
|
|
|
|
|
|
|
|
def change_kv(self, *largs):
|
2012-10-16 17:44:34 +00:00
|
|
|
'''Called when the update button is clicked. Needs to update the
|
|
|
|
interface for the currently active kv widget, if there is one based
|
|
|
|
on the kv file the user entered. If there is an error in their kv
|
|
|
|
syntax, show a nice popup.'''
|
2012-10-23 19:24:59 +00:00
|
|
|
|
2013-03-31 09:59:37 +00:00
|
|
|
kv_container = self.screen_manager.current_screen.children[0]
|
2012-10-18 03:48:34 +00:00
|
|
|
try:
|
|
|
|
parser = Parser(content=self.language_box.text.encode('utf8'))
|
|
|
|
kv_container.clear_widgets()
|
|
|
|
widget = Factory.get(parser.root.name)()
|
|
|
|
Builder._apply_rule(widget, parser.root, parser.root)
|
|
|
|
kv_container.add_widget(widget)
|
|
|
|
except (SyntaxError, ParserException) as e:
|
2012-12-15 03:41:20 +00:00
|
|
|
self.show_error(e)
|
|
|
|
except Exception, e:
|
|
|
|
self.show_error(e)
|
|
|
|
|
|
|
|
def show_error(self, e):
|
|
|
|
self.info_label.text = str(e)
|
|
|
|
self.anim = Animation(top=190.0, opacity=1, d=2, t='in_back') +\
|
|
|
|
Animation(top=190.0, d=3) +\
|
|
|
|
Animation(top=0, opacity=0, d=2)
|
|
|
|
self.anim.start(self.info_label)
|
2012-10-16 17:44:34 +00:00
|
|
|
|
|
|
|
class KivyCatalogApp(App):
|
|
|
|
'''The kivy App that runs the main root. All we do is build a catalog
|
|
|
|
widget into the root.'''
|
|
|
|
def build(self):
|
|
|
|
return Catalog()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
KivyCatalogApp().run()
|