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/autobuild.py b/doc/autobuild.py index a42c2eadb..2a93d6d82 100644 --- a/doc/autobuild.py +++ b/doc/autobuild.py @@ -23,6 +23,7 @@ import kivy # force loading of kivy modules import kivy.app +import kivy.metrics import kivy.atlas import kivy.core.audio import kivy.core.camera @@ -43,6 +44,7 @@ import kivy.modules.monitor import kivy.modules.touchring import kivy.modules.inspector import kivy.modules.recorder +import kivy.modules.screen import kivy.network.urlrequest import kivy.support import kivy.input.recorder @@ -50,7 +52,7 @@ import kivy.interactive from kivy.factory import Factory # force loading of all classes from factory -for x in Factory.classes: +for x in Factory.classes.keys()[:]: getattr(Factory, x) diff --git a/doc/sources/contents.rst.inc b/doc/sources/contents.rst.inc index 37f9370d8..b6d2d2c14 100644 --- a/doc/sources/contents.rst.inc +++ b/doc/sources/contents.rst.inc @@ -25,4 +25,9 @@ stunning applications in short time using the framework. contact +.. toctree:: + :maxdepth: 3 + + guide2-index + .. include:: api-index.rst diff --git a/doc/sources/contribute.rst b/doc/sources/contribute.rst index b2dcea3b4..49bf33971 100644 --- a/doc/sources/contribute.rst +++ b/doc/sources/contribute.rst @@ -175,11 +175,43 @@ get instant karma. Congratulations, you're a hero! Documentation Contributions --------------------------- -Documentation contributions generally follow the same workflow as code -contributions, just a bit more lax. We don't ask you to go through all the -hassle just to correct a single typo. For more complex contributions, please -consider following the suggested workflow though. +Documentation contributions generally follow the same workflow as code contributions, just a bit more lax. + #. Following the instructions above, + + #. Fork the repository. + + #. Clone your fork to your computer. + + #. Setup kivy repo as a remote source. + + #. Install python-sphinx. (See docs/sources/README for assistance.) + + #. Use ReStructuredText_Markup_ to make changes to the HTML documentation in docs/sources. + +.. _ReStructuredText_Markup: http://docutils.sourceforge.net/rst.html + +To submit a documentation update, use the following steps: + + #. Create a new, appropriately named branch in your local repository.:: + + git checkout -b my_docs_update + + #. Modify the documentation with your correction or improvement. + #. Re-generate the HTML pages, and review your update.:: + + make html + + #. Give each commit an appropriate commit message, so that others who are not familiar with the matter get a good idea of what you changed. + #. Keep each commit focused on a single related theme. Don't commit other stuff that doesn't logically belong to this update. + + #. Push to your remote repository on GitHub:: + + git push + + #. Send a *Pull Request* with a description of what you changed via the button in the GitHub interface of your repository. + +We don't ask you to go through all the hassle just to correct a single typo, but for more complex contributions, please follow the suggested workflow. Docstrings ~~~~~~~~~~ diff --git a/doc/sources/gettingstarted/examples.rst b/doc/sources/gettingstarted/examples.rst index 3feaf9c2e..c79eb7be8 100644 --- a/doc/sources/gettingstarted/examples.rst +++ b/doc/sources/gettingstarted/examples.rst @@ -107,6 +107,8 @@ Examples .. |wdg_desc1| replace:: Usage and Showcase of :class:`Accordion ` Widget. .. |wdg_file2| replace:: asyncimage.py .. |wdg_desc2| replace:: Usage and Showcase of :class:`AsyncImage ` Widget. +.. |wdg_file25| replace:: boxlayout_pos_hint.py +.. |wdg_desc25| replace:: Showcase of pos_hint under BoxLayout :class:`BoxLayout ` .. |wdg_file3| replace:: bubble_test.py .. |wdg_desc3| replace:: Usage and Showcase of :class:`Bubble ` Widget. .. |wdg_file4| replace:: customcollide.py @@ -118,7 +120,11 @@ Examples .. |wdg_file7| replace:: keyboardlistener.py .. |wdg_desc7| replace:: listen to the keyboard input and spew result to console. .. |wdg_file8| replace:: label_mipmap.py -.. |wdg_desc8| replace:: How to use :class:`Label ` widget with mipmap. +.. |wdg_desc8| replace:: How to use :class:`Label ` widget with +.. |wdg_file81| replace:: label_with_markup.py +.. |wdg_desc81| replace:: Useage of :class:`Label ` widget with markup. +.. |wdg_file82| replace:: popup_with_kv.py +.. |wdg_desc82| replace:: Useage of :class:`Popup ` widget with ``kv`` language .. |wdg_file9| replace:: rstexample.py .. |wdg_desc9| replace:: Usage and Showcase of :class:`RstDocument ` Widget. .. |wdg_file10| replace:: scatter.py @@ -198,12 +204,15 @@ Examples +------------+---------------+------------------------+ |- |wdg_dir| | - |wdg_file1| |- |wdg_desc1| | | | - |wdg_file2| |- |wdg_desc2| | +| | - |wdg_file25||- |wdg_desc25| | | | - |wdg_file3| |- |wdg_desc3| | | | - |wdg_file4| |- |wdg_desc4| | | | - |wdg_file5| |- |wdg_desc5| | | | - |wdg_file6| |- |wdg_desc6| | | | - |wdg_file7| |- |wdg_desc7| | | | - |wdg_file8| |- |wdg_desc8| | +| | - |wdg_file81||- |wdg_desc81| | +| | - |wdg_file82||- |wdg_desc82| | | | - |wdg_file9| |- |wdg_desc9| | | | - |wdg_file10||- |wdg_desc10| | | | - |wdg_file11||- |wdg_desc11| | diff --git a/doc/sources/guide-index.rst b/doc/sources/guide-index.rst index d503d7d66..2109134b1 100644 --- a/doc/sources/guide-index.rst +++ b/doc/sources/guide-index.rst @@ -1,3 +1,5 @@ +.. _guide-index: + Programming Guide ================= diff --git a/doc/sources/guide/designwithkv.rst b/doc/sources/guide/designwithkv.rst index 3291d3ae4..9f6702922 100644 --- a/doc/sources/guide/designwithkv.rst +++ b/doc/sources/guide/designwithkv.rst @@ -6,6 +6,9 @@ Designing with the Kivy Language ================================ +The code goes in main.py +~~~~~~~~~~~~~~~~~~~~~~~~ + Let's start with a little example. First, the Python file named `main.py`: .. include:: ../../../examples/guide/designwithkv/main.py @@ -20,6 +23,9 @@ In addition, we are creating a ``do_action()`` method, that will use both of these properties. It will change the ``info`` text, and change text in the ``label_wid`` widget. +The layout goes in controller.kv +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Executing this application without a corresponding `.kv` file will work, but nothing will be shown on the screen. This is expected, because the ``Controller`` class has no widgets in it, it's just a ``FloatLayout``. We can diff --git a/doc/sources/guide/environment.rst b/doc/sources/guide/environment.rst index b98f4bb2b..db7fe7ec3 100644 --- a/doc/sources/guide/environment.rst +++ b/doc/sources/guide/environment.rst @@ -32,11 +32,6 @@ KIVY_NO_FILELOG KIVY_NO_CONSOLELOG If set, logs will be not print on the console -KIVY_DPI - If set, the value will be used instead of the value returned by the window. - - .. versionadded:: 1.4.0 - Path control ------------ @@ -101,3 +96,22 @@ KIVY_CLIPBOARD Values: pygame, dummy +Metrics +------- + +KIVY_DPI + If set, the value will be used for :data:`Metrics.dpi`. + + .. versionadded:: 1.4.0 + +KIVY_METRICS_DENSITY + If set, the value will be used for :data:`Metrics.density`. + + .. versionadded:: 1.5.0 + +KIVY_METRICS_FONTSCALE + + If set, the value will be used for :data:`Metrics.fontscale`. + + .. versionadded:: 1.5.0 + diff --git a/doc/sources/guide/firstwidget.rst b/doc/sources/guide/firstwidget.rst index cadf87d6a..309b020d9 100644 --- a/doc/sources/guide/firstwidget.rst +++ b/doc/sources/guide/firstwidget.rst @@ -169,17 +169,15 @@ This is what has changed: :class:`~kivy.graphics.vertex_instructions.Line`, you will see that it accepts a ``points`` argument that has to be a list of 2D point coordinates, like ``(x1, y1, x2, y2, ..., xN, yN)``. - * Line 8: This is where it gets interesting. ``touch.ud`` is a Python + * Line 13: This is where it gets interesting. ``touch.ud`` is a Python dictionary (type ) that allows us to store *custom attributes* - for a touch. On this line we simply get a reference to it to make it - more clear that ``ud`` stands for ``userdata``. You could just as - well write ``touch.ud`` instead of ``userdata``. + for a touch. * Line 13: We make use of the Line instruction that we imported and set a Line up for drawing. Since this is done in ``on_touch_down``, there will be a new line for every new touch. By creating the line inside the ``with`` block, the canvas automatically knows about the line and will draw it. We just want to modify the line later, so we - store a reference to it in the ``userdata`` dictionary under the + store a reference to it in the ``touch.ud`` dictionary under the arbitrarily chosen but aptly named key 'line'. We pass the line that we're creating the initial touch position because that's where our line will begin. @@ -193,13 +191,13 @@ This is what has changed: shortly see why. * Line 16: Remember: This is the same touch object that we got in ``on_touch_down``, so we can simply access the data we stored away - in the userdata dictionary! + in the ``touch.ud`` dictionary! To the line we set up for this touch earlier, we now add the current position of the touch as a new point. We know that we need to extend the line because this happens in ``on_touch_move``, which is only called when the touch has moved, which is exactly why we want to update the line. - Storing the line in the userdata makes it a whole lot + Storing the line in the ``touch.ud`` makes it a whole lot easier for us as we don't have to maintain our own touch-to-line bookkeeping. @@ -218,13 +216,11 @@ Here are the changes: * Line 1: We import Python's random() function that will give us random values in the range of [0., 1.). - * Line 10: We want to memorize the color for this touch, so we store - it in the touch's userdata dictionary. - In this case we simply create a new tuple of 3 random + * Line 10: In this case we simply create a new tuple of 3 random float values that will represent a random RGB color. Since we do this in ``on_touch_down``, every new touch will get its own color. Don't get confused by the use of two ``=`` operators. We're just - binding the tuple to ``c`` as well as a shortcut for use within this + binding the tuple to ``color`` as well as a shortcut for use within this method because we're lazy. * Line 12: As before, we set the color for the canvas. Only this time we use the random values we generated and feed them to the color @@ -248,7 +244,7 @@ might even be able to create a nice little drawing! There is a nice trick to prevent this: Instead of creating a tuple with three random values, create a tuple like this: ``(random(), 1., 1.)``. Then, when passing it to the color instruction, set the mode to HSV - color space: ``Color(*c, mode='hsv')``. This way you will have a + color space: ``Color(*color, mode='hsv')``. This way you will have a smaller number of possible colors, but the colors that you get will always be equally bright. Only the hue changes. diff --git a/doc/sources/guide/kvlang.rst b/doc/sources/guide/kvlang.rst index 8aec98e32..7153ce587 100644 --- a/doc/sources/guide/kvlang.rst +++ b/doc/sources/guide/kvlang.rst @@ -115,6 +115,10 @@ is where the Kivy language can be useful. Usage of the Kivy language for graphics ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. note:: + + By default the name of the ``.kv`` file to be loaded is decided by the name of your main class. So if you main class's name is ``MyTestApp`` or ``MultiMapMayhem`` then ``mytest.kv`` or ``multimapmayhem.kv`` file will be auto-loaded respectively. + The Kivy language has a lot of benefits for this example ``Button``. You can create a rule that will match your widget, create graphics instructions, and update their properties according to a python expression. Here is the complete diff --git a/doc/sources/guide/packaging-android.rst b/doc/sources/guide/packaging-android.rst index 4906a6f90..f95afc7bb 100644 --- a/doc/sources/guide/packaging-android.rst +++ b/doc/sources/guide/packaging-android.rst @@ -10,6 +10,12 @@ Create a package for Android .. _Packaging your application into APK: +TestDrive +--------- +There is a VirtualBox Image we provide with the prerequisites along with +Android SDK and NDK preinstalled to ease your installation woes. You can +download it from `here `_. + Packaging your application into APK ----------------------------------- @@ -29,7 +35,7 @@ First, follow the prerequisites needed for the project: http://python-for-android.readthedocs.org/en/latest/prerequisites/ -Then a console, and type:: +Then open a console, and type:: git clone git://github.com/kivy/python-for-android diff --git a/doc/sources/guide/packaging-ios.rst b/doc/sources/guide/packaging-ios.rst index f22ff4569..84cc54928 100644 --- a/doc/sources/guide/packaging-ios.rst +++ b/doc/sources/guide/packaging-ios.rst @@ -81,6 +81,8 @@ You can customize the build in many ways: #. Go to the settings panel > build, search for "strip" options, and triple-check that they are all set to NO. Stripping is not working with Python dynamic modules, and will strip needed symbols. +#. Indicate a launch image in portrait/landscape for ipad with and without + retina display. .. _Known issues: @@ -90,12 +92,6 @@ Known issues Currently, the project have few issues as (we'll fixes them during the development): -- Loading time: Apple provide a way to reduce the feeling of a slow application - loading by showing an image when the application is initialize. But, due to - the SDL approach, IOS remove the launch image before we have started. So if - you are using a launch image, the user will see: The launch image -> black - screen -> your app. Remove the launch image for now. - - Application configuration not writing: we are learning how IOS manage its filesystem. diff --git a/doc/sources/guide/packaging-windows.rst b/doc/sources/guide/packaging-windows.rst index d5a9c2ad8..ea494dc57 100644 --- a/doc/sources/guide/packaging-windows.rst +++ b/doc/sources/guide/packaging-windows.rst @@ -68,7 +68,7 @@ Build the spec #. Go to the pyinstaller directory, and build the spec:: cd pyinstaller-2.0 - python Build.py touchtracer\\touchtracer.spec + python pyinstaller.py touchtracer\\touchtracer.spec #. The package will be in the `touchtracer\\dist\\touchtracer` directory ! 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/guide2-index.rst b/doc/sources/guide2-index.rst new file mode 100644 index 000000000..8b15accf8 --- /dev/null +++ b/doc/sources/guide2-index.rst @@ -0,0 +1,19 @@ +Programming Guide 2 +=================== + +.. warning:: + + We are currently refactoring the whole Programming Guide. This is a work in + progress, and might change at any times. + +.. toctree:: + :maxdepth: 2 + + guide2/basic + guide2/events + guide2/widgets + guide2/graphics + guide2/lang + guide2/bestpractices + guide2/advancedgraphics + guide2/packaging diff --git a/doc/sources/guide2/advancedgraphics.rst b/doc/sources/guide2/advancedgraphics.rst new file mode 100644 index 000000000..2afdd801e --- /dev/null +++ b/doc/sources/guide2/advancedgraphics.rst @@ -0,0 +1,13 @@ +.. _advancedgraphics: + +Advanced Graphics +================= + +Create your own Shader +---------------------- + +Rendering in a Framebuffer +-------------------------- + +Optimizations +------------- diff --git a/doc/sources/guide2/basic.rst b/doc/sources/guide2/basic.rst new file mode 100644 index 000000000..152d60ffe --- /dev/null +++ b/doc/sources/guide2/basic.rst @@ -0,0 +1,47 @@ +.. _basic: + +Basic Kivy +========== + +Installation of Kivy environment +-------------------------------- + +Kivy depends on multiples dependencies, such as pygame, gstreamer, PIL, cairo, +and more. All of them are not required, but depending the platform you're +working on, it can be a pain to install them. For Windows and MacOSX, we +provide a portable package that you can just unzip and use. + +.. toctree:: + :maxdepth: 1 + + /installation/installation-windows.rst + /installation/installation-macosx.rst + /installation/installation-linux.rst + +If you want to install everything yourself, ensure that you have at least +`Cython `_, `Pygame `. A typical pip +installation look like:: + + pip install cython + pip install hg+http://bitbucket.org/pygame/pygame + pip install kivy + +The `development version `_ can be installed with +git:: + + git clone https://github.com/kivy/kivy + make + + +Create an application +--------------------- + +Running the application +----------------------- + +Customize the application +------------------------- + +Platform specifics +------------------ + diff --git a/doc/sources/guide2/bestpractices.rst b/doc/sources/guide2/bestpractices.rst new file mode 100644 index 000000000..1937519e4 --- /dev/null +++ b/doc/sources/guide2/bestpractices.rst @@ -0,0 +1,17 @@ +.. _bestpractices: + +Best Practices +============== + +Designing your Application code +------------------------------- + +Handle Window resizing +---------------------- + +Managing resources +------------------ + +Platform consideration +---------------------- + diff --git a/doc/sources/guide2/events.rst b/doc/sources/guide2/events.rst new file mode 100644 index 000000000..e9277f95c --- /dev/null +++ b/doc/sources/guide2/events.rst @@ -0,0 +1,23 @@ +.. _events: +.. _properties: + +Events and Properties +===================== + +Introduction to Event Dispatcher +-------------------------------- + +Creating custom events +---------------------- + +Attaching callbacks +------------------- + +Introduction to properties +-------------------------- + +Declaration of a Property +------------------------- + +Dispatching a Property event +---------------------------- diff --git a/doc/sources/guide2/graphics.rst b/doc/sources/guide2/graphics.rst new file mode 100644 index 000000000..f532c8c0e --- /dev/null +++ b/doc/sources/guide2/graphics.rst @@ -0,0 +1,17 @@ +.. _graphics: + +Graphics +======== + +Introduction to Canvas +---------------------- + +Context instructions +-------------------- + +Drawing instructions +-------------------- + +Manipulating instructions +------------------------- + diff --git a/doc/sources/guide2/lang.rst b/doc/sources/guide2/lang.rst new file mode 100644 index 000000000..23b778b9f --- /dev/null +++ b/doc/sources/guide2/lang.rst @@ -0,0 +1,19 @@ +.. _lang: + +Kv language +=========== + +Concept behind the language +--------------------------- + +Rule context +------------ + +Instanciate children +-------------------- + +Extend canvas +------------- + +Templating +---------- diff --git a/doc/sources/guide2/packaging.rst b/doc/sources/guide2/packaging.rst new file mode 100644 index 000000000..921e0149e --- /dev/null +++ b/doc/sources/guide2/packaging.rst @@ -0,0 +1,2 @@ +Packaging +========= diff --git a/doc/sources/guide2/widgets.rst b/doc/sources/guide2/widgets.rst new file mode 100644 index 000000000..aeb188dac --- /dev/null +++ b/doc/sources/guide2/widgets.rst @@ -0,0 +1,17 @@ +.. _widgets: + +Widgets +======= + +Introduction to Widget +---------------------- + +Manipulating the Widget tree +---------------------------- + +Organize with Layouts +--------------------- + +Seperate with Screen Manager +---------------------------- + diff --git a/doc/sources/images/accordion.jpg b/doc/sources/images/accordion.jpg index 16426edde..150ea9e59 100644 Binary files a/doc/sources/images/accordion.jpg and b/doc/sources/images/accordion.jpg differ diff --git a/doc/sources/images/anchorlayout.gif b/doc/sources/images/anchorlayout.gif index d312ab755..2932d979c 100644 Binary files a/doc/sources/images/anchorlayout.gif and b/doc/sources/images/anchorlayout.gif differ diff --git a/doc/sources/images/anchorlayout.png b/doc/sources/images/anchorlayout.png index 1dff5221b..dc5f34bcd 100644 Binary files a/doc/sources/images/anchorlayout.png and b/doc/sources/images/anchorlayout.png differ diff --git a/doc/sources/images/boxlayout.gif b/doc/sources/images/boxlayout.gif index b890621b6..d98a2a223 100644 Binary files a/doc/sources/images/boxlayout.gif and b/doc/sources/images/boxlayout.gif differ diff --git a/doc/sources/images/boxlayout.png b/doc/sources/images/boxlayout.png index 475ca990f..c1117fd42 100644 Binary files a/doc/sources/images/boxlayout.png and b/doc/sources/images/boxlayout.png differ diff --git a/doc/sources/images/bubble.jpg b/doc/sources/images/bubble.jpg index 7a48178c3..d13b475bb 100644 Binary files a/doc/sources/images/bubble.jpg and b/doc/sources/images/bubble.jpg differ diff --git a/doc/sources/images/button.jpg b/doc/sources/images/button.jpg index 3266490d8..79bae771e 100644 Binary files a/doc/sources/images/button.jpg and b/doc/sources/images/button.jpg differ 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/images/floatlayout.gif b/doc/sources/images/floatlayout.gif index 227fe823b..311c52d88 100644 Binary files a/doc/sources/images/floatlayout.gif and b/doc/sources/images/floatlayout.gif differ diff --git a/doc/sources/images/gridlayout.gif b/doc/sources/images/gridlayout.gif index 934ec0fcc..dcb793ce6 100644 Binary files a/doc/sources/images/gridlayout.gif and b/doc/sources/images/gridlayout.gif differ diff --git a/doc/sources/images/gridlayout.png b/doc/sources/images/gridlayout.png index e84c4fc7b..e998d98c1 100644 Binary files a/doc/sources/images/gridlayout.png and b/doc/sources/images/gridlayout.png differ diff --git a/doc/sources/images/gridlayout_1.jpg b/doc/sources/images/gridlayout_1.jpg index 9a5c6287e..fb2602d01 100644 Binary files a/doc/sources/images/gridlayout_1.jpg and b/doc/sources/images/gridlayout_1.jpg differ diff --git a/doc/sources/images/gridlayout_2.jpg b/doc/sources/images/gridlayout_2.jpg index ae3acaac9..228f42fd8 100644 Binary files a/doc/sources/images/gridlayout_2.jpg and b/doc/sources/images/gridlayout_2.jpg differ diff --git a/doc/sources/images/gridlayout_3.jpg b/doc/sources/images/gridlayout_3.jpg index cea1614cb..1c2dbff22 100644 Binary files a/doc/sources/images/gridlayout_3.jpg and b/doc/sources/images/gridlayout_3.jpg differ diff --git a/doc/sources/images/popup.jpg b/doc/sources/images/popup.jpg index 304e78b47..0e65fc1f1 100644 Binary files a/doc/sources/images/popup.jpg and b/doc/sources/images/popup.jpg differ diff --git a/doc/sources/images/progressbar.jpg b/doc/sources/images/progressbar.jpg index fab950278..ecf3dae50 100644 Binary files a/doc/sources/images/progressbar.jpg and b/doc/sources/images/progressbar.jpg differ diff --git a/doc/sources/images/settings_kivy.jpg b/doc/sources/images/settings_kivy.jpg index a68fe627a..1f135c3ec 100644 Binary files a/doc/sources/images/settings_kivy.jpg and b/doc/sources/images/settings_kivy.jpg differ diff --git a/doc/sources/images/slider.jpg b/doc/sources/images/slider.jpg index 6a519bd68..655ca8359 100644 Binary files a/doc/sources/images/slider.jpg and b/doc/sources/images/slider.jpg differ diff --git a/doc/sources/images/stacklayout.gif b/doc/sources/images/stacklayout.gif index 842724a04..6ec194b77 100644 Binary files a/doc/sources/images/stacklayout.gif and b/doc/sources/images/stacklayout.gif differ diff --git a/doc/sources/images/stacklayout.png b/doc/sources/images/stacklayout.png index 9451503c0..02061c0e4 100644 Binary files a/doc/sources/images/stacklayout.png and b/doc/sources/images/stacklayout.png differ diff --git a/doc/sources/images/switch-off.jpg b/doc/sources/images/switch-off.jpg index fbe168949..8dd266489 100644 Binary files a/doc/sources/images/switch-off.jpg and b/doc/sources/images/switch-off.jpg differ diff --git a/doc/sources/images/switch-on.jpg b/doc/sources/images/switch-on.jpg index 62909ce54..a169f4d24 100644 Binary files a/doc/sources/images/switch-on.jpg and b/doc/sources/images/switch-on.jpg differ diff --git a/doc/sources/index.rst b/doc/sources/index.rst index 737e38e3d..fc38b1914 100644 --- a/doc/sources/index.rst +++ b/doc/sources/index.rst @@ -8,12 +8,10 @@ Welcome to Kivy's documentation. Kivy is an open source software library for rap development of applications equipped with novel user interfaces, such as multi-touch apps. -You are probably wondering why you'd be interested in using Kivy. There is -a document outlining our :ref:`philosophy` that we encourage you to read. +We recommend that you get started with :doc:`/gettingstarted/index`. Then head over the :ref:`guide-index`. We also have a :ref:`quickstart` if you are impatient. -We recommend that you get started with :ref:`installation` and then head over to -the :ref:`quickstart` document. Besides the quickstart, there is also a more -detailed :ref:`architecture`. +You are probably wondering why you'd be interested in using Kivy. There is +a document outlining our :ref:`philosophy` that we encourage you to read, and an detailed :ref:`architecture`. If you want to contribute to Kivy, make sure to read :ref:`contributing`. If your concern isn't addressed in the documentation, feel free to :ref:`contact`. diff --git a/doc/sources/installation/installation-linux.rst b/doc/sources/installation/installation-linux.rst index bc5440596..febb464e4 100644 --- a/doc/sources/installation/installation-linux.rst +++ b/doc/sources/installation/installation-linux.rst @@ -6,14 +6,15 @@ Installation on Linux Prerequisites ------------- -Ubuntu (11.10) -~~~~~~~~~~~~~~ +Ubuntu (11.10 or newer) +~~~~~~~~~~~~~~~~~~~~~~~ :: $ sudo apt-get install python-setuptools python-pygame python-opengl \ - python-gst0.10 python-enchant gstreamer0.10-plugins-good cython python-dev \ - build-essential libgl1-mesa-dev libgles2-mesa-dev + python-gst0.10 python-enchant gstreamer0.10-plugins-good python-dev \ + build-essential libgl1-mesa-dev libgles2-mesa-dev python-pip + $ sudo pip install --upgrade cython Fedora (16) ~~~~~~~~~~~ @@ -21,10 +22,10 @@ Fedora (16) :: $ sudo yum install python-distutils-extra python-enchant freeglut PyOpenGL \ - SDL_ttf-devel SDL_mixer-devel pygame pygame-devel Cython khrplatform-devel \ + SDL_ttf-devel SDL_mixer-devel pygame pygame-devel khrplatform-devel \ mesa-libGLES mesa-libGLES-devel gstreamer-plugins-good gstreamer \ - gstreamer-python mtdev-devel - + gstreamer-python mtdev-devel python-pip + $ sudo pip install --upgrade cython OpenSuse (12.1) ~~~~~~~~~~~~~~~ @@ -33,8 +34,9 @@ OpenSuse (12.1) $ sudo zypper install python-distutils-extra python-pygame python-opengl \ python-gstreamer-0_10 python-enchant gstreamer-0_10-plugins-good \ - python-Cython python-devel Mesa-devel + python-devel Mesa-devel python-pip $ zypper install -t pattern devel_C_C++ + $ sudo pip install --upgrade cython Mageia (1 and 2(cauldron)) @@ -47,6 +49,8 @@ Mageia (1 and 2(cauldron)) gstreamer0.10-python python-enchant gstreamer0.10-plugins-good \ python-cython lib64python-devel lib64mesagl1-devel lib64mesaegl1-devel \ lib64mesaglesv2_2-devel make gcc + $ sudo easy_install pip + $ sudo pip install --upgrade cython Installation diff --git a/doc/sources/installation/installation-macosx.rst b/doc/sources/installation/installation-macosx.rst index 59da8d8af..22d2e6618 100644 --- a/doc/sources/installation/installation-macosx.rst +++ b/doc/sources/installation/installation-macosx.rst @@ -21,7 +21,7 @@ file that contains: To install Kivy, you must: - 1. Download the latest version from http://kivy.org/#downloads + 1. Download the latest version from http://kivy.org/#download 2. Double-click to open it 3. Drag the Kivy.app into your Applications folder 4. Make sure to read the Readme.txt diff --git a/doc/sources/installation/installation.rst b/doc/sources/installation/installation.rst index ea975ebc4..29ae413d1 100644 --- a/doc/sources/installation/installation.rst +++ b/doc/sources/installation/installation.rst @@ -4,7 +4,7 @@ Installation ============ We try not to reinvent the wheel, but to bring something innovative to the -market. As a consequence, we're focused on our own code and use pre-existing, +market. As a consequence, we're focused on our own code and use pre-existing, high-quality third-party libraries where possible. To support the full, rich set of features that Kivy offers, several other libraries are required. If you do not use a specific feature (e.g. video playback) you @@ -12,7 +12,7 @@ don't need the corresponding dependency, however. That said, there is one dependency that Kivy **does** require: `Cython `_. -In addition, you need a `Python `_ 2.x (**not** 3.x) +In addition, you need a `Python `_ 2.x (2.6 <= x < 3.0) interpreter. If you want to enable features like windowing (i.e., open a Window), audio/video playback or spelling correction, additional dependencies must be available. For these, we recommend `Pygame `_, `Gst-Python @@ -79,8 +79,14 @@ necessary packages: :: $ sudo apt-get install python-setuptools python-pygame python-opengl \ - python-gst0.10 python-enchant gstreamer0.10-plugins-good cython python-dev \ - build-essential libgl1-mesa-dev libgles2-mesa-dev + python-gst0.10 python-enchant gstreamer0.10-plugins-good python-dev \ + build-essential libgl1-mesa-dev libgles2-mesa-dev python-pip + +Kivy require a recent version of cython, so it's better to use the last +version published on pypi. + +:: + $ sudo pip install --upgrade cython .. _dev-install: 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/android/compass/500px-Windrose.svg.png b/examples/android/compass/500px-Windrose.svg.png new file mode 100644 index 000000000..f3f5f0db7 Binary files /dev/null and b/examples/android/compass/500px-Windrose.svg.png differ diff --git a/examples/android/compass/README.txt b/examples/android/compass/README.txt new file mode 100644 index 000000000..ed331c014 --- /dev/null +++ b/examples/android/compass/README.txt @@ -0,0 +1,24 @@ +Compass +======= + +Demonstrate the combination of the Android Magnetic Field Sensor and the Kivy functionality viewing a Compass. + +Kivy Python-For-Android +----------------------- + +Please look at the lastest docs of the +`Kivy Python-For-Android Project `__ + +Building an APK +--------------- + +:: + + ./distribute.sh -m "pyjnius kivy" + + +:: + + ./build.py --package org.test.compass --name compass \ + --version 1.0 --dir ~/code/kivy/examples/android/compass debug + diff --git a/examples/android/compass/android.txt b/examples/android/compass/android.txt new file mode 100644 index 000000000..813307fd6 --- /dev/null +++ b/examples/android/compass/android.txt @@ -0,0 +1,3 @@ +title=Compass +author=Nik Klever +orientation=portrait diff --git a/examples/android/compass/main.py b/examples/android/compass/main.py new file mode 100644 index 000000000..59b329c80 --- /dev/null +++ b/examples/android/compass/main.py @@ -0,0 +1,223 @@ +import kivy +kivy.require('1.3.0') + +import math +import time + +from kivy.app import App +from kivy.uix.floatlayout import FloatLayout +from kivy.uix.button import Button +from kivy.uix.scatter import Scatter +from kivy.clock import Clock +from kivy.vector import Vector +from kivy.core.window import Window +from kivy.graphics import Color, Ellipse, Rectangle, Triangle +from kivy.logger import Logger + +from jnius import autoclass + +Logger.info('COMPASS: STARTED') + +def LoggerDisplayMetrics(metrics): + """ + Logging all values of the Java Android DisplayMetrics class + to get some more information about the metrics of Android devices + """ + display = {'Default':metrics.DENSITY_DEFAULT, + 'Device':metrics.DENSITY_DEVICE, + 'High':metrics.DENSITY_HIGH, + 'Low':metrics.DENSITY_LOW, + 'Medium':metrics.DENSITY_MEDIUM, + 'XHIGH':metrics.DENSITY_XHIGH, + 'density':metrics.density, + 'densityDpi':metrics.densityDpi, + 'heightPixels':metrics.heightPixels, + 'scaledDensity':metrics.scaledDensity, + 'widthPixels':metrics.widthPixels, + 'xdpi':metrics.xdpi, + 'ydpi':metrics.ydpi} + for (k,v) in display.items(): + Logger.info('COMPASS: display %s = %s'%(k,v)) + +class CompassWidget(FloatLayout): + + def __init__(self, **kwargs): + """ + Constructor of the CompassWidget class + """ + super(CompassWidget, self).__init__(**kwargs) + + def build(self,pos,size): + """ + building the background of the CompassWidget with a circle of a + compass windrose image + (Source of this image: Wikipedia - http://en.wikipedia.org/wiki/Compass_rose) + """ + with self.canvas: + self.pos = pos + self.size = size + self.windrose = Ellipse(source="500px-Windrose.svg.png", pos=pos, size=size) + +class NeedleWidget(Scatter): + + def __init__(self, **kwargs): + """ + Constructor of the NeedleWidget class + + do_rotation, do_translation, do_scale are all set to False + to indicate that the scatter widget can onlybe updated from the + app and not by an interaction with your fingers + """ + super(NeedleWidget, self).__init__(**kwargs) + + self.do_rotation = False + self.do_translation = False + self.do_scale = False + self.auto_bring_to_front = True + + def rotateNeedle(self,angle): + """ + rotate the NeedleWidget with the parameter angle + 90 is subtracted because North of the windrose is at 90 degrees + of the device + """ + self.rotation = angle - 90 + + def build(self,center,needleSize): + """ + building the needle with two Triangles of different color + """ + self.pos = center - needleSize/2. + self.size = needleSize + self.size_hint = [None, None] + with self.canvas: + Color(1., 0, 0) + needleTP1 = Vector(needleSize[0]/2.,needleSize[1]) + needleTP2 = Vector(needleSize[0]/2.,0) + needleTP3 = Vector(-needleSize[0],needleSize[1]/2.) + needlePoints = (needleTP1[0],needleTP1[1], + needleTP2[0],needleTP2[1], + needleTP3[0],needleTP3[1]) + self.needleT1 = Triangle(points=needlePoints) + Color(0.5, 0.5, 0.5) + needleTP3 = Vector(2*needleSize[0],needleSize[1]/2.) + needlePoints = (needleTP1[0],needleTP1[1], + needleTP2[0],needleTP2[1], + needleTP3[0],needleTP3[1]) + self.needleT2 = Triangle(points=needlePoints) + +class CompassApp(App): + + def __init__(self, **kwargs): + """ + Constructor of the Compass App + + 1) The Java Android API DisplayMetrics is called to get + information about the densityDpi factor of the Android device + + 2) The Kivy Python-For-Android Android API is called to + get access to the hardware sensors of the Android device + + """ + super(CompassApp, self).__init__(**kwargs) + DisplayMetrics = autoclass('android.util.DisplayMetrics') + metrics = DisplayMetrics() + metrics.setToDefaults() + LoggerDisplayMetrics(metrics) + self.densityDpi = metrics.densityDpi + + Hardware = autoclass('org.renpy.android.Hardware') + self.hw = Hardware() + Logger.info('COMPASS: Hardware Objects: %s'%(str(dir(self.hw)))) + Logger.info('COMPASS: Hardware Sensors\n%s\n'%(self.hw.getHardwareSensors())) + + def viewCompass(self, *largs): + """ + viewCompass calls the readSensor method of the + magneticFieldSensor instance of the generic3AxisSensor, it reads the + 3-tuple value of the magnetic field + + the declination angle is computed as the angle of the magnetic field + vector in the x,y-plane and the unity-vector of the y-axis. + + afterwards the rotateNeedle function rotates the needle as given + by the declination angle parameter + """ + (x, y, z) = self.hw.magneticFieldSensor.readSensor() + declination = Vector(x,y).angle((0,1)) + #Logger.info('COMPASS: viewCompass x=%s y=%s z=%s declination=%s'%(x,y,z,declination)) + self.needle.rotateNeedle(declination) + + def stopApp(self,*largs): + """ + this function is called when pushed the stopButton, disables + the magneticFieldSensor and stops the app + """ + self.hw.magneticFieldSensor.changeStatus(False) + Logger.info('COMPASS: stop largs '+str(largs)) + self.stop() + + def build(self): + """ + Building all together: + + 1) Creating the parent widget and clearing it to white background color + + 2) Defining a suitable position and size of the CompassWidget, the + needleSize and the stopButtonHeight depending on the densityDpi value + given by DisplayMetrics + + 3) Creating an instance of the CompassWidget and adding it to the + parent widget and calling the appropriate build function + + 4) Creating an instance of the NeedleWidget and adding it also to the + parent widget and calling the appropriate build function + + 5) Creating an instance of a Button widget and adding it as stopButton + also to the parent widget and bind it with the stopApp function + + 6) Calling the instance method changeStatus of the magneticFieldSensor + instance with parameter True to enable the magnetic field sensor + and additionally calling the function schedule_interval of the Clock + class for a repeated call of the function viewCompass every second. + """ + parent = FloatLayout(size=(500,500)) + Window.clearcolor = (1, 1, 1, 1) + + if self.densityDpi == 240: + CompassPos = Vector(50., 200.) + CompassSize = Vector(400., 400.) + needleSize = Vector(100., 60.) + stopButtonHeight = 60 + elif self.densityDpi == 320: + CompassPos = Vector(75., 300.) + CompassSize = Vector(600., 600.) + needleSize = Vector(150., 90.) + stopButtonHeight = 90 + else: + Logger.info('COMPASS: widget size should be adopted - minimum used for densityDpi=%s'%(str(self.densityDpi))) + CompassPos = Vector(50., 200.) + CompassSize = Vector(400., 400.) + needleSize = Vector(100., 60.) + stopButtonHeight = 60 + + self.Compass = CompassWidget() + parent.add_widget(self.Compass) + self.Compass.build(pos=CompassPos,size=CompassSize) + + self.needle = NeedleWidget() + parent.add_widget(self.needle) + self.needle.build(center=CompassPos+CompassSize/2.,needleSize=needleSize) + + self.stopButton = Button(text='Stop', pos_hint={'right':1}, size_hint=(None,None), height=stopButtonHeight) + parent.add_widget(self.stopButton) + self.stopButton.bind(on_press=self.stopApp) + + self.hw.magneticFieldSensor.changeStatus(True) + Clock.schedule_interval(self.viewCompass, 1.) + return parent + +if __name__ in ('__main__', '__android__'): + CompassApp().run() + + diff --git a/examples/android/compass/public.version b/examples/android/compass/public.version new file mode 100644 index 000000000..7dbe9c441 --- /dev/null +++ b/examples/android/compass/public.version @@ -0,0 +1 @@ +1323529642.46 \ No newline at end of file diff --git a/examples/animation/animate.py b/examples/animation/animate.py index 0b642b036..cb28e48f8 100644 --- a/examples/animation/animate.py +++ b/examples/animation/animate.py @@ -28,8 +28,7 @@ class TestApp(App): def build(self): # create a button, and attach animate() method as a on_press handler - button = Button(size_hint=(None, None), text='plop') - button.bind(on_press=self.animate) + button = Button(size_hint=(None, None), text='plop', on_press=self.animate) return button if __name__ == '__main__': diff --git a/examples/canvas/canvas_stress.py b/examples/canvas/canvas_stress.py index 458559cc7..b52998f0c 100644 --- a/examples/canvas/canvas_stress.py +++ b/examples/canvas/canvas_stress.py @@ -34,14 +34,14 @@ class StressCanvasApp(App): label = Label(text='0') - btn_add100 = Button(text='+ 100 rects') - btn_add100.bind(on_press=partial(self.add_rects, label, wid, 100)) + btn_add100 = Button(text='+ 100 rects', + on_press=partial(self.add_rects, label, wid, 100)) - btn_add500 = Button(text='+ 500 rects') - btn_add500.bind(on_press=partial(self.add_rects, label, wid, 500)) + btn_add500 = Button(text='+ 500 rects', + on_press=partial(self.add_rects, label, wid, 500)) - btn_reset = Button(text='Reset') - btn_reset.bind(on_press=partial(self.reset_rects, label, wid)) + btn_reset = Button(text='Reset', + on_press=partial(self.reset_rects, label, wid)) layout = BoxLayout(size_hint=(1, None), height=50) layout.add_widget(btn_add100) diff --git a/examples/canvas/lines_extended.py b/examples/canvas/lines_extended.py index ab4777ce7..05970079c 100644 --- a/examples/canvas/lines_extended.py +++ b/examples/canvas/lines_extended.py @@ -83,6 +83,20 @@ Builder.load_string(''' Label: center: root.center text: 'Rectangle' + + +: + canvas: + Color: + rgba: .1, .1, 1, .9 + Line: + width: 2. + bezier: (self.x, self.y, self.center_x - 40, self.y + 100, self.center_x + 40, self.y - 100, self.right, self.y) + Label: + center: root.center + text: 'Bezier' + + ''') @@ -107,6 +121,9 @@ class LineCircle3(Widget): class LineRectangle(Widget): pass +class LineBezier(Widget): + pass + class LineExtendedApp(App): def build(self): root = GridLayout(cols=2, padding=50, spacing=50) @@ -117,6 +134,7 @@ class LineExtendedApp(App): root.add_widget(LineCircle2()) root.add_widget(LineCircle3()) root.add_widget(LineRectangle()) + root.add_widget(LineBezier()) return root if __name__ == '__main__': diff --git a/examples/demo/kivycatalog/README b/examples/demo/kivycatalog/README new file mode 100644 index 000000000..b3e466423 --- /dev/null +++ b/examples/demo/kivycatalog/README @@ -0,0 +1,8 @@ +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 +Known bugs: +* The DropDown +* 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..ac0fe1fef --- /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: "data/fonts/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..ce4156fbb --- /dev/null +++ b/examples/demo/kivycatalog/container_kvs/PlaygroundContainer.kv @@ -0,0 +1,4 @@ +#:kivy 1.4 + +RstDocument: + text: "Welcome\n---------------\nThe 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..2655e177e --- /dev/null +++ b/examples/demo/kivycatalog/kivycatalog.kv @@ -0,0 +1,147 @@ +#:kivy 1.4 +#:import KivyLexer kivy.extras.highlight.KivyLexer + +[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 + auto_reload: chkbx.active + info_label: info_lbl + orientation: 'vertical' + 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: + lexer: KivyLexer() + text_size: self.width-20, self.height-20 + font_name: "data/fonts/DroidSansMono.ttf" + valign: "top" + id: language_box + text: "This box will display the kivy language for whatever has been selected" + on_text: root.schedule_reload() + BoxLayout: + size_hint: 1, None + height: '30pt' + BoxLayout: + CheckBox: + id: chkbx + active: True + Label: + text: "Auto Reload" + Button: + text: 'Render Now' + on_release: root.change_kv(*args) + FloatLayout: + size_hint: 1, None + height: 0 + TextInput: + id:info_lbl + readonly: True + font_size: '14sp' + background_color: (0, 0, 0, 1) + foreground_color: (1, 1, 1, 1) + opacity:0 + size_hint: 1, None + text_size: self.size + height: '150pt' + top: 0 diff --git a/examples/demo/kivycatalog/main.py b/examples/demo/kivycatalog/main.py new file mode 100644 index 000000000..8e6691edb --- /dev/null +++ b/examples/demo/kivycatalog/main.py @@ -0,0 +1,166 @@ +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.codeinput import CodeInput +from kivy.animation import Animation +from kivy.clock import Clock + +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(CodeInput): + 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() + # reset undo/redo history + self.language_box.reset_undo() + + 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): + '''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: + self.info_label.text = str(e) + self.anim = Animation(top=190.0, opacity=1, d=2, t='in_back') +\ + Animation(top=190.0, d=2) +\ + Animation(top=0, opacity=0, d=2) + self.anim.start(self.info_label) + 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), + 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/demo/shadereditor/shadereditor.kv b/examples/demo/shadereditor/shadereditor.kv index 1ce0446e5..072987d81 100644 --- a/examples/demo/shadereditor/shadereditor.kv +++ b/examples/demo/shadereditor/shadereditor.kv @@ -1,4 +1,5 @@ #:kivy 1.0 +#: import GLShaderLexer pygments.lexers.GLShaderLexer : viewer: viewer @@ -13,16 +14,18 @@ text: 'Fragment Shader' size_hint_y: None height: self.texture_size[1] + 10 - TextInput: + CodeInput: text: root.fs + lexer: GLShaderLexer() on_text: root.fs = args[1] Label: text: 'Vertex Shader' size_hint_y: None height: self.texture_size[1] + 10 - TextInput: + CodeInput: text: root.vs + lexer: GLShaderLexer() on_text: root.vs = args[1] ShaderViewer: diff --git a/examples/demo/showcase/main.py b/examples/demo/showcase/main.py index 7c1923beb..0ae5f9a3b 100644 --- a/examples/demo/showcase/main.py +++ b/examples/demo/showcase/main.py @@ -16,6 +16,7 @@ from kivy.properties import StringProperty from kivy.clock import Clock import random + class Showcase(FloatLayout): pass @@ -60,6 +61,7 @@ class BoxLayoutShowcase(FloatLayout): super(BoxLayoutShowcase, self).__init__(**kwargs) self.buttons = 0 self.txt = 'horizontal' + self.text_size = self.size Clock.schedule_once(self.add_button, 1) def add_button(self, *l): @@ -71,7 +73,10 @@ class BoxLayoutShowcase(FloatLayout): self.txt = self.blayout.orientation = 'horizontal' else: self.txt = self.blayout.orientation = 'vertical' - self.blayout.add_widget(Button(text = self.txt)) + + btn = Button(text=self.txt, halign='center', valign='middle') + btn.bind(size=btn.setter('text_size')) + self.blayout.add_widget(btn) Clock.schedule_once(self.add_button, 1) @@ -87,9 +92,9 @@ class FloatLayoutShowcase(FloatLayout): if self.buttons > 5: self.buttons = 0 self.flayout.clear_widgets() - self.flayout.add_widget(Button(text = 'no restrictions\n what so ever', - size_hint = (None, None), size = (150, 40), - pos_hint = {'x':random.random(), 'y': random.random()})) + self.flayout.add_widget(Button(text='no restrictions\n what so ever', + size_hint=(None, None), size=(150, 40), + pos_hint={'x': random.random(), 'y': random.random()})) Clock.schedule_once(self.add_button, 1) @@ -106,7 +111,7 @@ class GridLayoutShowcase(FloatLayout): def add_button(self, *l): self.buttons += 1 - if self.buttons > 20: + if self.buttons > 10: self.buttons = 0 self.glayout.clear_widgets() if self.txt == "rows = 3": @@ -117,7 +122,7 @@ class GridLayoutShowcase(FloatLayout): self.glayout.rows = 3 self.glayout.cols = 7 self.txt = "rows = 3" - self.glayout.add_widget(Button(text = self.txt)) + self.glayout.add_widget(Button(text=self.txt)) Clock.schedule_once(self.add_button, 1) @@ -157,9 +162,11 @@ class StandardWidgets(FloatLayout): class ComplexWidgets(FloatLayout): pass + class TreeViewWidgets(FloatLayout): pass + class ShowcaseApp(App): def on_select_node(self, instance, value): @@ -243,17 +250,16 @@ class ShowcaseApp(App): return col def show_popup(self): - btnclose = Button(text='Close this popup', size_hint_y=None, height=50) + btnclose = Button(text='Close this popup', size_hint_y=None, height='50sp') content = BoxLayout(orientation='vertical') content.add_widget(Label(text='Hello world')) content.add_widget(btnclose) popup = Popup(content=content, title='Modal popup example', - size_hint=(None, None), size=(300, 300), - auto_dismiss=False) + size_hint=(None, None), size=('300dp', '300dp')) btnclose.bind(on_release=popup.dismiss) button = Button(text='Open popup', size_hint=(None, None), - size=(150, 70)) - button.bind(on_release=popup.open) + size=('150sp', '70dp'), + on_release=popup.open) popup.open() col = AnchorLayout() col.add_widget(button) diff --git a/examples/demo/showcase/showcase.kv b/examples/demo/showcase/showcase.kv index 41f0efcc8..9b6cbb7fd 100644 --- a/examples/demo/showcase/showcase.kv +++ b/examples/demo/showcase/showcase.kv @@ -14,17 +14,18 @@ orientation: 'vertical' BoxLayout: - padding: 10 - spacing: 10 + padding: '10dp' + spacing: '10dp' size_hint: 1, None pos_hint: {'top': 1} - height: 44 + height: '44dp' Image: size_hint: None, None - size: 24, 24 - source: 'data/logo/kivy-icon-24.png' + size: '24dp', '24dp' + source: 'data/logo/kivy-icon-64.png' + mipmap: True Label: - height: 24 + height: '24dp' text_size: self.width, None color: (1, 1, 1, .8) text: 'Kivy %s - Showcase' % kivy.__version__ @@ -55,7 +56,7 @@ [Title@Label] pos_hint: {'center_x': .5, 'y': .3} text: ctx.text - font_size: 22 + font_size: '29dp' Title: @@ -153,9 +154,9 @@ [HSeparator@Label]: size_hint_y: None - height: 45 + height: max(dp(45), self.texture_size[1] + dp(10)) text: ctx.text if 'text' in ctx else '' - text_size: self.size + text_size: self.width, None valign: 'middle' halign: 'center' canvas.before: @@ -257,18 +258,18 @@ TextInput: text: 'Monoline textinput' size_hint_y: None - height: 30 + height: sp(30) TextInput: text: 'This is a password' size_hint_y: None - height: 30 + height: sp(30) password: True TextInput: text: 'Readonly textinput' size_hint_y: None - height: 30 + height: sp(30) readonly: True TextInput: @@ -313,6 +314,8 @@ title: 'Panel 1' Label: text: 'This is a label fit to the content view' + halign: 'center' + text_size: self.width, None AccordionItem: title: 'Panel 2' @@ -323,6 +326,8 @@ title: 'Panel 3' Label: text: 'This is a label fit to the content view' + halign: 'center' + text_size: self.width, None Accordion: @@ -333,6 +338,7 @@ title: 'Panel 1' Label: text: 'This is a label fit to the content view' + text_size: self.width, None AccordionItem: title: 'Panel 2' @@ -343,6 +349,7 @@ title: 'Panel 3' Label: text: 'This is a label fit to the content view' + text_size: self.width, None VSeparator diff --git a/examples/demo/touchtracer/touchtracer.kv b/examples/demo/touchtracer/touchtracer.kv index b6326865b..259bb5329 100644 --- a/examples/demo/touchtracer/touchtracer.kv +++ b/examples/demo/touchtracer/touchtracer.kv @@ -10,17 +10,18 @@ size: self.size BoxLayout: - padding: 10 - spacing: 10 + padding: '10dp' + spacing: '10dp' size_hint: 1, None pos_hint: {'top': 1} - height: 44 + height: '44dp' Image: size_hint: None, None - size: 24, 24 - source: 'data/logo/kivy-icon-24.png' + size: '24dp', '24dp' + source: 'data/logo/kivy-icon-64.png' + mipmap: True Label: - height: 24 + height: '24dp' text_size: self.width, None color: (1, 1, 1, .8) text: 'Kivy %s - Touchtracer' % kivy.__version__ diff --git a/examples/guide/firstwidget/3_draw_ellipse.py b/examples/guide/firstwidget/3_draw_ellipse.py index c3cc59e27..05ca0f059 100644 --- a/examples/guide/firstwidget/3_draw_ellipse.py +++ b/examples/guide/firstwidget/3_draw_ellipse.py @@ -4,14 +4,16 @@ from kivy.graphics import Color, Ellipse class MyPaintWidget(Widget): + def on_touch_down(self, touch): with self.canvas: Color(1, 1, 0) d = 30. - Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d)) + Ellipse(pos=(touch.x - d / 2, touch.y - d / 2), size=(d, d)) class MyPaintApp(App): + def build(self): return MyPaintWidget() diff --git a/examples/guide/firstwidget/4_draw_line.py b/examples/guide/firstwidget/4_draw_line.py index b56a118bd..a7d286acd 100644 --- a/examples/guide/firstwidget/4_draw_line.py +++ b/examples/guide/firstwidget/4_draw_line.py @@ -4,19 +4,20 @@ from kivy.graphics import Color, Ellipse, Line class MyPaintWidget(Widget): + def on_touch_down(self, touch): - userdata = touch.ud with self.canvas: Color(1, 1, 0) d = 30. - Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d)) - userdata['line'] = Line(points=(touch.x, touch.y)) + Ellipse(pos=(touch.x - d / 2, touch.y - d / 2), size=(d, d)) + touch.ud['line'] = Line(points=(touch.x, touch.y)) def on_touch_move(self, touch): touch.ud['line'].points += [touch.x, touch.y] class MyPaintApp(App): + def build(self): return MyPaintWidget() diff --git a/examples/guide/firstwidget/5_random_colors.py b/examples/guide/firstwidget/5_random_colors.py index 37ad86074..25906b865 100644 --- a/examples/guide/firstwidget/5_random_colors.py +++ b/examples/guide/firstwidget/5_random_colors.py @@ -5,20 +5,21 @@ from kivy.graphics import Color, Ellipse, Line class MyPaintWidget(Widget): + def on_touch_down(self, touch): - userdata = touch.ud - userdata['color'] = c = (random(), random(), random()) + color = (random(), random(), random()) with self.canvas: - Color(*c) + Color(*color) d = 30. - Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d)) - userdata['line'] = Line(points=(touch.x, touch.y)) + Ellipse(pos=(touch.x - d / 2, touch.y - d / 2), size=(d, d)) + touch.ud['line'] = Line(points=(touch.x, touch.y)) def on_touch_move(self, touch): touch.ud['line'].points += [touch.x, touch.y] class MyPaintApp(App): + def build(self): return MyPaintWidget() diff --git a/examples/guide/firstwidget/6_button.py b/examples/guide/firstwidget/6_button.py index 041dc4252..667aed5e7 100644 --- a/examples/guide/firstwidget/6_button.py +++ b/examples/guide/firstwidget/6_button.py @@ -8,13 +8,12 @@ from kivy.graphics import Color, Ellipse, Line class MyPaintWidget(Widget): def on_touch_down(self, touch): - userdata = touch.ud - userdata['color'] = c = (random(), 1, 1) + color = (random(), 1, 1) with self.canvas: - Color(*c, mode='hsv') - d = 30 - Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d)) - userdata['line'] = Line(points=(touch.x, touch.y)) + Color(*color, mode='hsv') + d = 30. + Ellipse(pos=(touch.x - d / 2, touch.y - d / 2), size=(d, d)) + touch.ud['line'] = Line(points=(touch.x, touch.y)) def on_touch_move(self, touch): touch.ud['line'].points += [touch.x, touch.y] diff --git a/examples/kv/builder_template.py b/examples/kv/builder_template.py index 9aa8a3b04..86a456abd 100644 --- a/examples/kv/builder_template.py +++ b/examples/kv/builder_template.py @@ -17,7 +17,7 @@ class BlehApp(App): def build(self): root = BoxLayout() for idx, word in enumerate(('Hello', 'World')): - wid = Builder.template('BlehItem', { + wid = Builder.template('BlehItem', **{ 'idx': idx, 'word': word, }) root.add_widget(wid) diff --git a/examples/widgets/boxlayout_poshint.py b/examples/widgets/boxlayout_poshint.py new file mode 100644 index 000000000..fd2513b43 --- /dev/null +++ b/examples/widgets/boxlayout_poshint.py @@ -0,0 +1,52 @@ +from kivy.uix.gridlayout import GridLayout +from kivy.app import App +from kivy.lang import Builder + +Builder.load_string(''' +: + cols: 1 + + BoxLayout: + orientation: 'vertical' + Button: + size_hint_x: 0.4 + pos_hint: {'x': 0} + text: 'pos_hint: x=0' + + Button: + size_hint_x: 0.2 + pos_hint: {'center_x': 0.5} + text: 'pos_hint: center_x=0.5' + + Button: + size_hint_x: 0.4 + pos_hint: {'right': 1} + text: 'pos_hint: right=1' + + BoxLayout: + Button: + size_hint_y: 0.4 + pos_hint: {'y': 0} + text: 'pos_hint: y=0' + + Button: + size_hint_y: 0.2 + pos_hint: {'center_y': .5} + text: 'pos_hint: center_y=0.5' + + Button: + size_hint_y: 0.4 + pos_hint: {'top': 1} + text: 'pos_hint: top=1' +''') + +class Demo(GridLayout): + pass + + +class DemoApp(App): + def build(self): + return Demo() + +if __name__ == '__main__': + DemoApp().run() diff --git a/examples/widgets/carousel_buttons.py b/examples/widgets/carousel_buttons.py new file mode 100644 index 000000000..fbfb1c600 --- /dev/null +++ b/examples/widgets/carousel_buttons.py @@ -0,0 +1,37 @@ +''' +Carousel example with button inside. +This is a tiny test for testing the scroll distance/timeout +And ensure the down/up are dispatched if no gesture is done. +''' +from kivy.uix.carousel import Carousel +from kivy.uix.gridlayout import GridLayout +from kivy.app import App +from kivy.lang import Builder + +Builder.load_string(''' +: + cols: 3 + Label: + text: str(id(root)) + Button + Button + Button + Button + Button + Button + Button + Button +''') + +class Page(GridLayout): + pass + +class TestApp(App): + def build(self): + root = Carousel() + for x in xrange(10): + root.add_widget(Page()) + return root + +if __name__ == '__main__': + TestApp().run() diff --git a/examples/widgets/codeinput.py b/examples/widgets/codeinput.py new file mode 100644 index 000000000..858b4e54a --- /dev/null +++ b/examples/widgets/codeinput.py @@ -0,0 +1,179 @@ +from kivy.app import App +from kivy.extras.highlight import KivyLexer +from kivy.uix.spinner import Spinner, SpinnerOption +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.codeinput import CodeInput +from kivy.uix.popup import Popup +from kivy.properties import ListProperty +from kivy.core.window import Window +from pygments import lexers +from pygame import font as fonts +import codecs, os + +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 Fnt_SpinnerOption(SpinnerOption): + pass + + +class LoadDialog(Popup): + + def load(self, path, selection): + self.choosen_file = [None, ] + self.choosen_file = selection + Window.title = selection[0][selection[0].rfind(os.sep)+1:] + self.dismiss() + + def cancel(self): + self.dismiss() + + +class SaveDialog(Popup): + + def save(self, path, selection): + _file = codecs.open(selection, 'w', encoding='utf8') + _file.write(self.text) + Window.title = selection[selection.rfind(os.sep)+1:] + _file.close() + self.dismiss() + + def cancel(self): + self.dismiss() + + +class CodeInputTest(App): + + files = ListProperty([None, ]) + + def build(self): + b = BoxLayout(orientation='vertical') + languages = Spinner( + text='language', + values=sorted(['KvLexer', ] + lexers.LEXERS.keys())) + + languages.bind(text=self.change_lang) + + menu = BoxLayout( + size_hint_y=None, + height='30pt') + fnt_size = Spinner( + text='12', + values=map(str, range(5, 40))) + fnt_size.bind(text=self._update_size) + fnt_name = Spinner( + text='DroidSansMono', + option_cls=Fnt_SpinnerOption, + values=sorted(map(str, fonts.get_fonts()))) + fnt_name.bind(text=self._update_font) + mnu_file = Spinner( + text='File', + values=('Open', 'SaveAs', 'Save', 'Close')) + mnu_file.bind(text=self._file_menu_selected) + + menu.add_widget(mnu_file) + menu.add_widget(fnt_size) + menu.add_widget(fnt_name) + menu.add_widget(languages) + b.add_widget(menu) + + self.codeinput = CodeInput( + lexer=KivyLexer(), + font_name='data/fonts/DroidSansMono.ttf', font_size=12, + text=example_text) + + b.add_widget(self.codeinput) + + return b + + def _update_size(self, instance, size): + self.codeinput.font_size = float(size) + + def _update_font(self, instance, fnt_name): + instance.font_name = self.codeinput.font_name =\ + fonts.match_font(fnt_name) + + def _file_menu_selected(self, instance, value): + if value == 'File': + return + instance.text = 'File' + if value == 'Open': + if not hasattr(self, 'load_dialog'): + self.load_dialog = LoadDialog() + self.load_dialog.open() + self.load_dialog.bind(choosen_file=self.setter('files')) + elif value == 'SaveAs': + if not hasattr(self, 'saveas_dialog'): + self.saveas_dialog = SaveDialog() + self.saveas_dialog.text = self.codeinput.text + self.saveas_dialog.open() + elif value == 'Save': + if self.files[0]: + _file = codecs.open(self.files[0], 'w', encoding='utf8') + _file.write(self.codeinput.text) + _file.close() + elif value == 'Close': + if self.files[0]: + self.codeinput.text = '' + Window.title = 'untitled' + + def on_files(self, instance, values): + if not values[0]: + return + _file = codecs.open(values[0], 'r', encoding='utf8') + self.codeinput.text = _file.read() + _file.close() + + def change_lang(self, instance, l): + if l == 'KvLexer': + lx = KivyLexer() + else: + lx = lexers.get_lexer_by_name(lexers.LEXERS[l][2][0]) + self.codeinput.lexer = lx + +if __name__ == '__main__': + CodeInputTest().run() diff --git a/examples/widgets/codeinputtest.kv b/examples/widgets/codeinputtest.kv new file mode 100644 index 000000000..1263dc520 --- /dev/null +++ b/examples/widgets/codeinputtest.kv @@ -0,0 +1,55 @@ +#:import fonts pygame.font +#:import os os +: + fnt_name: fonts.match_font(self.text) + font_name: self.fnt_name if self.fnt_name else self.font_name + +: + title: filechooser.path + choosen_file: None + BoxLayout: + size: root.size + pos: root.pos + orientation: "vertical" + FileChooserListView: + id: filechooser + path: os.getcwd() + + BoxLayout: + size_hint_y: None + height: 30 + Button: + text: "Cancel" + on_release: root.cancel() + + Button: + text: "Load" + on_release: root.load(filechooser.path, filechooser.selection) + +: + text_input: text_input + BoxLayout: + size: root.size + pos: root.pos + orientation: "vertical" + FileChooserListView: + id: filechooser + path: os.getcwd() + on_selection: text_input.text = self.selection and self.selection[0] or '' + + TextInput: + id: text_input + size_hint_y: None + height: 30 + multiline: False + + BoxLayout: + size_hint_y: None + height: 30 + Button: + text: "Cancel" + on_release: root.cancel() + + Button: + text: "Save" + on_release: root.save(filechooser.path, text_input.text) \ No newline at end of file diff --git a/examples/widgets/popup_with_kv.py b/examples/widgets/popup_with_kv.py index 61bf7ca8d..0a42c4527 100644 --- a/examples/widgets/popup_with_kv.py +++ b/examples/widgets/popup_with_kv.py @@ -22,8 +22,7 @@ class CustomPopup(Popup): class TestApp(App): def build(self): - b = Button() - b.bind(on_press=self.show_popup) + b = Button(on_press=self.show_popup) return b def show_popup(self, b): diff --git a/examples/widgets/tabbed_panel_showcase.py b/examples/widgets/tabbed_panel_showcase.py index 2bdff4ba9..4b52bd1c7 100644 --- a/examples/widgets/tabbed_panel_showcase.py +++ b/examples/widgets/tabbed_panel_showcase.py @@ -54,8 +54,9 @@ Builder.load_string(''' size_hint: (.45, .45) pos_hint: {'center_x': .25, 'y': .55} - #replace the default tab with our custom tab + #replace the default tab with our custom tab class default_tab_cls: sh.__class__ + do_default_tab: True default_tab_content: default_content tab_width: 40 tab_height: 70 @@ -100,19 +101,18 @@ Builder.load_string(''' size_hint: None, 1 orientation: 'vertical' width: 22 - Widget: - Button: - border: 0,0,0,0 - background_normal: 'tools/theming/defaulttheme/close.png' - on_release: root.panel.remove_widget(root) - Widget: + Image: + source: 'tools/theming/defaulttheme/close.png' + on_touch_down: + if self.collide_point(*args[1].pos) :\ + root.panel.remove_widget(root) tab_pos: 'top_right' size_hint: (.45, .45) pos_hint: {'center_x': .75, 'y': .55} - #replace the default tab with our custom tab + # replace the default tab with our custom tab default_tab: def_tab #allow variable tab width tab_width: None @@ -170,29 +170,23 @@ Builder.load_string(''' tab_pos: 'bottom_left' size_hint: (.45, .45) pos_hint: {'center_x': .25, 'y': .02} - default_tab_text: 'Settings' - default_tab_content: default_content - FloatLayout: + do_default_tab: False + + TabbedPanelItem: + id: settings + text: 'Settings' RstDocument: - id: default_content text: '\\n'.join(("Normal tabs", "-------------",\ - "Tabs in \\'%s\\' position" %root.tab_pos)) - Image: - id: tab_2_content - pos: self.parent.pos - size: self.parent.size - source: 'data/images/defaulttheme-0.png' - Image: - id: tab_3_content - pos:self.parent.pos - size: self.parent.size - source: 'data/images/image-loading.gif' - TabbedPanelHeader: + "Tabs in \\'%s\\' position" %root.tab_pos)) + TabbedPanelItem: text: 'tab2' - content: tab_2_content - TabbedPanelHeader: + BubbleButton: + text: 'switch to settings' + on_press: root.switch_to(settings) + TabbedPanelItem: text: 'tab3' - content: tab_3_content + Image: + source: 'data/images/image-loading.gif' tab_pos: 'right_top' @@ -257,26 +251,23 @@ Builder.load_string(''' class Tp(TabbedPanel): + #override tab switching method to animate on tab switch def switch_to(self, header): - anim = Animation(color=(1, 1, 1, 0), d =.24, t = 'in_out_quad') + anim = Animation(opacity=0, d=.24, t='in_out_quad') def start_anim(_anim, child, in_complete, *lt): - if hasattr(child, 'color'): - _anim.start(child) - elif not in_complete: - _on_complete() + _anim.start(child) def _on_complete(*lt): - if hasattr(header.content, 'color'): - header.content.color = (0, 0, 0, 0) - anim = Animation(color = - (1, 1, 1, 1), d =.23, t = 'in_out_quad') + if header.content: + header.content.opacity = 0 + anim = Animation(opacity=1, d=.43, t='in_out_quad') start_anim(anim, header.content, True) super(Tp, self).switch_to(header) anim.bind(on_complete = _on_complete) - if self.content: + if self.current_tab.content: start_anim(anim, self.current_tab.content, False) else: _on_complete() diff --git a/examples/widgets/tabbedpanel.py b/examples/widgets/tabbedpanel.py index a58dc9c0d..49fcb3bb2 100644 --- a/examples/widgets/tabbedpanel.py +++ b/examples/widgets/tabbedpanel.py @@ -15,30 +15,24 @@ Builder.load_string(""" : size_hint: .5, .5 pos_hint: {'center_x': .5, 'center_y': .5} - default_tab_content: set1_content + do_default_tab: False - Label: - id: set1_content - text: 'First tab content area' - - BoxLayout: - id: set2_content + TabbedPanelItem: + text: 'first tab' Label: - text: 'Second tab content area' - Button: - text: 'Button that does nothing' + text: 'First tab content area' + TabbedPanelItem: + text: 'tab2' + BoxLayout: + Label: + text: 'Second tab content area' + Button: + text: 'Button that does nothing' + TabbedPanelItem: + text: 'tab3' + RstDocument: + text: '\\n'.join(("Hello world", "-----------", "You are in the third tab.")) - RstDocument: - id: set3_content - text: '\\n'.join(("Hello world", "-----------", "You are in the third tab.")) - - # now categorize widgets inserted above in a specific header - TabbedPanelHeader: - text: 'Tab 2' - content: set2_content - TabbedPanelHeader: - text: 'Tab 3' - content: set3_content """) class Test(TabbedPanel): diff --git a/examples/widgets/unicode_textinput.py b/examples/widgets/unicode_textinput.py index 9f8458cb5..c2ce664d5 100644 --- a/examples/widgets/unicode_textinput.py +++ b/examples/widgets/unicode_textinput.py @@ -5,25 +5,50 @@ from kivy.lang import Builder from kivy.properties import StringProperty, ObjectProperty from kivy.uix.boxlayout import BoxLayout from kivy.uix.floatlayout import FloatLayout +from kivy.uix.spinner import SpinnerOption from kivy.uix.popup import Popup +import os Builder.load_string( ''' #: import utils kivy #: import os os +#: import font pygame.font +#: import Factory kivy.factory.Factory + + fnt_name: font.match_font(self.text) + font_name: self.fnt_name if self.fnt_name else self.font_name + orientation: 'vertical' txt_input: unicode_txt + spnr_fnt: fnt_spnr + BoxLayout: + size_hint: 1, .05 + Spinner: + id: fnt_spnr + text: 'DroidSansMono' + fnt_name: font.match_font(self.text) + font_name: self.fnt_name if self.fnt_name else self.font_name + values: sorted(font.get_fonts()) + option_cls: Factory.FntSpinnerOption + Spinner: + id: fntsz_spnr + text: '15' + values: map(str, map(sp, range(5,39))) ScrollView: size_hint: 1, .9 TextInput: id: unicode_txt - background_color: [.5, .5, .5, 1] - foreground_color: .5, 1, .5, 1 + background_color: .8811, .8811, .8811, 1 + foreground_color: 0, 0, 0, 1 + font_name: fnt_spnr.font_name + font_size: fntsz_spnr.text + 'sp' text: root.unicode_string size_hint: 1, None - height: 1364 + height: 1494 + on_font_name: self.height = (self.line_height + self.padding_y) * (len(self._lines)-1) BoxLayout: size_hint: 1, .05 Label: @@ -86,6 +111,8 @@ if _platform == 'linux' else '/system/fonts' if _platform == 'android' else os.p (filechooser.path, filechooser.selection) ''') +class FntSpinnerOption(SpinnerOption): + pass class LoadDialog(FloatLayout): load = ObjectProperty(None) @@ -172,6 +199,9 @@ Yiddish: דער גיך ברוין פוקס דזשאַמפּס איבער def load(self, _path, _fname): self.txt_input.font_name = _fname[0] + _f_name = _fname[0][_fname[0].rfind(os.sep) + 1:] + self.spnr_fnt.text = _f_name[:_f_name.rfind('.')] + self._popup.dismiss() def show_load(self): diff --git a/kivy/__init__.py b/kivy/__init__.py index b82419f20..14f989c39 100644 --- a/kivy/__init__.py +++ b/kivy/__init__.py @@ -28,7 +28,7 @@ __all__ = ( 'kivy_config_fn', 'kivy_usermodules_dir', ) -__version__ = '1.4.1-dev' +__version__ = '1.4.2-dev' import sys import shutil @@ -201,7 +201,7 @@ for option in kivy_options: if key in environ: try: if type(kivy_options[option]) in (list, tuple): - kivy_options[option] = (str(environ[key]), ) + kivy_options[option] = environ[key].split(',') else: kivy_options[option] = environ[key].lower() in \ ('true', '1', 'yes', 'yup') @@ -369,11 +369,15 @@ if not environ.get('KIVY_DOC_INCLUDE'): Logger.info('Core: Kivy configuration saved.') sys.exit(0) -# android hooks: force fullscreen and add android touch input provider -if platform() == 'android': - from kivy.config import Config - Config.set('graphics', 'fullscreen', 'auto') - Config.remove_section('input') - Config.add_section('input') - Config.set('input', 'androidtouch', 'android') + # configure all activated modules + from kivy.modules import Modules + Modules.configure() + + # android hooks: force fullscreen and add android touch input provider + if platform() == 'android': + from kivy.config import Config + Config.set('graphics', 'fullscreen', 'auto') + Config.remove_section('input') + Config.add_section('input') + Config.set('input', 'androidtouch', 'android') diff --git a/kivy/_event.pxd b/kivy/_event.pxd new file mode 100644 index 000000000..d1731a53f --- /dev/null +++ b/kivy/_event.pxd @@ -0,0 +1,7 @@ + +cdef class EventDispatcher(object): + cdef dict __event_stack + cdef dict __properties + cdef dict __storage + cpdef dict properties(self) + cdef readonly int uid diff --git a/kivy/_event.pyx b/kivy/_event.pyx index 8e1f57f2d..ffaebcb90 100644 --- a/kivy/_event.pyx +++ b/kivy/_event.pyx @@ -19,7 +19,6 @@ from functools import partial from kivy.weakmethod import WeakMethod from kivy.properties cimport Property, ObjectProperty -cdef tuple forbidden_properties = ('touch_down', 'touch_move', 'touch_up') cdef int widget_uid = 0 cdef dict cache_properties = {} @@ -29,18 +28,17 @@ cdef class EventDispatcher(object): See the module docstring for usage. ''' - cdef dict __event_stack - cdef dict __properties - def __cinit__(self, *largs, **kwargs): global widget_uid, cache_properties - cdef dict widget_dict = self.__dict__ cdef dict cp = cache_properties cdef dict attrs_found cdef list attrs cdef Property attr cdef str k + self.__event_stack = {} + self.__storage = {} + __cls__ = self.__class__ # XXX for the moment, we need to create a uniq id for properties. @@ -48,8 +46,7 @@ cdef class EventDispatcher(object): # are longer than using a custom __uid. I hope we can figure out a way # of doing that without require any python code. :) widget_uid += 1 - widget_dict['__uid'] = widget_uid - widget_dict['__storage'] = {} + self.uid = widget_uid if __cls__ not in cp: attrs_found = cp[__cls__] = {} @@ -58,7 +55,7 @@ cdef class EventDispatcher(object): uattr = getattr(__cls__, k) if not isinstance(uattr, Property): continue - if k in forbidden_properties: + if k == 'touch_down' or k == 'touch_move' or k == 'touch_up': raise Exception('The property <%s> have a forbidden name' % k) attrs_found[k] = uattr else: @@ -175,6 +172,7 @@ cdef class EventDispatcher(object): print 'press on button', obj, 'with date:', value ''' + cdef Property prop for key, value in kwargs.iteritems(): if key[:3] == 'on_': if key not in self.__event_stack: @@ -183,13 +181,15 @@ cdef class EventDispatcher(object): handler = WeakMethod(value) self.__event_stack[key].append(handler) else: - self.__properties[key].bind(self, value) + prop = self.__properties[key] + prop.bind(self, value) def unbind(self, **kwargs): '''Unbind properties from callback functions. Same usage as :func:`bind`. ''' + cdef Property prop for key, value in kwargs.iteritems(): if key[:3] == 'on_': if key not in self.__event_stack: @@ -201,7 +201,8 @@ cdef class EventDispatcher(object): self.__event_stack[key].remove(handler) break else: - self.__properties[key].unbind(self, value) + prop = self.__properties[key] + prop.unbind(self, value) def dispatch(self, str event_type, *largs): '''Dispatch an event across all the handler added in bind(). @@ -224,11 +225,13 @@ cdef class EventDispatcher(object): # # Properties # - def __proxy_setter(self, dstinstance, name, instance, value): - self.__properties[name].__set__(dstinstance, value) + def __proxy_setter(self, EventDispatcher dstinstance, name, instance, value): + cdef Property prop = self.__properties[name] + prop.set(dstinstance, value) - def __proxy_getter(self, dstinstance, name, instance): - return self.__properties[name].__get__(dstinstance) + def __proxy_getter(self, EventDispatcher dstinstance, name, instance): + cdef Property prop = self.__properties[name] + return prop.get(dstinstance) def setter(self, name): '''Return the setter of a property. Useful if you want to directly bind @@ -261,7 +264,7 @@ cdef class EventDispatcher(object): ''' return self.__properties[name] - cpdef dict properties(self): + cpdef dict properties(EventDispatcher self): '''Return all the properties in that class in a dictionnary of key/property class. Can be used for introspection. @@ -270,7 +273,7 @@ cdef class EventDispatcher(object): cdef dict ret, p ret = {} p = self.__properties - for x in self.__dict__['__storage'].keys(): + for x in self.__storage: ret[x] = p[x] return ret diff --git a/kivy/animation.py b/kivy/animation.py index be21c2cd0..ae200def3 100644 --- a/kivy/animation.py +++ b/kivy/animation.py @@ -126,7 +126,7 @@ class Animation(EventDispatcher): return self._transition @property - def animated_animated_properties(self): + def animated_properties(self): '''Return the properties used to animate ''' return self._animated_properties @@ -231,7 +231,7 @@ class Animation(EventDispatcher): # no more properties to animation ? kill the animation. if not props['properties']: - self.stop(widget) + self.cancel(widget) # # Private diff --git a/kivy/app.py b/kivy/app.py index 1a296abd9..b05ccd9a5 100644 --- a/kivy/app.py +++ b/kivy/app.py @@ -381,6 +381,7 @@ class App(EventDispatcher): if clsname.endswith('App'): clsname = clsname[:-3] filename = join(kv_directory, '%s.kv' % clsname.lower()) + Logger.debug('App: Loading kv <{0}>'.format(filename)) if not exists(filename): Logger.debug('App: kv <%s> not found' % filename) return False @@ -472,6 +473,7 @@ class App(EventDispatcher): filename = self.get_application_config() if filename is None: return config + Logger.debug('App: Loading configuration <{0}>'.format(filename)) if exists(filename): try: config.read(filename) @@ -481,6 +483,8 @@ class App(EventDispatcher): self.build_config(config) pass else: + Logger.debug('App: First configuration, create <{0}>'.format( + filename)) config.filename = filename config.write() return config @@ -542,6 +546,10 @@ class App(EventDispatcher): runTouchApp() self.dispatch('on_stop') + # Clear the window children + for child in window.children: + window.remove_widget(child) + def stop(self, *largs): '''Stop the application. 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/base.py b/kivy/base.py index 1374d4eb3..7e618b157 100644 --- a/kivy/base.py +++ b/kivy/base.py @@ -15,12 +15,10 @@ __all__ = ( 'stopTouchApp', ) -from os import environ from kivy.config import Config from kivy.logger import Logger from kivy.clock import Clock from kivy.event import EventDispatcher -from kivy.utils import platform, reify # private vars EventLoop = None @@ -86,18 +84,19 @@ class EventLoopBase(EventDispatcher): ''' def __init__(self): + self.register_event_type('on_start') + self.register_event_type('on_pause') + self.register_event_type('on_stop') super(EventLoopBase, self).__init__() self.quit = False self.input_events = [] self.postproc_modules = [] self.status = 'idle' self.input_providers = [] + self.input_providers_autoremove = [] self.event_listeners = [] self.window = None self.me_list = [] - self.register_event_type('on_start') - self.register_event_type('on_pause') - self.register_event_type('on_stop') @property def touches(self): @@ -105,32 +104,6 @@ class EventLoopBase(EventDispatcher): ''' return self.me_list - @reify - def dpi(self): - '''Return the DPI of the screen. Depending of the platform, the DPI can - be taken from the Window provider (Desktop mainly), or from - platform-specific module (like android/ios). - - On desktop, you can overload the value returned by the Window object - (96 by default), by setting the environ KIVY_DPI:: - - KIVY_DPI=200 python main.py - - .. versionadded:: 1.4.0 - ''' - custom_dpi = environ.get('KIVY_DPI') - if custom_dpi: - return float(custom_dpi) - - plat = platform() - if plat == 'android': - import android - return android.get_dpi() - - # for all other platforms.. - self.ensure_window() - return self.window.dpi - def ensure_window(self): '''Ensure that we have an window ''' @@ -141,11 +114,13 @@ class EventLoopBase(EventDispatcher): ''' self.window = window - def add_input_provider(self, provider): + def add_input_provider(self, provider, auto_remove=False): '''Add a new input provider to listen for touch event ''' - if not provider in self.input_providers: + if provider not in self.input_providers: self.input_providers.append(provider) + if auto_remove: + self.input_providers_autoremove.append(provider) def remove_input_provider(self, provider): '''Remove an input provider @@ -184,19 +159,27 @@ class EventLoopBase(EventDispatcher): def stop(self): '''Stop all input providers and call callbacks registered using EventLoop.add_stop_callback()''' - #stop in reverse order that we started them!! (liek push pop), - #very important becasue e.g. wm_touch and WM_PEN both store - #old window proc and teh restore, if order is messed big problem - #happens, crashing badly without error - for provider in reversed(self.input_providers): + + # XXX stop in reverse order that we started them!! (like push pop), very + # important because e.g. wm_touch and WM_PEN both store old window proc + # and the restore, if order is messed big problem happens, crashing + # badly without error + for provider in reversed(self.input_providers[:]): provider.stop() + if provider in self.input_providers_autoremove: + self.input_providers_autoremove.remove(provider) + self.input_providers.remove(provider) + + # ensure any restart will not break anything later. + self.input_events = [] self.status = 'stopped' self.dispatch('on_stop') def add_postproc_module(self, mod): '''Add a postproc input module (DoubleTap, RetainTouch are default)''' - self.postproc_modules.append(mod) + if mod not in self.postproc_modules: + self.postproc_modules.append(mod) def remove_postproc_module(self, mod): '''Remove a postproc module''' @@ -289,10 +272,11 @@ class EventLoopBase(EventDispatcher): self.input_events = mod.process(events=self.input_events) # real dispatch input - for etype, me in self.input_events: - self.post_dispatch_input(etype, me) - - self.input_events = [] + input_events = self.input_events + pop = input_events.pop + post_dispatch_input = self.post_dispatch_input + while input_events: + post_dispatch_input(*pop(0)) def idle(self): '''This function is called every frames. By default : @@ -420,7 +404,7 @@ def runTouchApp(widget=None, slave=False): # create provider p = provider(key, args) if p: - EventLoop.add_input_provider(p) + EventLoop.add_input_provider(p, True) # add postproc modules for mod in kivy_postproc_modules.values(): @@ -428,7 +412,8 @@ def runTouchApp(widget=None, slave=False): # add main widget if widget and EventLoop.window: - EventLoop.window.add_widget(widget) + if widget not in EventLoop.window.children: + EventLoop.window.add_widget(widget) # start event loop Logger.info('Base: Start application main loop') diff --git a/kivy/config.py b/kivy/config.py index 7be5a88e8..6530ee358 100644 --- a/kivy/config.py +++ b/kivy/config.py @@ -138,6 +138,16 @@ Available configuration tokens property in :class:`~kivy.uix.scrollview.Scrollview` widget. Check the widget documentation for more information. + `scroll_stoptime`: int + Default value of :data:`~kivy.uix.scrollview.Scrollview.scroll_stoptime` + property in :class:`~kivy.uix.scrollview.Scrollview` widget. + Check the widget documentation for more information. + + `scroll_moves`: int + Default value of :data:`~kivy.uix.scrollview.Scrollview.scroll_moves` + property in :class:`~kivy.uix.scrollview.Scrollview` widget. + Check the widget documentation for more information. + :modules: You can activate modules with this syntax:: @@ -174,7 +184,7 @@ from kivy.logger import Logger, logger_config_update from kivy.utils import OrderedDict # Version number of current configuration format -KIVY_CONFIG_VERSION = 6 +KIVY_CONFIG_VERSION = 7 #: Kivy configuration object Config = None @@ -388,7 +398,7 @@ if not environ.get('KIVY_DOC_INCLUDE'): elif version == 3: # add token for scrollview - Config.setdefault('widgets', 'scroll_timeout', '250') + Config.setdefault('widgets', 'scroll_timeout', '55') Config.setdefault('widgets', 'scroll_distance', '20') Config.setdefault('widgets', 'scroll_friction', '1.') @@ -408,6 +418,13 @@ if not environ.get('KIVY_DOC_INCLUDE'): elif version == 5: Config.setdefault('graphics', 'resizable', '1') + elif version == 6: + # if the timeout is still the default value, change it + if Config.getint('widgets', 'scroll_timeout') == 250: + Config.set('widgets', 'scroll_timeout', '55') + Config.setdefault('widgets', 'scroll_stoptime', '300') + Config.setdefault('widgets', 'scroll_moves', '5') + #elif version == 1: # # add here the command for upgrading from configuration 0 to 1 # diff --git a/kivy/core/gl/__init__.py b/kivy/core/gl/__init__.py index 502b88c31..cc56d1660 100644 --- a/kivy/core/gl/__init__.py +++ b/kivy/core/gl/__init__.py @@ -8,10 +8,16 @@ core provider can select an OpenGL ES or a 'classic' desktop OpenGL library. ''' from os import environ +from sys import platform, exit MIN_REQUIRED_GL_VERSION = (2, 0) +def msgbox(message): + if platform == 'win32': + import win32ui + win32ui.MessageBox(message, 'Kivy Fatal Error') + exit(1) if 'KIVY_DOC' not in environ: @@ -22,16 +28,32 @@ if 'KIVY_DOC' not in environ: def init_gl(): gl_init_symbols() - gl_init_resources() print_gl_version() + gl_init_resources() def print_gl_version(): version = str(glGetString(GL_VERSION)) + vendor = str(glGetString(GL_VENDOR)) + renderer = str(glGetString(GL_RENDERER)) Logger.info('GL: OpenGL version <%s>' % version) - Logger.info('GL: OpenGL vendor <%s>' % str( - glGetString(GL_VENDOR))) - Logger.info('GL: OpenGL renderer <%s>' % str( - glGetString(GL_RENDERER))) + Logger.info('GL: OpenGL vendor <%s>' % vendor) + Logger.info('GL: OpenGL renderer <%s>' % renderer) + + # Let the user know if his graphics hardware/drivers are too old + major, minor = gl_get_version() + Logger.info('GL: OpenGL parsed version: %d, %d' % (major, minor)) + if (major, minor) < MIN_REQUIRED_GL_VERSION: + msg = ( + 'GL: Minimum required OpenGL version (2.0) NOT found!\n\n' + 'OpenGL version detected: {0}.{1}\n\n' + 'Version: {2}\nVendor: {3}\nRenderer: {4}\n\n' + 'Try upgrading your graphics drivers and/or your ' + 'graphics hardware in case of problems.\n\n' + 'The application will leave now.').format( + major, minor, version, vendor, renderer) + Logger.critical(msg) + msgbox(msg) + Logger.info('GL: Shading version <%s>' % str( glGetString(GL_SHADING_LANGUAGE_VERSION))) Logger.info('GL: Texture max size <%s>' % str( @@ -39,15 +61,6 @@ if 'KIVY_DOC' not in environ: Logger.info('GL: Texture max units <%s>' % str( glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS)[0])) - # Let the user know if his graphics hardware/drivers are too old - major, minor = gl_get_version() - Logger.info('GL: OpenGL parsed version: %d, %d' % (major, minor)) - if (major, minor) < MIN_REQUIRED_GL_VERSION: - msg = 'GL: Minimum required OpenGL version (2.0) NOT found! ' \ - 'Try upgrading your graphics drivers and/or your ' \ - 'graphics hardware in case of problems.' - Logger.critical(msg) - # To be able to use our GL provider, we must have a window # Automaticly import window auto to ensure the default window creation import kivy.core.window diff --git a/kivy/core/image/__init__.py b/kivy/core/image/__init__.py index a61b1ce1f..19dcc3e19 100644 --- a/kivy/core/image/__init__.py +++ b/kivy/core/image/__init__.py @@ -41,11 +41,11 @@ class ImageData(object): The container will always have at least the mipmap level 0. ''' - __slots__ = ('fmt', 'mipmaps', 'source') + __slots__ = ('fmt', 'mipmaps', 'source', 'flip_vertical') _supported_fmts = ('rgb', 'rgba', 'bgr', 'bgra', 's3tc_dxt1', 's3tc_dxt3', 's3tc_dxt5') - def __init__(self, width, height, fmt, data, source=None): + def __init__(self, width, height, fmt, data, source=None, flip_vertical=True): assert fmt in ImageData._supported_fmts #: Decoded image format, one of a available texture format @@ -58,6 +58,9 @@ class ImageData(object): #: Image source, if available self.source = source + #: Indicate if the texture will need to be vertically flipped + self.flip_vertical = flip_vertical + def release_data(self): mm = self.mipmaps for item in mm.itervalues(): @@ -165,10 +168,13 @@ class ImageLoaderBase(object): # if not create it and append to the cache if texture is None: + imagedata = self._data[count] texture = Texture.create_from_data( - self._data[count], mipmap=self._mipmap) + imagedata, mipmap=self._mipmap) if not self._nocache: Cache.append('kv.texture', uid, texture) + if imagedata.flip_vertical: + texture.flip_vertical() # set as our current texture self._textures.append(texture) diff --git a/kivy/core/image/img_dds.py b/kivy/core/image/img_dds.py index b444907ac..a6bca6518 100644 --- a/kivy/core/image/img_dds.py +++ b/kivy/core/image/img_dds.py @@ -24,7 +24,8 @@ class ImageLoaderDDS(ImageLoaderBase): self.filename = filename width, height = dds.size - im = ImageData(width, height, dds.dxt, dds.images[0], source=filename) + im = ImageData(width, height, dds.dxt, dds.images[0], source=filename, + flip_vertical=False) if len(dds.images) > 1: images = dds.images images_size = dds.images_size diff --git a/kivy/core/image/img_gif.py b/kivy/core/image/img_gif.py index 746519678..8c14cc687 100644 --- a/kivy/core/image/img_gif.py +++ b/kivy/core/image/img_gif.py @@ -133,7 +133,7 @@ class ImageLoaderGIF(ImageLoaderBase): i += 1 img_data_append(ImageData(ls_width, ls_height, - 'rgba', pixel_map.tostring())) + 'rgba', pixel_map.tostring(), flip_vertical=False)) if draw_method_replace: pixel_map = array('B', [0] * (ls_width * ls_height * 4)) diff --git a/kivy/core/image/img_pil.py b/kivy/core/image/img_pil.py index 6c18b1dc1..29dea90dc 100644 --- a/kivy/core/image/img_pil.py +++ b/kivy/core/image/img_pil.py @@ -54,8 +54,6 @@ class ImageLoaderPIL(ImageLoaderBase): raise _img_tmp = imc - # image are not in the good direction, flip ! - _img_tmp = _img_tmp.transpose(PILImage.FLIP_TOP_BOTTOM) return _img_tmp def _img_read(self, im): diff --git a/kivy/core/image/img_pygame.py b/kivy/core/image/img_pygame.py index 05361e52e..04cc70bfc 100644 --- a/kivy/core/image/img_pygame.py +++ b/kivy/core/image/img_pygame.py @@ -64,7 +64,7 @@ class ImageLoaderPygame(ImageLoaderBase): # update internals self.filename = filename - data = pygame.image.tostring(im, fmt.upper(), True) + data = pygame.image.tostring(im, fmt.upper()) return [ImageData(im.get_width(), im.get_height(), fmt, data, source=filename)] diff --git a/kivy/core/text/markup.py b/kivy/core/text/markup.py index fabdc922d..abd41384f 100644 --- a/kivy/core/text/markup.py +++ b/kivy/core/text/markup.py @@ -38,7 +38,7 @@ If you need to escape the markup from the current text, use __all__ = ('MarkupLabel', ) from kivy.graphics.texture import Texture -from kivy.utils import platform +from kivy.properties import dpi2px from kivy.parser import parse_color from kivy.logger import Logger import re @@ -138,9 +138,14 @@ class MarkupLabel(MarkupLabelBase): spop('italic') self.resolve_font_name() elif item[:6] == '[size=': + item = item[6:-1] try: - size = int(item[6:-1]) + if item[-2:] in ('px', 'pt', 'in', 'cm', 'mm', 'dp', 'sp'): + size = dpi2px(item[:-2], item[-2:]) + else: + size = int(item) except ValueError: + raise size = options['font_size'] spush('font_size') options['font_size'] = size diff --git a/kivy/core/window/__init__.py b/kivy/core/window/__init__.py index 0fa45b4a2..0a06d2fd3 100755 --- a/kivy/core/window/__init__.py +++ b/kivy/core/window/__init__.py @@ -270,7 +270,7 @@ class WindowBase(EventDispatcher): return True else: return False - size = AliasProperty(_get_size, _set_size) + size = AliasProperty(_get_size, _set_size, bind=('_size', )) '''Get the rotated size of the window. If :data:`rotation` is set, then the size will change to reflect the rotation. ''' @@ -476,8 +476,8 @@ class WindowBase(EventDispatcher): self.create_window() # attach modules + listener event - Modules.register_window(self) EventLoop.set_window(self) + Modules.register_window(self) EventLoop.add_event_listener(self) # manage keyboard(s) @@ -539,6 +539,7 @@ class WindowBase(EventDispatcher): from kivy.graphics.context import get_context get_context().reload() Clock.schedule_once(lambda x: self.canvas.ask_update(), 0) + self.dispatch('on_resize', *self.system_size) # ensure the gl viewport is correct self.update_viewport() diff --git a/kivy/core/window/window_pygame.py b/kivy/core/window/window_pygame.py index eec2b2015..6db0ea61e 100644 --- a/kivy/core/window/window_pygame.py +++ b/kivy/core/window/window_pygame.py @@ -312,8 +312,6 @@ class WindowPygame(WindowBase): else: pass - # force deletion of window - pygame.display.quit() # # Pygame wrapper diff --git a/kivy/data/images/defaulttheme-0.png b/kivy/data/images/defaulttheme-0.png index 39a9df84f..67661dcc9 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 4c3c861d8..b53cf3fd0 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], "splitter_h": [228, 214, 32, 7], "filechooser_selected": [187, 393, 118, 118], "audio-volume-muted": [350, 344, 48, 48], "sliderv_background": [473, 404, 37, 41], "splitter_down": [498, 178, 7, 32], "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], "splitter": [497, 360, 7, 32], "splitter_down_h": [462, 263, 32, 7], "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], "splitter_h": [255, 327, 32, 7], "filechooser_selected": [189, 392, 118, 118], "audio-volume-muted": [452, 342, 48, 48], "sliderv_background": [252, 166, 37, 41], "splitter_down": [502, 358, 7, 32], "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": [240, 125, 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], "splitter_grip": [495, 484, 12, 26], "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], "splitter": [495, 450, 7, 32], "splitter_down_h": [456, 402, 32, 7], "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], "splitter_grip_h": [428, 397, 26, 12], "textinput": [118, 212, 111, 111]}} \ No newline at end of file diff --git a/kivy/data/style.kv b/kivy/data/style.kv index 5c5edd273..340842c67 100644 --- a/kivy/data/style.kv +++ b/kivy/data/style.kv @@ -3,7 +3,7 @@