diff --git a/examples/demo/kivycatalog/DroidSansMono.ttf b/examples/demo/kivycatalog/DroidSansMono.ttf new file mode 100644 index 000000000..6e79dad17 Binary files /dev/null and b/examples/demo/kivycatalog/DroidSansMono.ttf differ diff --git a/examples/demo/kivycatalog/README b/examples/demo/kivycatalog/README new file mode 100644 index 000000000..6c3b5dac0 --- /dev/null +++ b/examples/demo/kivycatalog/README @@ -0,0 +1,18 @@ +This is the Kivy Catalog viewer. It serves two purposes: + +1. To showcase the various widgets available in Kivy +2. To allow interactive editing of Kivy language files + to get immediate feedback as to how they work + +To use it, you'll need to install Kivy and it's dependencies +a la http://kivy.org/docs/installation/installation.html Then run +python main.py and browse or edit widgets to your heart's content. + +Known bugs: +* The DropDown item I had tested completely crashes Kivy +* The GridLayout example could use some extra features +* If you try to start the app with focused set to true, weird stuff happens. + but it works fine if you set focused to true and press render. +* Video playback doesn't work for me, but this may be a dependency issue +* Popups are displayed inline +* Some widgets are still missing \ No newline at end of file diff --git a/examples/demo/kivycatalog/container_kvs/AnchorLayoutContainer.kv b/examples/demo/kivycatalog/container_kvs/AnchorLayoutContainer.kv new file mode 100644 index 000000000..5890fac96 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/AnchorLayoutContainer.kv @@ -0,0 +1,15 @@ +#:kivy 1.4 + +AnchorLayout: + anchor_x: "right" + anchor_y: "bottom" + Button: + text: "Button 1" + size_hint: .2, .4 + Button: + text: "Button 2" + size_hint: .4, .2 + + Button: + text: "Button 3" + size_hint: .2, .2 diff --git a/examples/demo/kivycatalog/container_kvs/BoxLayoutContainer.kv b/examples/demo/kivycatalog/container_kvs/BoxLayoutContainer.kv new file mode 100644 index 000000000..5cee03b16 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/BoxLayoutContainer.kv @@ -0,0 +1,14 @@ +#:kivy 1.4 + +BoxLayout: + orientation: 'vertical' + padding: 20 + spacing: 10 + Button: + text: "Button 1" + size_hint: 1, None + Button: + text: "Button 2" + size_hint: 1, 0.5 + Button: + text: "Button 3" \ No newline at end of file diff --git a/examples/demo/kivycatalog/container_kvs/ButtonContainer.kv b/examples/demo/kivycatalog/container_kvs/ButtonContainer.kv new file mode 100644 index 000000000..9b719776c --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/ButtonContainer.kv @@ -0,0 +1,27 @@ +#:kivy 1.4 + +GridLayout: + cols: 2 + Button: + text: "Button 1" + Button: + text: "Button 2" + font_size: 24 + Button: + text: "Button 3" + background_color: .7, .7, 1, 1 + Button: + text: "Button 4" + on_press: self.text = 'pressed' + on_release: self.text = 'Button 4' + ToggleButton: + text: "A toggle button" + ToggleButton: + text: "a toggle button in a group" + group: "money" + ToggleButton: + text: "A toggle in the down state" + state: "down" + ToggleButton: + text: "another toggle button in a group" + group: "money" diff --git a/examples/demo/kivycatalog/container_kvs/CheckBoxContainer.kv b/examples/demo/kivycatalog/container_kvs/CheckBoxContainer.kv new file mode 100644 index 000000000..172b0ac39 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/CheckBoxContainer.kv @@ -0,0 +1,27 @@ +#:kivy 1.4 + +GridLayout: + cols: 2 + CheckBox: + Label: + text: "A checkbox" + active: True + CheckBox: + Label: + text: "Another checkbox" + CheckBox: + group: "money" + Label: + text: "A radio in a group" + CheckBox: + group: "money" + Label: + text: "Another radio in same group" + active: True + Switch: + Label: + text: "A Switch" + Switch: + active: True + Label: + text: "An active switch" \ No newline at end of file diff --git a/examples/demo/kivycatalog/container_kvs/FileChooserContainer.kv b/examples/demo/kivycatalog/container_kvs/FileChooserContainer.kv new file mode 100644 index 000000000..8a44fdaeb --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/FileChooserContainer.kv @@ -0,0 +1,19 @@ +#:kivy 1.4 + +BoxLayout: + # Double as a Tabbed Panel Demo! + TabbedPanel: + tab_pos: "top_right" + default_tab_text: "List View" + default_tab_content: list_view_tab + + TabbedPanelHeader: + text: 'Icon View' + content: icon_view_tab + + FileChooserListView: + id: list_view_tab + + FileChooserIconView: + id: icon_view_tab + show_hidden: True \ No newline at end of file diff --git a/examples/demo/kivycatalog/container_kvs/FloatLayoutContainer.kv b/examples/demo/kivycatalog/container_kvs/FloatLayoutContainer.kv new file mode 100644 index 000000000..303cec6fc --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/FloatLayoutContainer.kv @@ -0,0 +1,16 @@ +#:kivy 1.4 + +FloatLayout: + Button: + text: "Button 1" + pos: 100, 100 + size_hint: .2, .4 + Button: + text: "Button 2" + pos: 200, 200 + size_hint: .4, .2 + + Button: + text: "Button 3" + pos_hint: {'x': .8, 'y': .6} + size_hint: .2, .2 diff --git a/examples/demo/kivycatalog/container_kvs/GridLayoutContainer.kv b/examples/demo/kivycatalog/container_kvs/GridLayoutContainer.kv new file mode 100644 index 000000000..f382b3c29 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/GridLayoutContainer.kv @@ -0,0 +1,20 @@ +#:kivy 1.4 + +GridLayout: + cols: 2 + Button: + text: "Button 1" + size_hint_x: None + width: 100 + Button: + text: "Button 2" + Button: + text: "Button 3" + size_hint_x: None + Button: + text: "Button 4" + Button: + text: "Button 5" + Button: + text: "Button 6" + size_hint_x: None diff --git a/examples/demo/kivycatalog/container_kvs/LabelContainer.kv b/examples/demo/kivycatalog/container_kvs/LabelContainer.kv new file mode 100644 index 000000000..2eb5c8333 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/LabelContainer.kv @@ -0,0 +1,21 @@ +#:kivy 1.4 + +GridLayout: + rows: 2 + Label: + text: "Label 1" + Label: + text: "Label with\nmultiple lines" + Label: + text: "Label [color=ff3333]with[/color] [color=3333ff][b]markup[/b][/color]" + markup: True + Label: + text: "Label with [ref=reference]reference[/ref]" + markup: True + on_ref_press: self.text = "ref clicked" + Label: + text: "different font" + bold: True + font_name: "DroidSansMono.ttf" + font_size: 32 + valign: "bottom" \ No newline at end of file diff --git a/examples/demo/kivycatalog/container_kvs/MediaContainer.kv b/examples/demo/kivycatalog/container_kvs/MediaContainer.kv new file mode 100644 index 000000000..ed9467a57 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/MediaContainer.kv @@ -0,0 +1,9 @@ +#:kivy 1.4 + +BoxLayout: + orientation: "vertical" + Image: + source: "../../widgets/softboy.png" + Video: + source: "../../widgets/softboy.avi" + state: "play" \ No newline at end of file diff --git a/examples/demo/kivycatalog/container_kvs/PlaygroundContainer.kv b/examples/demo/kivycatalog/container_kvs/PlaygroundContainer.kv new file mode 100644 index 000000000..9cdb3352d --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/PlaygroundContainer.kv @@ -0,0 +1,6 @@ +#:kivy 1.4 + +Label: + text_size: self.width-60, self.height-60 + valign: "middle" + text: "The Kivy Catalog is an interactive showcase of Kivy Widgets defined in the Kivy language. For each widget you see, you can directly edit the .kv language syntax to see what effects your changes have on the widget. Click 'Render' or hit 'Ctrl-S' to view your changes.\n\nThere is also a playground on this tab where you can test your Kivy language code directly. This is beta software. The basics seem to work, but some widgets are missing or don't have the ideal .kv representation. Not all widgets are represented yet. It is trivial to add a new .kv file to the interface.\n\nPull requests are welcome." diff --git a/examples/demo/kivycatalog/container_kvs/PopupContainer.kv b/examples/demo/kivycatalog/container_kvs/PopupContainer.kv new file mode 100644 index 000000000..39a9fafdc --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/PopupContainer.kv @@ -0,0 +1,23 @@ +#:kivy 1.4 + +BoxLayout: + orientation: "vertical" + Bubble: + size_hint: (None, None) + size: (150, 50) + pos_hint: {'center_x': .5, 'y': .6} + arrow_pos: 'bottom_mid' + orientation: 'horizontal' + BubbleButton: + text: 'Click' + BubbleButton: + text: 'A' + BubbleButton: + text: 'Bubble' + Popup: + id: popup + title: "An example popup" + content: popupcontent + Label: + id: popupcontent + text: "Some text\nin the popup" diff --git a/examples/demo/kivycatalog/container_kvs/ProgressBarContainer.kv b/examples/demo/kivycatalog/container_kvs/ProgressBarContainer.kv new file mode 100644 index 000000000..d5f8ff8d7 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/ProgressBarContainer.kv @@ -0,0 +1,17 @@ +#:kivy 1.4 + +BoxLayout: + orientation: 'vertical' + padding: 50 + ProgressBar: + id: bar + value: 140 + max: 300 + Slider: + id: slider + max: 200 + value: 140 + on_value: bar.value = self.value + Slider: + orientation: 'vertical' + on_value: slider.value = self.value diff --git a/examples/demo/kivycatalog/container_kvs/RestContainer.kv b/examples/demo/kivycatalog/container_kvs/RestContainer.kv new file mode 100644 index 000000000..3a23078b0 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/RestContainer.kv @@ -0,0 +1,5 @@ +#:kivy 1.4 + +BoxLayout: + RstDocument: + text: ".. _top:\n\nHello world\n===========\n\nThis is an **emphased text**, some ``interpreted text``.\nAnd this is a reference to top_::\n\n $ print 'Hello world'\n" diff --git a/examples/demo/kivycatalog/container_kvs/ScatterContainer.kv b/examples/demo/kivycatalog/container_kvs/ScatterContainer.kv new file mode 100644 index 000000000..3b94f6124 --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/ScatterContainer.kv @@ -0,0 +1,16 @@ +#:kivy 1.4 + +FloatLayout: + Scatter: + size_hint: None, None + size: 100, 100 + pos: 100, 100 + Image: + source: "../../widgets/softboy.png" + Scatter: + size_hint: None, None + size: 100, 100 + pos: 100, 100 + do_rotation: False + Label: + text: "something" diff --git a/examples/demo/kivycatalog/container_kvs/SelectorsContainer.kv b/examples/demo/kivycatalog/container_kvs/SelectorsContainer.kv new file mode 100644 index 000000000..342e679ad --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/SelectorsContainer.kv @@ -0,0 +1,10 @@ +#:kivy 1.4 + +BoxLayout: + orientation: 'vertical' + Spinner: + text: "Work" + values: "Work", "Home", "Mobile", "Skype" + size_hint: (None, None) + size: (100, 44) + # Wanted to put DropDown here, too, but it seems not to be working too well when loaded from .kv diff --git a/examples/demo/kivycatalog/container_kvs/StackLayoutContainer.kv b/examples/demo/kivycatalog/container_kvs/StackLayoutContainer.kv new file mode 100644 index 000000000..867261d1a --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/StackLayoutContainer.kv @@ -0,0 +1,25 @@ +#:kivy 1.4 + +StackLayout: + orientation: 'tb-lr' + padding: 10 + spacing: 5 + Button: + text: "Button 1" + size_hint: .2, .4 + width: 100 + Button: + text: "Button 2" + size_hint: .2, .4 + Button: + text: "Button 3" + size_hint: .2, .4 + Button: + text: "Button 4" + size_hint: .2, .4 + Button: + text: "Button 5" + size_hint: .2, .4 + Button: + text: "Button 6" + size_hint: .2, .4 \ No newline at end of file diff --git a/examples/demo/kivycatalog/container_kvs/TextContainer.kv b/examples/demo/kivycatalog/container_kvs/TextContainer.kv new file mode 100644 index 000000000..8f23edf4d --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/TextContainer.kv @@ -0,0 +1,20 @@ +#:kivy 1.4 + +BoxLayout: + orientation: "vertical" + TextInput: + text: "Single Line Input" + multiline: False + TextInput: + text: "Text Input, start typing here\nmultiline\nsupport" + background_color: .8, .8, 0, 1 + size_hint: 1, 3 + TextInput: + text: "Password (but you can't see it)" + password: True + multiline: False + on_text: viewer.text = self.text + TextInput: + id: viewer + read_only: True + text: "edit the password to see it here" diff --git a/examples/demo/kivycatalog/kivycatalog.kv b/examples/demo/kivycatalog/kivycatalog.kv new file mode 100644 index 000000000..c97b33af4 --- /dev/null +++ b/examples/demo/kivycatalog/kivycatalog.kv @@ -0,0 +1,120 @@ +#:kivy 1.4 + +[ContainerToggle@ToggleButton]: + group: "container_toggle" + text: ctx.text + on_press: root.parent.parent.parent.show_kv(*args) + state: ctx.state if hasattr(ctx, "state") else "normal" + +: + language_box: language_box + screen_manager: screen_manager + BoxLayout: + BoxLayout: + size_hint: None, 1 + orientation: "vertical" + ContainerToggle: + text: "Welcome" + state: "down" + ContainerToggle: + text: "Float Layout" + ContainerToggle: + text: "Box Layout" + ContainerToggle: + text: "Anchor Layout" + ContainerToggle: + text: "Grid Layout" + ContainerToggle: + text: "Stack Layout" + ContainerToggle: + text: "Buttons" + ContainerToggle: + text: "Labels" + ContainerToggle: + text: "Booleans" + ContainerToggle: + text: "Progress Bar" + ContainerToggle: + text: "Media" + ContainerToggle: + text: "Text" + ContainerToggle: + text: "Popups" + ContainerToggle: + text: "Selectors" + ContainerToggle: + text: "File Choosers" + ContainerToggle: + text: "Scatter" + ContainerToggle: + text: "ReST" + ScreenManager: + id: screen_manager + Screen: + name: "Welcome" + PlaygroundContainer: + Screen: + name: "Float Layout" + FloatLayoutContainer + Screen: + name: "Box Layout" + BoxLayoutContainer: + Screen: + name: "Anchor Layout" + AnchorLayoutContainer: + Screen: + name: "Grid Layout" + GridLayoutContainer: + Screen: + name: "Stack Layout" + StackLayoutContainer: + Screen: + name: "Buttons" + ButtonContainer: + Screen: + name: "Labels" + LabelContainer: + Screen: + name: "Booleans" + CheckBoxContainer: + Screen: + name: "Progress Bar" + ProgressBarContainer: + Screen: + name: "Media" + MediaContainer: + Screen: + name: "Text" + TextContainer: + Screen: + name: "Popups" + PopupContainer: + Screen: + name: "Selectors" + SelectorsContainer: + Screen: + name: "File Choosers" + FileChooserContainer: + Screen: + name: "Scatter" + ScatterContainer: + Screen: + name: "ReST" + RestContainer: + + BoxLayout: + id: bl + orientation: "vertical" + size_hint: None, 1 + width: 400 + KivyRenderTextInput: + text_size: self.width-20, self.height-20 + font_name: "DroidSansMono.ttf" + valign: "top" + id: language_box + text: "This box will display the kivy language for whatever has been selected" + Button: + size_hint: 1, None + height: 50 + text: "Render" + on_press: root.change_kv(*args) \ No newline at end of file diff --git a/examples/demo/kivycatalog/main.py b/examples/demo/kivycatalog/main.py new file mode 100644 index 000000000..6a02fc70e --- /dev/null +++ b/examples/demo/kivycatalog/main.py @@ -0,0 +1,153 @@ +import kivy +kivy.require('1.4.2') +import os +import sys +from kivy.app import App +from kivy.factory import Factory +from kivy.lang import Builder, Parser, ParserException +from kivy.properties import ObjectProperty +from kivy.config import Config + +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.popup import Popup +from kivy.uix.label import Label +from kivy.uix.textinput import TextInput + +print Config.get('graphics', 'width') + +Config.set('graphics', 'width', '1024') +Config.set('graphics', 'height', '768') + +'''List of classes that need to be instantiated in the factory from .kv files. +''' +CONTAINER_CLASSES = [c[:-3] for c in os.listdir('container_kvs') + 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): + '''Get the name of the kv file, a lowercase version of the class name.''' + return os.path.join('container_kvs', + self.__class__.__name__ + ".kv") + + +for class_name in CONTAINER_CLASSES: + globals()[class_name] = type(class_name, (Container,), {}) + + +class KivyRenderTextInput(TextInput): + 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'): + self.parent.parent.parent.change_kv(True) + return + + super(KivyRenderTextInput, self)._keyboard_on_key_down( + window, keycode, text, modifiers) + + +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. + + The entire interface for the Catalog is defined in kivycatalog.kv, although + individual containers are defined in the container_kvs directory. + + 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() + screen_manager = ObjectProperty() + + def __init__(self, **kwargs): + super(Catalog, self).__init__(**kwargs) + self.show_kv(None) + + def show_kv(self, object): + '''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.''' + + # 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 + + with open(self.screen_manager.current_screen.content.children[ + 0].kv_file) as file: + self.language_box.text = file.read() + + def change_kv(self, button): + '''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.''' + kv_container = self.screen_manager.current_screen.content.children[0] + 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: + content = Label(text=str(e), text_size=(350, None)) + popup = Popup(title="Parse Error in Kivy Language Markup", + content=content, text_size=(350, None), + size_hint=(None, None), size=(400, 400)) + popup.open() + except: + import traceback + traceback.print_exc() + popup = Popup(title="Boom", + content=Label(text="Something horrible happened while parsing your Kivy Language", text_size=(350, None)), + text_size=(350, None), + size_hint=(None, None), size=(400, 400)) + popup.open() + + +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()