diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..baa845e61 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: python +python: + - "2.6" + - "2.7" +# command to install dependencies +before_install: + - sudo apt-get update + - sudo apt-get install python-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev libsdl1.2-dev libsmpeg-dev python-numpy libportmidi-dev ffmpeg libswscale-dev libavformat-dev libavcodec-dev libjpeg-dev libtiff4-dev libx11-6 libX11-dev + - sudo apt-get install python-setuptools python-opengl gstreamer0.10-plugins-good build-essential libgl1-mesa-dev libgles2-mesa-dev mercurial + - sudo apt-get install xvfb + +install: + - pip install hg+http://bitbucket.org/pygame/pygame + - pip install --upgrade cython pil --use-mirrors + - make + +# command to run tests +script: + - xvfb-run -s "+extension GLX" make test diff --git a/doc/sources/guide/readingdoc.rst b/doc/sources/guide/readingdoc.rst index d54ffeb63..1d6e8a62f 100644 --- a/doc/sources/guide/readingdoc.rst +++ b/doc/sources/guide/readingdoc.rst @@ -16,7 +16,7 @@ it will be generated like this: .. image:: images/api-button.jpg -It should be read like this: the "Button" class is into the "kivy.uix.button" +It should be read like this: the "Button" class is in the "kivy.uix.button" module. So if you want to import that class in your code, write that:: from kivy.uix.button import Button diff --git a/doc/sources/images/codeinput.jpg b/doc/sources/images/codeinput.jpg new file mode 100644 index 000000000..48d8f0cc1 Binary files /dev/null and b/doc/sources/images/codeinput.jpg differ diff --git a/doc/sources/sphinxext/preprocess.py b/doc/sources/sphinxext/preprocess.py index b7d1d5b5a..a49665a8c 100644 --- a/doc/sources/sphinxext/preprocess.py +++ b/doc/sources/sphinxext/preprocess.py @@ -106,8 +106,8 @@ def callback_signature(app, what, name, obj, options, signature, def setup(app): import kivy - sys.path += [join(dirname(kivy.__file__), 'tools', 'highlight', 'pygments')] - from lexer_kivy import KivyLexer + sys.path += [join(dirname(kivy.__file__), 'extras')] + from highlight import KivyLexer app.add_lexer('kv', KivyLexer()) app.add_autodocumenter(CythonMethodDocumenter) 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() diff --git a/examples/widgets/codeinput.py b/examples/widgets/codeinput.py new file mode 100644 index 000000000..6a16894fd --- /dev/null +++ b/examples/widgets/codeinput.py @@ -0,0 +1,82 @@ +from kivy.app import App +from kivy.extras.highlight import KivyLexer +from kivy.factory import Factory +import pygments + +example_text = ''' +---------------------Python---------------------------------- +import kivy +kivy.require('1.0.6') # replace with your current kivy version ! +from kivy.app import App +from kivy.uix.button import Button + +class MyApp(App): + def build(self): + return Button(text='Hello World') + +if __name__ == '__main__': + MyApp().run() +----------------------Java------------------------------------ + +public static byte toUnsignedByte(int intVal) { + byte byteVal; + return (byte)(intVal & 0xFF); +} +---------------------kv lang------------------------- +#:kivy 1.0 + +: + canvas: + Color: + rgb: .5, .5, .5 + Rectangle: + pos: self.pos + size: self.size +-----------------HTML----------------------------- + +
+ + + +''' + + +class CodeInputTest(App): + def build(self): + b = Factory.BoxLayout(orientation='vertical') + languages = Factory.Spinner( + text='language', + values=sorted(['KvLexer', ] + pygments.lexers.LEXERS.keys()), + size_hint_y=None, + height='30pt') + + languages.bind(text=self.change_lang) + b.add_widget(languages) + + self.codeinput = Factory.CodeInput( + lexer=KivyLexer(), + font_name='data/fonts/DroidSansMono.ttf', font_size=12, + text=example_text) + + b.add_widget(self.codeinput) + + return b + + def change_lang(self, instance, l): + if l == 'KvLexer': + lx = KivyLexer() + + else: + lx = pygments.lexers.get_lexer_by_name( + pygments.lexers.LEXERS[l][2][0]) + + self.codeinput.lexer = lx + + +CodeInputTest().run() diff --git a/kivy/atlas.py b/kivy/atlas.py index 6121ece6d..51f2aa5b4 100644 --- a/kivy/atlas.py +++ b/kivy/atlas.py @@ -192,7 +192,7 @@ class Atlas(EventDispatcher): self.textures = textures @staticmethod - def create(outname, filenames, size, padding=1): + def create(outname, filenames, size, padding=2): '''This method can be used to create manually an atlas from a set of images. @@ -204,11 +204,17 @@ class Atlas(EventDispatcher): List of filename to put in the atlas `size`: int Size of an atlas image - `padding`: int, default to 1 - Padding to put around each image. Care, if you put 0, they might - be some issues with OpenGL, because by default, Kivy texture are - using GL_CLAMP_TO_EDGE, and the edge is another image than - the image you'll want to display. + `padding`: int, default to 2 + Padding to put around each image. + + Be careful. If you're using a padding < 2, you might get issues + with border of the images. Because of the OpenGL linearization, + it might take the pixels of the adjacent image. + + If you're using a padding >= 2, we'll automatically generate a + "border" of 1px of your image, around the image. If you look at + the result, don't be scared if the image inside it are not + exactly the same as yours :). ''' # Thanks to # omnisaurusgames.com/2011/06/texture-atlas-generation-using-python/ @@ -232,7 +238,6 @@ class Atlas(EventDispatcher): # the freebox tuple format is: outidx, x, y, w, h freeboxes = [(0, 0, 0, size, size)] numoutimages = 1 - padding = 1 # full boxes are areas where we have placed images in the atlas # the full box tuple format is: image, outidx, x, y, w, h, filename @@ -291,7 +296,16 @@ class Atlas(EventDispatcher): outimages = [Image.new('RGBA', (size, size)) for i in range(0, int(numoutimages))] for fb in fullboxes: - outimages[fb[1]].paste(fb[0], (fb[2], fb[3])) + x, y = fb[2], fb[3] + out = outimages[fb[1]] + out.paste(fb[0], (fb[2], fb[3])) + w, h = fb[0].size + if padding > 1: + out.paste(fb[0].crop((0, 0, w, 1)), (x, y - 1)) + out.paste(fb[0].crop((0, h - 1, w, h)), (x, y + h)) + out.paste(fb[0].crop((0, 0, 1, h)), (x - 1, y)) + out.paste(fb[0].crop((w - 1, 0, w, h)), (x + w, y)) + # save the output images for idx, outimage in enumerate(outimages): diff --git a/kivy/data/images/defaulttheme-0.png b/kivy/data/images/defaulttheme-0.png index 070430af8..066afd936 100644 Binary files a/kivy/data/images/defaulttheme-0.png and b/kivy/data/images/defaulttheme-0.png differ diff --git a/kivy/data/images/defaulttheme.atlas b/kivy/data/images/defaulttheme.atlas index 96177a659..66c44a36a 100644 --- a/kivy/data/images/defaulttheme.atlas +++ b/kivy/data/images/defaulttheme.atlas @@ -1 +1 @@ -{"defaulttheme-0.png": {"player-play-overlay": [306, 396, 117, 115], "spinner_pressed": [91, 124, 29, 37], "progressbar_background": [220, 137, 24, 24], "media-playback-pause": [448, 344, 48, 48], "tab_btn_pressed": [465, 178, 32, 32], "image-missing": [399, 344, 48, 48], "filechooser_selected": [187, 393, 118, 118], "audio-volume-muted": [350, 344, 48, 48], "sliderv_background": [473, 404, 37, 41], "tab": [332, 228, 96, 32], "close": [490, 491, 20, 20], "ring": [1, 326, 185, 185], "vkeyboard_key_down": [121, 129, 32, 32], "vkeyboard_background": [187, 328, 64, 64], "checkbox_off": [300, 178, 32, 32], "bubble_arrow": [148, 162, 16, 10], "player-background": [228, 222, 103, 103], "bubble": [424, 446, 65, 65], "spinner": [61, 124, 29, 37], "sliderh_background": [148, 173, 41, 37], "audio-volume-medium": [301, 344, 48, 48], "media-playback-start": [1, 162, 48, 48], "tab_btn": [432, 178, 32, 32], "bubble_btn_pressed": [267, 178, 32, 32], "tree_closed": [490, 470, 20, 20], "switch-background": [429, 228, 83, 32], "filechooser_file": [332, 261, 64, 64], "checkbox_radio_off": [366, 178, 32, 32], "vkeyboard_key_normal": [154, 129, 32, 32], "checkbox_radio_on": [399, 178, 32, 32], "checkbox_on": [333, 178, 32, 32], "tree_opened": [490, 449, 20, 20], "button_pressed": [31, 124, 29, 37], "media-playback-stop": [50, 162, 48, 48], "audio-volume-high": [424, 397, 48, 48], "audio-volume-low": [252, 344, 48, 48], "bubble_btn": [234, 178, 32, 32], "modalview-background": [462, 271, 45, 54], "button": [1, 124, 29, 37], "progressbar": [187, 137, 32, 24], "switch-button": [190, 178, 43, 32], "filechooser_folder": [397, 261, 64, 64], "slider_cursor": [99, 162, 48, 48], "textinput_active": [1, 211, 114, 114], "textinput": [116, 214, 111, 111]}} \ No newline at end of file +{"defaulttheme-0.png": {"player-play-overlay": [309, 395, 117, 115], "spinner_pressed": [384, 170, 29, 37], "progressbar_background": [483, 183, 24, 24], "media-playback-pause": [52, 159, 48, 48], "tab_btn_pressed": [138, 125, 32, 32], "image-missing": [2, 159, 48, 48], "filechooser_selected": [189, 392, 118, 118], "audio-volume-muted": [452, 342, 48, 48], "sliderv_background": [252, 166, 37, 41], "tab": [336, 225, 96, 32], "close": [240, 137, 20, 20], "ring": [2, 325, 185, 185], "vkeyboard_key_down": [172, 125, 32, 32], "vkeyboard_background": [189, 326, 64, 64], "checkbox_off": [449, 175, 32, 32], "bubble_arrow": [495, 500, 16, 10], "player-background": [231, 220, 103, 103], "bubble": [428, 445, 65, 65], "spinner": [353, 170, 29, 37], "sliderh_background": [468, 286, 41, 37], "audio-volume-medium": [402, 342, 48, 48], "media-playback-start": [102, 159, 48, 48], "tab_btn": [104, 125, 32, 32], "bubble_btn_pressed": [415, 175, 32, 32], "tree_closed": [262, 137, 20, 20], "switch-background": [428, 411, 83, 32], "filechooser_file": [336, 259, 64, 64], "checkbox_radio_off": [36, 125, 32, 32], "vkeyboard_key_normal": [206, 125, 32, 32], "checkbox_radio_on": [70, 125, 32, 32], "checkbox_on": [2, 125, 32, 32], "tree_opened": [284, 137, 20, 20], "button_pressed": [322, 170, 29, 37], "media-playback-stop": [152, 159, 48, 48], "audio-volume-high": [302, 342, 48, 48], "audio-volume-low": [352, 342, 48, 48], "bubble_btn": [479, 225, 32, 32], "modalview-background": [255, 336, 45, 54], "button": [291, 170, 29, 37], "progressbar": [468, 260, 32, 24], "switch-button": [434, 225, 43, 32], "filechooser_folder": [402, 259, 64, 64], "slider_cursor": [202, 159, 48, 48], "textinput_active": [2, 209, 114, 114], "textinput": [118, 212, 111, 111]}} \ No newline at end of file diff --git a/kivy/extras/__init__.py b/kivy/extras/__init__.py new file mode 100644 index 000000000..8d1c8b69c --- /dev/null +++ b/kivy/extras/__init__.py @@ -0,0 +1 @@ + diff --git a/kivy/tools/highlight/pygments/lexer_kivy.py b/kivy/extras/highlight.py similarity index 97% rename from kivy/tools/highlight/pygments/lexer_kivy.py rename to kivy/extras/highlight.py index 2b62b01c3..c83b3193c 100644 --- a/kivy/tools/highlight/pygments/lexer_kivy.py +++ b/kivy/extras/highlight.py @@ -1,3 +1,5 @@ +'''Pygments lexer for kv language +''' from pygments.lexer import RegexLexer, bygroups, using from pygments.lexers.agile import PythonLexer from pygments import highlight diff --git a/kivy/factory_registers.py b/kivy/factory_registers.py index e020e7ce5..0ff43a79d 100644 --- a/kivy/factory_registers.py +++ b/kivy/factory_registers.py @@ -81,6 +81,7 @@ r('Bubble', module='kivy.uix.bubble') r('BubbleButton', module='kivy.uix.bubble') r('Camera', module='kivy.uix.camera') r('Carousel', module='kivy.uix.carousel') +r('CodeInput', module='kivy.uix.codeinput') r('CheckBox', module='kivy.uix.checkbox') r('DropDown', module='kivy.uix.dropdown') r('FloatLayout', module='kivy.uix.floatlayout') diff --git a/kivy/input/postproc/__init__.py b/kivy/input/postproc/__init__.py index 562cb4644..ad3058f2d 100644 --- a/kivy/input/postproc/__init__.py +++ b/kivy/input/postproc/__init__.py @@ -7,10 +7,10 @@ Input Postprocessing __all__ = ('kivy_postproc_modules', ) import os -from doubletap import InputPostprocDoubleTap -from ignorelist import InputPostprocIgnoreList -from retaintouch import InputPostprocRetainTouch -from dejitter import InputPostprocDejitter +from kivy.input.postproc.doubletap import InputPostprocDoubleTap +from kivy.input.postproc.ignorelist import InputPostprocIgnoreList +from kivy.input.postproc.retaintouch import InputPostprocRetainTouch +from kivy.input.postproc.dejitter import InputPostprocDejitter # Mapping of ID to module kivy_postproc_modules = {} diff --git a/kivy/interactive.py b/kivy/interactive.py index 063625c28..b07f9f03b 100644 --- a/kivy/interactive.py +++ b/kivy/interactive.py @@ -43,7 +43,7 @@ can be quickly listed by using the '.' operator and pressing 'tab.' Try this code in an Ipython shell.:: from kivy.interactive import InteractiveLauncher - from kivy.app imort App + from kivy.app import App from kivy.uix.widget import Widget from kivy.graphics import Color, Ellipse diff --git a/kivy/tests/test_filechooser.py b/kivy/tests/test_filechooser.py index 518ce8416..9a14a8e57 100644 --- a/kivy/tests/test_filechooser.py +++ b/kivy/tests/test_filechooser.py @@ -1,4 +1,4 @@ -from common import GraphicUnitTest +from kivy.tests.common import GraphicUnitTest class FileChooserTestCase(GraphicUnitTest): diff --git a/kivy/tests/test_graphics.py b/kivy/tests/test_graphics.py index 372da9535..6afef0f4f 100644 --- a/kivy/tests/test_graphics.py +++ b/kivy/tests/test_graphics.py @@ -5,7 +5,7 @@ Graphics tests Testing the simple vertex instructions ''' -from common import GraphicUnitTest +from kivy.tests.common import GraphicUnitTest class VertexInstructionTestCase(GraphicUnitTest): diff --git a/kivy/tests/test_issue_609.py b/kivy/tests/test_issue_609.py index 90969f22b..d7b6d1be5 100644 --- a/kivy/tests/test_issue_609.py +++ b/kivy/tests/test_issue_609.py @@ -1,4 +1,4 @@ -from common import GraphicUnitTest +from kivy.tests.common import GraphicUnitTest class Issue609(GraphicUnitTest): diff --git a/kivy/tests/test_uix_anchorlayout.py b/kivy/tests/test_uix_anchorlayout.py index 9dde6dcd1..e893e7efd 100644 --- a/kivy/tests/test_uix_anchorlayout.py +++ b/kivy/tests/test_uix_anchorlayout.py @@ -3,7 +3,7 @@ Anchor layout unit test ======================= ''' -from common import GraphicUnitTest +from kivy.tests.common import GraphicUnitTest class UIXAnchorLayoutTestcase(GraphicUnitTest): diff --git a/kivy/tests/test_uix_boxlayout.py b/kivy/tests/test_uix_boxlayout.py index c9791085d..8d450c33b 100644 --- a/kivy/tests/test_uix_boxlayout.py +++ b/kivy/tests/test_uix_boxlayout.py @@ -6,7 +6,7 @@ Order matter. On the screen, most of example must have the red->blue->green order. ''' -from common import GraphicUnitTest +from kivy.tests.common import GraphicUnitTest class UIXBoxLayoutTestcase(GraphicUnitTest): diff --git a/kivy/tests/test_uix_widget.py b/kivy/tests/test_uix_widget.py index eab662057..92375a6a8 100644 --- a/kivy/tests/test_uix_widget.py +++ b/kivy/tests/test_uix_widget.py @@ -1,4 +1,4 @@ -from common import GraphicUnitTest +from kivy.tests.common import GraphicUnitTest class UIXWidgetTestCase(GraphicUnitTest): diff --git a/kivy/tools/highlight/pygments/__init__.py b/kivy/tools/highlight/pygments/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/kivy/uix/codeinput.py b/kivy/uix/codeinput.py new file mode 100644 index 000000000..67f227ce4 --- /dev/null +++ b/kivy/uix/codeinput.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +''' +Code Input +========== + +.. versionadded:: 1.5.0 + +.. image:: images/codeinput.jpg + + +The :class:`CodeInput` provides a box of editable highlited text, like the ones +shown in the image. + +It supports all the features supported by the :class:`~kivy.uix.textinput` and +Code highliting for `languages supported by pygments +`_ along with `KivyLexer` for `KV Language` +highliting. + +Usage example +------------- + +To create a CodeInput with highliting for `KV language`:: + + from kivy.uix.codeinput import CodeInput + from kivy.extras.highlight import KivyLexer + codeinput = CodeInput(lexer=KivyLexer()) + +To create a CodeInput with highliting for `Cython`:: + + from kivy.uix.codeinput import CodeInput + from pygments.lexers import CythonLexer + codeinput = CodeInput(lexer=CythonLexer()) + +''' + +__all__ = ('CodeInput', ) + +from pygments import highlight +from pygments import lexers +from pygments.formatters import BBCodeFormatter + +from kivy.uix.textinput import TextInput +from kivy.core.text.markup import MarkupLabel as Label +from kivy.cache import Cache +from kivy.properties import ObjectProperty + +Cache_get = Cache.get +Cache_append = Cache.append + +# TODO: fix empty line rendering +# TODO: color chooser for keywords/strings/... + + +class CodeInput(TextInput): + '''CodeInput class, used for displaying highlited code. + ''' + + lexer = ObjectProperty(None) + '''This holds the selected Lexer used by pygments to highlite the code + + + :data:`lexer` is a :class:`~kivy.properties.ObjectProperty` defaults to + `PythonLexer` + ''' + + def __init__(self, **kwargs): + self.formatter = BBCodeFormatter() + self.lexer = lexers.PythonLexer() + self.text_color = (0, 0, 0, 1) + self._label_cached = Label() + super(CodeInput, self).__init__(**kwargs) + self._line_options = kw = self._get_line_options() + self._label_cached = Label(**kw) + #use text_color as foreground color + text_color = kwargs.get('foreground_color') + if text_color: + self.text_color = (text_color[0], text_color[1], text_color[2], + text_color[3]) + # set foreground to white to allow text colors to show + # use text_color as the default color in bbcodes + self.foreground_color = [1, 1, 1, 1] + if not kwargs.get('background_color'): + self.background_color = [.9, .92, .92, 1] + + def _create_line_label(self, text): + # Create a label from a text, using line options + ntext = text.replace('\n', '').replace('\t', ' ' * self.tab_width) + if self.password: + ntext = '*' * len(ntext) + ntext = self._get_bbcode(ntext) + kw = self._get_line_options() + cid = '%s\0%s' % (ntext, str(kw)) + texture = Cache_get('textinput.label', cid) + + if not texture: + # FIXME right now, we can't render very long line... + # if we move on "VBO" version as fallback, we won't need to do this. + # try to found the maximum text we can handle + label = Label(text=ntext, **kw) + if text.find('\n') > 0: + label.text = '' + else: + label.text = ntext + try: + label.refresh() + except ValueError: + pass + + # ok, we found it. + texture = label.texture + Cache_append('textinput.label', cid, texture) + label.text = '' + return texture + + def _get_line_options(self): + kw = super(CodeInput, self)._get_line_options() + kw['markup'] = True + kw['valign'] = 'top' + return kw + + def _get_bbcode(self, ntext): + # get bbcoded text for python + try: + ntext[0] + # replace brackets with special chars that aren't highlighted + # by pygment. can't use &bl; ... cause & is highlighted + # if at some time support for braille is added then replace these + # characters with something else + ntext = ntext.replace('[', u'⣿;').replace(']', u'⣾;') + ntext = highlight(ntext, self.lexer, self.formatter) + ntext = ntext.replace(u'⣿;', '&bl;').replace(u'⣾;', '&br;') + # replace special chars with &bl; and &br; + ntext = ''.join(('[color=rgba', str(self.text_color), ']', + ntext, '[/color]')) + ntext = ntext.replace('\n', '') + return ntext + except IndexError: + return '' + + # overriden to prevent cursor position off screen + def _cursor_offset(self): + '''Get the cursor x offset on the current line + ''' + offset = 0 + try: + if self.cursor_col: + offset = self._get_text_width( + self._lines[self.cursor_row][:self.cursor_col]) + except: + pass + finally: + return offset + + def on_lexer(self, instance, value): + self._trigger_refresh_text() + +if __name__ == '__main__': + from kivy.extras.highlight import KivyLexer + from kivy.app import App + + class CodeInputTest(App): + def build(self): + return CodeInput(lexer=KivyLexer(), + font_name='data/fonts/DroidSansMono.ttf', font_size=12, + text=''' +#:kivy 1.0 + +: + canvas: + Color: + rgb: .5, .5, .5 + Rectangle: + pos: self.pos + size: self.size''') + + CodeInputTest().run()