diff --git a/Makefile b/Makefile
index 7e46594d3..85284cedb 100644
--- a/Makefile
+++ b/Makefile
@@ -70,6 +70,7 @@ clean:
-rm .coverage
-rm .noseids
-rm -rf kivy/tests/build
+ -find kivy -iname '*.so' -exec rm {} \;
-find kivy -iname '*.pyc' -exec rm {} \;
-find kivy -iname '*.pyo' -exec rm {} \;
diff --git a/doc/Makefile b/doc/Makefile
index d61b2d8bd..571a0b883 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -2,7 +2,9 @@
#
# You can set these variables from the command line.
-SPHINXOPTS =
+PYTHON = python
+SPHINXOPTS = -W
+#SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
@@ -28,12 +30,14 @@ clean:
html:
mkdir -p build/html build/doctrees
+ $(PYTHON) autobuild.py
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
@echo
@echo "Build finished. The HTML pages are in build/html."
gettext:
mkdir -p build/html build/doctrees_gettext
+ $(PYTHON) autobuild.py
$(SPHINXBUILD) -b gettext $(ALLSPHINXOPTSGT) build/gettext
@echo
@echo "Build finished. The Gettext pages are in build/gettext."
@@ -41,6 +45,7 @@ gettext:
pickle:
mkdir -p build/pickle build/doctrees
+ $(PYTHON) autobuild.py
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
@echo
@echo "Build finished; now you can process the pickle files or run"
@@ -51,6 +56,7 @@ web: pickle
htmlhelp:
mkdir -p build/htmlhelp build/doctrees
+ $(PYTHON) autobuild.py
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
@@ -58,6 +64,7 @@ htmlhelp:
latex:
mkdir -p build/latex build/doctrees
+ $(PYTHON) autobuild.py
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
@echo
@echo "Build finished; the LaTeX files are in build/latex."
@@ -66,12 +73,14 @@ latex:
changes:
mkdir -p build/changes build/doctrees
+ $(PYTHON) autobuild.py
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
@echo
@echo "The overview file is in build/changes."
linkcheck:
mkdir -p build/linkcheck build/doctrees
+ $(PYTHON) autobuild.py
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
diff --git a/doc/README b/doc/README
index 04b4ecda3..5af6ddb43 100644
--- a/doc/README
+++ b/doc/README
@@ -1,9 +1,9 @@
Kivy - Documentation
====================
-You can access to the API documentation on web :
+You can access the API documentation on web :
* last released version : http://kivy.org/docs/api
- * trunk version, nightly updated : http://kivy.org/docs/api-trunk/
+ * trunk version, updated nightly : http://kivy.org/docs/api-trunk/
How to build the documentation
@@ -13,10 +13,7 @@ You need to install :
* Python Sphinx (apt-get install python-sphinx)
* Latest kivy
-Run autobuild.py :
- python autobuild.py
-
-And generate documentation
+Generate documentation using make::
make html
Documentation will be accessible in build/html/
diff --git a/doc/autobuild.py b/doc/autobuild.py
index 06dfa6cb8..a42c2eadb 100644
--- a/doc/autobuild.py
+++ b/doc/autobuild.py
@@ -61,8 +61,13 @@ examples_framework_dir = os.path.join(base_dir, '..', 'examples', 'framework')
def writefile(filename, data):
global dest_dir
- print 'write', filename
+ # avoid to rewrite the file if the content didn't change
f = os.path.join(dest_dir, filename)
+ print 'write', filename
+ if os.path.exists(f):
+ with open(f) as fd:
+ if fd.read() == data:
+ return
h = open(f, 'w')
h.write(data)
h.close()
diff --git a/doc/sources/.static/default.css b/doc/sources/.static/default.css
index ab16bab4c..a3b566cad 100644
--- a/doc/sources/.static/default.css
+++ b/doc/sources/.static/default.css
@@ -708,3 +708,22 @@ dl.api-level dt {
float: left;
margin-right: 22px;
}
+
+/** Fix list within dt
+ */
+table.field-list dt {
+ margin: 0px;
+ background-color: #eeeeee;
+}
+
+table.field-list dl {
+ margin-top: 0px;
+}
+table.field-list td.field-body {
+ padding-top: 0px;
+}
+table.field-list th.field-name {
+ min-width: 100px;
+}
+
+
diff --git a/doc/sources/conf.py b/doc/sources/conf.py
index b060b1da8..d4608cae1 100644
--- a/doc/sources/conf.py
+++ b/doc/sources/conf.py
@@ -62,12 +62,8 @@ release = kivy.__version__
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
-# List of documents that shouldn't be included in the build.
-#unused_docs = []
-
-# List of directories, relative to source directories, that shouldn't be searched
-# for source files.
-#exclude_dirs = []
+# suppress exclusion warnings
+exclude_patterns = ['gettingstarted/*', 'guide/layouts.rst']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
diff --git a/doc/sources/contents.rst.inc b/doc/sources/contents.rst.inc
index abe45c875..51533c074 100644
--- a/doc/sources/contents.rst.inc
+++ b/doc/sources/contents.rst.inc
@@ -6,6 +6,7 @@ and why you'd want to use it.
It goes on with a discussion of the architecture and shows you how to create
stunning applications in short time using the framework.
+
.. toctree::
:maxdepth: 2
diff --git a/doc/sources/contribute.rst b/doc/sources/contribute.rst
index b0f5709cf..39f2ab6d3 100644
--- a/doc/sources/contribute.rst
+++ b/doc/sources/contribute.rst
@@ -58,6 +58,30 @@ your contribution won't be forgotten or lost. Also, your name will always be
associated with the change you made, which basically means eternal fame in our
code history (you can opt-out if you don't want that).
+
+Coding style
+~~~~~~~~~~~~
+
+- If you didn't do it yet, read the
+ `PEP8 `_ about coding style in python.
+
+- Activate pep8 check on git commit like this::
+
+ make hook
+
+This will pass the code added to git staging zone (about to be committed)
+thought a pep8 checker program when you do a commit, and check that you didn't
+introduce pep8 errors, if so, the commit will be rejected, correct the errors,
+and try again.
+
+Performances
+~~~~~~~~~~~~
+
+- take care of performance issues, read
+ `Python performance tips `_
+- cpu intensive parts of Kivy are written in cython, if you are doing a lot of
+ computation, consider using it too.
+
Git & GitHub
~~~~~~~~~~~~
@@ -91,9 +115,12 @@ but the fork you create with the following steps:
#. Compile and set up PYTHONPATH or install (see :ref:`dev-install`).
#. Install our pre-commit hook that ensures your code doesn't violate our
styleguide by executing `make hook` from the root directory of your
- clone. This will run our styleguide check whenever you do a commit,
- and if there are violations in the parts that you changed, your commit
+ clone. This will run our styleguide check whenever you do a commit,
+ and if there are violations in the parts that you changed, your commit
will be aborted. Fix & retry.
+ #. Add kivy repo as a remote source::
+
+ git remote add kivy https://github.com/kivy/kivy.git
Now, whenever you want to create a patch, you follow the following steps:
@@ -102,7 +129,10 @@ Now, whenever you want to create a patch, you follow the following steps:
#. Create a new, appropriately named branch in your local repository for
that specific feature or bugfix.
(Keeping a new branch per feature makes sure we can easily pull in your
- changes without pulling any other stuff that is not supposed to be pulled.)
+ changes without pulling any other stuff that is not supposed to be pulled.)::
+
+ git checkout -b new_feature
+
#. Modify the code to do what you want (e.g., fix it).
#. Test the code. Try to do this even for small fixes. You never know
whether you have introduced some weird bug without testing.
@@ -114,8 +144,14 @@ Now, whenever you want to create a patch, you follow the following steps:
not familiar with the matter get a good idea of what you changed.
#. Once you are satisfied with your changes, merge with our upstream
repository. We can pull your stuff, but since you know best what you
- changed, you should do the merge.
- #. Push to your remote repository on GitHub.
+ changed, you should do the merge::
+
+ git pull kivy master
+
+ #. 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. (This is why we forked
initially. Your repository is linked against ours.)
@@ -145,6 +181,75 @@ hassle just to correct a single typo. For more complex contributions, please
consider following the suggested workflow though.
+Docstrings
+~~~~~~~~~~
+
+Every module/class/method/function need a docstring, use the following keyword
+when relevant
+
+- ``.. versionadded::`` to mark the version the feature was added.
+- ``.. versionchanged::`` to mark the version behaviour of the feature was
+ changed.
+- ``.. note::`` to add additional info about how to use the feature or related
+ feature.
+- ``.. warning::`` to indicate a potential issue the user might run into using
+ the feature.
+
+Examples::
+
+ def my_new_feature(self, arg):
+ """
+ New feature is awesome
+
+ .. versionadded:: 1.1.4
+
+ .. note:: This new feature will likely blow your mind
+
+ .. warning:: Please take a seat before trying this feature
+ """
+
+Will result in:
+
+ def my_new_feature(self, arg):
+ """
+ New feature is awesome
+
+ .. versionadded:: 1.1.4
+
+ .. note:: This new feature will likely blow your mind
+
+ .. warning:: Please take a seat before trying this feature
+
+ """
+
+
+When refering to other parts of the api use:
+
+- ``:mod:`~kivy.module``` to refer to a module
+- ``:class:`~kivy.module.Class``` to refer to a class
+- ``:meth:`~kivy.module.Class.method``` to refer to a method
+- ``:doc:`api-kivy.module``` to refer to the documentation of a module (same
+ for a class and a method)
+
+Obviously replacing `module` `class` and `method` with their real name, and
+using using '.' to separate modules refering to imbricated modules, e.g::
+
+ :mod:`~kivy.uix.floatlayout`
+ :class:`~kivy.uix.floatlayout.FloatLayout`
+ :meth:`~kivy.core.window.WindowBase.toggle_fullscreen`
+ :doc:`/api-kivy.core.window`
+
+Will result in:
+
+ :mod:`~kivy.uix.floatlayout`
+ :class:`~kivy.uix.floatlayout.FloatLayout`
+ :meth:`~kivy.core.window.WindowBase.toggle_fullscreen`
+ :doc:`/api-kivy.core.window`
+
+`:doc:` and `:mod:` are essentially the same, except for an anchor in the url,
+this makes `:doc:` prefered, for the cleaner url.
+
+
Unit tests contributions
------------------------
diff --git a/doc/sources/gettingstarted/diving.rst b/doc/sources/gettingstarted/diving.rst
index 31fe801ab..6f8e933d4 100644
--- a/doc/sources/gettingstarted/diving.rst
+++ b/doc/sources/gettingstarted/diving.rst
@@ -6,23 +6,23 @@ Diving in
To further get into kivy take a look at :doc:`/index`
-Kivy comes with a set of :doc:`examples` in the ``examples`` directory.
+Kivy comes with a set of :doc:`examples` in the ``kivy_installation/examples`` directory.
You should try modifying/improving/adapting them to your needs.
Browse the `snippet wiki `_ . You can even add your snippet in the user snippets section.
-Understand the basics about `kivy graphics `_
+Understand the basics about :mod:`kivy graphics `
-Take a look at the built-in `widgets `_
+Take a look at the built-in :mod:`widgets `
-Follow the `programming guide `_ to get even more familiar with kivy.
+Follow the :doc:`/guide-index` to get even more familiar with kivy.
-See how to use different `Modules `_ like the Inspector for live inspection in the modules section.
+See how to use different :mod:`Modules ` like the Inspector for live inspection in the modules section.
-Learn how to handle custom `Input `_
+Learn how to handle custom :mod:`Input `
-See how kivy has been extended with `extensions `_ support.
+See how kivy has been extended with :mod:`extensions ` support.
-Familiarize yourself with the `kivy framework `_
+Familiarize yourself with the :mod:`kivy framework `
Kivy is open source so you can **contribute** , take a look at :doc:`/contribute` section to see the guidelines.
diff --git a/doc/sources/gettingstarted/drawing.rst b/doc/sources/gettingstarted/drawing.rst
index f19ff38e4..9e5e7ead0 100644
--- a/doc/sources/gettingstarted/drawing.rst
+++ b/doc/sources/gettingstarted/drawing.rst
@@ -17,4 +17,4 @@ In both cases the canvas of the MyWidget is re-drawn whenever the ``position`` o
You can use **canvas.before** or **canvas.after** . This allows you to separate your instructions based on when you want them to happen.
-For an in-depth look at how Kivy's graphics are handled, look `here. `_
+For an in-depth look at how Kivy's graphics are handled, look :mod:`here. `
diff --git a/doc/sources/gettingstarted/events.rst b/doc/sources/gettingstarted/events.rst
index 01590b049..3cb7f8cff 100644
--- a/doc/sources/gettingstarted/events.rst
+++ b/doc/sources/gettingstarted/events.rst
@@ -57,7 +57,7 @@ Going further
Another thing to note is that if you override an event, you become responsible
for implementing all its behaviour previously handled by the base class. The
-easiest way to do this is to call `super()`: ::
+easiest way to do this is to call `super()`::
def on_touch_down(self, touch):
if super(OurClassName, self).on_touch_down(touch):
diff --git a/doc/sources/gettingstarted/examples.rst b/doc/sources/gettingstarted/examples.rst
index 1500a0550..51796b08b 100644
--- a/doc/sources/gettingstarted/examples.rst
+++ b/doc/sources/gettingstarted/examples.rst
@@ -58,7 +58,7 @@ Examples
.. |tws_dir| replace:: ./examples/frameworks/twisted
.. |tws_file| replace:: echo_client_app.py
.. |tws_file2| replace:: echo_server_app.py
-.. |tws_desc| replace:: A clent and server app using `twisted-inside-kivy `__
+.. |tws_desc| replace:: A clent and server app using :doc:`Twisted inside Kivy `
.. |gst_dir| replace:: ./examples/gestures
.. |gst_file| replace:: gesture_board.py
@@ -66,7 +66,7 @@ Examples
.. |kv_dir| replace:: ./examples/guide/designwithkv
.. |kv_file| replace:: main.py
-.. |kv_desc| replace:: Programming Guide examples on how to `design with kv lang `__.
+.. |kv_desc| replace:: Programming Guide examples on how to :doc:`design with kv lang `
.. |fwd_dir| replace:: ./examples/guide/firstwidget
.. |fwd_file| replace:: 1_skeleton.py
@@ -75,11 +75,11 @@ Examples
.. |fwd_file4| replace:: 4_draw_line.py
.. |fwd_file5| replace:: 5_random_colors.py
.. |fwd_file6| replace:: 6_button.py
-.. |fwd_desc| replace:: Programming Guide examples `Your first widget `__ .
+.. |fwd_desc| replace:: Programming Guide examples :doc:`Your first widget `
.. |qst_dir| replace:: ./examples/guide/quickstart
.. |qst_file| replace:: main.py
-.. |qst_desc| replace:: Programming Guide `guide/quickstart `__ example.
+.. |qst_desc| replace:: Programming Guide :doc:`guide/quickstart ` example.
.. |kin_dir| replace:: ./examples/kinect
.. |kin_file| replace:: main.py
diff --git a/doc/sources/gettingstarted/framework.rst b/doc/sources/gettingstarted/framework.rst
index 7a1964b9b..c3d43c65b 100644
--- a/doc/sources/gettingstarted/framework.rst
+++ b/doc/sources/gettingstarted/framework.rst
@@ -15,12 +15,12 @@ Non-widget stuff
.. |atlas_text| replace:: :class:`Atlas ` is a class for managing texture maps, i.e. packing multiple textures into one image. Using it allows you to reduce the number of images to load and speed up the application start.
-.. |clock_text| replace:: :class:`Clock ` provides you with a convenient way to do jobs at set time intervals and is preferred over *sleep()* which would block the kivy Event Loop. These intervals can be set relative to the OpenGL Drawing instructions, :ref:`before ` or :ref:`after ` frame. Clock also provides you with a way to create :ref:`triggered events ` that are grouped together and only called once before the next frame.
+.. |clock_text| replace:: :class:`Clock ` provides you with a convenient way to do jobs at set time intervals and is preferred over *sleep()* which would block the kivy Event Loop. These intervals can be set relative to the OpenGL Drawing instructions, :ref:`before ` or :ref:`after ` frame. Clock also provides you with a way to create :ref:`triggered events ` that are grouped together and only called once before the next frame.
-.. |sched_once| replace:: `Clock.schedule_once `__
-.. |sched_intrvl| replace:: `Clock.schedule_interval `__
-.. |unsched| replace:: `Clock.unschedule `__
-.. |trigger| replace:: `Clock.create_trigger `__
+.. |sched_once| replace:: :meth:`~kivy.clock.ClockBase.schedule_once`
+.. |sched_intrvl| replace:: :meth:`~kivy.clock.ClockBase.schedule_interval`
+.. |unsched| replace:: :meth:`~kivy.clock.ClockBase.unschedule`
+.. |trigger| replace:: :meth:`~kivy.clock.ClockBase.create_trigger`
.. |urlreq| replace:: :class:`UrlRequest ` is useful to do asynchronous requests without blocking the event loop, and manage the result and progress with callbacks.
+------------------+------------------+
diff --git a/doc/sources/gettingstarted/layouts.rst b/doc/sources/gettingstarted/layouts.rst
index c6b3d6bc5..ff8fc11e5 100644
--- a/doc/sources/gettingstarted/layouts.rst
+++ b/doc/sources/gettingstarted/layouts.rst
@@ -5,11 +5,12 @@ Layouts
Arranging Your Widgets
-Layouts are used to arrange widgets in a perticular manner. ::
+Layouts are used to arrange widgets in a perticular manner::
AnchorLayout: widgets can be anchored to 'top', 'bottom', 'left', 'right', 'center'
BoxLayout: widgets are arranged in a box in either 'vertical' or 'horizontal' orientation
FloatLayout: Widgets are essentially unrestricted
+ RelativeLayout: Child widgets are positioned relative to the layout.
GridLayout: widgets are arranged in a grid defined by `rows` and `cols` properties
StackLayout: widgets are stacked in `lr-tb` (left to right then top to bottom) or `tb-lr` order
@@ -20,8 +21,9 @@ size_hint and pos_hint are used to calculate widget's size and position only if
However one can set these to None and provide direct values in screen coordinates.
For a detailed look at how you can arrange widgets using layouts look in
-`AnchorLayout `_
-`BoxLayout `_
-`FloatLayout `_
-`GridLayout `_
-`StackLayout `_
+:mod:`AnchorLayout `
+:mod:`BoxLayout `
+:mod:`FloatLayout `
+:mod:`GridLayout `
+:mod:`StackLayout `
+:mod:`RelativeLayout `
diff --git a/doc/sources/gettingstarted/properties.rst b/doc/sources/gettingstarted/properties.rst
index c76bd7fcc..7e2fc2cc0 100644
--- a/doc/sources/gettingstarted/properties.rst
+++ b/doc/sources/gettingstarted/properties.rst
@@ -33,15 +33,15 @@ provides an ``on_`` event that is called whenever the property's
state/value changes .
Kivy provides the following properties:
- `NumericProperty `_,
- `StringProperty `_,
- `ListProperty `_,
- `ObjectProperty `_,
- `BooleanProperty `_,
- `BoundedNumericProperty `_,
- `OptionProperty `_,
- `ReferenceListProperty `_,
- `AliasProperty `_,
- `DictProperty `_,
+ :mod:`~kivy.properties.NumericProperty`,
+ :mod:`~kivy.properties.StringProperty`,
+ :mod:`~kivy.properties.ListProperty`,
+ :mod:`~kivy.properties.ObjectProperty`,
+ :mod:`~kivy.properties.BooleanProperty`,
+ :mod:`~kivy.properties.BoundedNumericProperty`,
+ :mod:`~kivy.properties.OptionProperty`,
+ :mod:`~kivy.properties.ReferenceListProperty`,
+ :mod:`~kivy.properties.AliasProperty`,
+ :mod:`~kivy.properties.DictProperty`,
For an in-depth explaination, look at :doc:`/api-kivy.properties`
diff --git a/doc/sources/guide-index.rst b/doc/sources/guide-index.rst
index d0a71482b..d503d7d66 100644
--- a/doc/sources/guide-index.rst
+++ b/doc/sources/guide-index.rst
@@ -17,4 +17,3 @@ Programming Guide
guide/designwithkv
guide/other-frameworks
guide/packaging
-
diff --git a/doc/sources/guide/inputs.rst b/doc/sources/guide/inputs.rst
index a31ee4299..d36b238ca 100644
--- a/doc/sources/guide/inputs.rst
+++ b/doc/sources/guide/inputs.rst
@@ -2,7 +2,7 @@ Input management
================
Input architecture
------------------
+------------------
Kivy is able to handle most types of input: mouse, touchscreen, accelerometer,
gyroscope, etc. It handles the native multitouch protocols on the following
@@ -96,7 +96,7 @@ profile exists::
if 'angle' in touch.profile:
print 'The touch angle is', touch.a
-You can find a list of available profiles in the :doc:`api-kivy.input.motionevent`
+You can find a list of available profiles in the :mod:`api-kivy.input.motionevent`
documentation.
Touch events
diff --git a/doc/sources/guide/layouts.rst b/doc/sources/guide/layouts.rst
index d000dc1a5..987b37137 100644
--- a/doc/sources/guide/layouts.rst
+++ b/doc/sources/guide/layouts.rst
@@ -25,8 +25,9 @@ BoxLayout:
.. image:: ../images/boxlayout.gif
GridLayout:
- Arrange widgets in a grid, you must specifiy at least one dimension of the
- grid, so kivy can comput size of the elements and how to arrange them
+ Arrange widgets in a grid. You must specifiy at least one dimension of the
+ grid so kivy can compute the size of the elements and how to arrange them.
+
`pos_hint` not honored
.. image:: ../images/gridlayout.gif
diff --git a/doc/sources/guide/packaging-android.rst b/doc/sources/guide/packaging-android.rst
index 56da5295e..29553390b 100644
--- a/doc/sources/guide/packaging-android.rst
+++ b/doc/sources/guide/packaging-android.rst
@@ -4,13 +4,9 @@ Create a package for Android
============================
.. versionchanged:: 1.1.0
-
- Starting from 1.1.0, we are not providing anymore a Kivy-XXX-android.zip.
- We are using `python-for-android `_
- project.
-
- The whole packaging is explained at
- `http://python-for-android.readthedocs.org/en/latest/index.html`_
+ Kivy-XXX-android.zip is not provided anymore. We are using
+ `python-for-android `_
+ (`doc `_)
Packaging your application into APK
-----------------------------------
diff --git a/doc/sources/guide/packaging-ios.rst b/doc/sources/guide/packaging-ios.rst
index 2bcb4600f..fbb174c52 100644
--- a/doc/sources/guide/packaging-ios.rst
+++ b/doc/sources/guide/packaging-ios.rst
@@ -102,6 +102,17 @@ development):
FAQ
---
+Application quit abnormally!
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default, all the print on the console and files are avoided. If you have an
+issue when running your application, you can activate the log by commenting the
+line in the main.m::
+
+ putenv("KIVY_NO_CONSOLELOG=1");
+
+Then, you should see all the Kivy log on the Xcode console.
+
How Apple can accept a python app ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -112,5 +123,7 @@ dynamically loaded.
Did you already submit a Kivy application to the App store ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Yes, check `Defletouch on iTunes `_
+Yes, check:
+- `Defletouch on iTunes `_,
+- `ProcessCraft on iTunes `_
diff --git a/doc/sources/guide/packaging.rst b/doc/sources/guide/packaging.rst
index c7e6f6ffe..e8bdaa8e9 100644
--- a/doc/sources/guide/packaging.rst
+++ b/doc/sources/guide/packaging.rst
@@ -11,3 +11,4 @@ Packaging your application
packaging-android
packaging-ios
+ android
diff --git a/doc/sources/guide/widgettree.rst b/doc/sources/guide/widgettree.rst
index 1860b6088..61b39f077 100644
--- a/doc/sources/guide/widgettree.rst
+++ b/doc/sources/guide/widgettree.rst
@@ -54,7 +54,7 @@ Traversing the tree
-------------------
The widget class has a :data:`~kivy.uix.widget.Widget.children` list property
-that contains all the children. You can easily traverse the tree by doing ::
+that contains all the children. You can easily traverse the tree by doing::
root = BoxLayout()
# ... add widgets to root ...
diff --git a/doc/sources/images/spinner.jpg b/doc/sources/images/spinner.jpg
new file mode 100644
index 000000000..e1c309a15
Binary files /dev/null and b/doc/sources/images/spinner.jpg differ
diff --git a/doc/sources/index.rst b/doc/sources/index.rst
index fcbd1f03c..9c0c8bd6e 100644
--- a/doc/sources/index.rst
+++ b/doc/sources/index.rst
@@ -1,5 +1,6 @@
:orphan:
+
Welcome to Kivy
===============
@@ -19,6 +20,28 @@ concern isn't addressed in the documentation, feel free to :ref:`contact`.
.. include:: contents.rst.inc
+.. Suppress warnings about documents not being included in toc, but don't
+ actually show them.
+.. toctree::
+ :hidden:
+
+ api-index
+ api-kivy.lib.osc.OSC
+ api-kivy.lib.osc.oscAPI
+.. gettingstarted/diving
+ gettingstarted/drawing
+ gettingstarted/events
+ gettingstarted/examples
+ gettingstarted/first_app
+ gettingstarted/framework
+ gettingstarted/installation
+ gettingstarted/intro
+ gettingstarted/layouts
+ gettingstarted/packaging
+ gettingstarted/properties
+ gettingstarted/rules
+ guide/layouts
+
Appendix
========
diff --git a/doc/sources/installation/installation.rst b/doc/sources/installation/installation.rst
index e93954ced..16520c864 100644
--- a/doc/sources/installation/installation.rst
+++ b/doc/sources/installation/installation.rst
@@ -141,7 +141,7 @@ Uninstalling Kivy
If you are mixing multiple Kivy installations, you might be confused about where each Kivy version is
located. Please note that you might need to follow these steps multiple times, if you have multiple kivy versions
installed in the Python library path.
-To find your current installed version, you can use the command line: ::
+To find your current installed version, you can use the command line::
$ python -c 'import kivy; print kivy.__path__'
diff --git a/doc/sources/sphinxext/preprocess.py b/doc/sources/sphinxext/preprocess.py
index e983d4b69..b7d1d5b5a 100644
--- a/doc/sources/sphinxext/preprocess.py
+++ b/doc/sources/sphinxext/preprocess.py
@@ -63,7 +63,7 @@ def callback_docstring(app, what, name, obj, options, lines):
line = lines.pop(0)
# trick to realign the first line to the second one.
- # FIXME: fail if we finishing with ::
+ # FIXME: fail if we finishing with::
line_with_text = [x for x in lines if len(x.strip())]
if len(line_with_text) and line is not None and len(lines):
l = len(line_with_text[0]) - len(line_with_text[0].lstrip())
diff --git a/examples/animation/animate.py b/examples/animation/animate.py
index e63b603d6..0b642b036 100644
--- a/examples/animation/animate.py
+++ b/examples/animation/animate.py
@@ -32,6 +32,5 @@ class TestApp(App):
button.bind(on_press=self.animate)
return button
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
TestApp().run()
-
diff --git a/examples/audio/main.py b/examples/audio/main.py
index fef40c42a..9afcdc919 100644
--- a/examples/audio/main.py
+++ b/examples/audio/main.py
@@ -57,5 +57,5 @@ class AudioApp(App):
return root
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
AudioApp().run()
diff --git a/examples/demo/pictures/main.py b/examples/demo/pictures/main.py
index ea0062347..cf05b9423 100644
--- a/examples/demo/pictures/main.py
+++ b/examples/demo/pictures/main.py
@@ -53,6 +53,6 @@ class PicturesApp(App):
return True
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
PicturesApp().run()
diff --git a/examples/demo/shadereditor/main.py b/examples/demo/shadereditor/main.py
index c061fdfd1..099134de5 100644
--- a/examples/demo/shadereditor/main.py
+++ b/examples/demo/shadereditor/main.py
@@ -108,5 +108,5 @@ class ShaderEditorApp(App):
def build(self):
return ShaderEditor()
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
ShaderEditorApp().run()
diff --git a/examples/demo/showcase/main.py b/examples/demo/showcase/main.py
index e31528a1b..7c1923beb 100644
--- a/examples/demo/showcase/main.py
+++ b/examples/demo/showcase/main.py
@@ -277,5 +277,5 @@ class ShowcaseApp(App):
tv.add_node(TreeViewLabel(text='Subitem %d' % x), n)
return tv
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
ShowcaseApp().run()
diff --git a/examples/demo/showcase/showcase.kv b/examples/demo/showcase/showcase.kv
index 386f198b6..729b03088 100644
--- a/examples/demo/showcase/showcase.kv
+++ b/examples/demo/showcase/showcase.kv
@@ -255,41 +255,48 @@
TextInput:
text: 'Monoline textinput'
size_hint_y: None
- height: 50
+ height: 30
TextInput:
text: 'This is a password'
size_hint_y: None
- height: 50
+ height: 30
password: True
TextInput:
text: 'Readonly textinput'
size_hint_y: None
- height: 50
+ height: 30
readonly: True
TextInput:
text: 'Multiline textinput\nSecond line'
+ HSeparator:
+ text: 'Spinner'
+
+ Spinner:
+ size_hint_y: None
+ height: 44
+ values: ('Work', 'Home', 'Other', 'Custom')
+ text: 'Home'
+
HSeparator:
text: 'Checkbox'
GridLayout:
rows: 1
- CheckBox
+ CheckBox:
+ active: True
CheckBox
GridLayout:
cols: 2
CheckBox:
group: 'group1'
+ active: True
CheckBox:
group: 'group1'
- CheckBox:
- group: 'group2'
- CheckBox:
- group: 'group2'
diff --git a/examples/demo/touchtracer/main.py b/examples/demo/touchtracer/main.py
index 0f68f76b4..76e601463 100644
--- a/examples/demo/touchtracer/main.py
+++ b/examples/demo/touchtracer/main.py
@@ -98,5 +98,5 @@ class TouchtracerApp(App):
def on_pause(self):
return True
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
TouchtracerApp().run()
diff --git a/examples/guide/designwithkv/main.py b/examples/guide/designwithkv/main.py
index 133b27837..08b220405 100644
--- a/examples/guide/designwithkv/main.py
+++ b/examples/guide/designwithkv/main.py
@@ -24,5 +24,5 @@ class ControllerApp(App):
def build(self):
return Controller(info='Hello world')
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
ControllerApp().run()
diff --git a/examples/guide/quickstart/main.py b/examples/guide/quickstart/main.py
index 4597bb275..481b8bccb 100644
--- a/examples/guide/quickstart/main.py
+++ b/examples/guide/quickstart/main.py
@@ -10,5 +10,5 @@ class MyApp(App):
def build(self):
return Button(text='Hello World')
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
MyApp().run()
diff --git a/examples/shader/shadertree.py b/examples/shader/shadertree.py
index c9d022d2c..eb379d417 100644
--- a/examples/shader/shadertree.py
+++ b/examples/shader/shadertree.py
@@ -201,5 +201,5 @@ class ShaderTreeApp(App):
root.add_widget(btn)
return root
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
ShaderTreeApp().run()
diff --git a/examples/tutorials/pong/main.py b/examples/tutorials/pong/main.py
index 349297312..677319376 100644
--- a/examples/tutorials/pong/main.py
+++ b/examples/tutorials/pong/main.py
@@ -80,5 +80,5 @@ class PongApp(App):
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
PongApp().run()
diff --git a/examples/tutorials/pong/steps/step1/main.py b/examples/tutorials/pong/steps/step1/main.py
index 60eb7def8..713280323 100644
--- a/examples/tutorials/pong/steps/step1/main.py
+++ b/examples/tutorials/pong/steps/step1/main.py
@@ -9,5 +9,5 @@ class PongApp(App):
def build(self):
return PongGame()
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
PongApp().run()
diff --git a/examples/tutorials/pong/steps/step2/main.py b/examples/tutorials/pong/steps/step2/main.py
index 60eb7def8..713280323 100644
--- a/examples/tutorials/pong/steps/step2/main.py
+++ b/examples/tutorials/pong/steps/step2/main.py
@@ -9,5 +9,5 @@ class PongApp(App):
def build(self):
return PongGame()
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
PongApp().run()
diff --git a/examples/tutorials/pong/steps/step3/main.py b/examples/tutorials/pong/steps/step3/main.py
index 283139464..e731e21e8 100644
--- a/examples/tutorials/pong/steps/step3/main.py
+++ b/examples/tutorials/pong/steps/step3/main.py
@@ -24,5 +24,5 @@ class PongApp(App):
Factory.register("PongBall", PongBall)
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
PongApp().run()
diff --git a/examples/tutorials/pong/steps/step4/main.py b/examples/tutorials/pong/steps/step4/main.py
index 09c7d2b42..1f4182615 100644
--- a/examples/tutorials/pong/steps/step4/main.py
+++ b/examples/tutorials/pong/steps/step4/main.py
@@ -44,5 +44,5 @@ class PongApp(App):
Factory.register("PongBall", PongBall)
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
PongApp().run()
diff --git a/examples/tutorials/pong/steps/step5/main.py b/examples/tutorials/pong/steps/step5/main.py
index 6d1a1fc79..bb4cafd53 100644
--- a/examples/tutorials/pong/steps/step5/main.py
+++ b/examples/tutorials/pong/steps/step5/main.py
@@ -79,5 +79,5 @@ class PongApp(App):
-if __name__ in ('__android__', '__main__'):
+if __name__ == '__main__':
PongApp().run()
diff --git a/examples/widgets/asyncimage.py b/examples/widgets/asyncimage.py
index def28d690..24bf2d4a3 100644
--- a/examples/widgets/asyncimage.py
+++ b/examples/widgets/asyncimage.py
@@ -29,5 +29,5 @@ class TestAsyncApp(App):
return CenteredAsyncImage(
source='http://icanhascheezburger.files.wordpress.com/2009/12/funny-pictures-cat-is-expecting-you.jpg')
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
TestAsyncApp().run()
diff --git a/examples/widgets/bubble_test.py b/examples/widgets/bubble_test.py
index b25401f0d..83a53f87a 100644
--- a/examples/widgets/bubble_test.py
+++ b/examples/widgets/bubble_test.py
@@ -56,5 +56,5 @@ class TestBubbleApp(App):
def build(self):
return BubbleShowcase()
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
TestBubbleApp().run()
diff --git a/examples/widgets/image_mipmap.py b/examples/widgets/image_mipmap.py
index 32c625243..996167251 100644
--- a/examples/widgets/image_mipmap.py
+++ b/examples/widgets/image_mipmap.py
@@ -25,5 +25,5 @@ class LabelMipmapTest(App):
s.add_widget(l2)
return s
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
LabelMipmapTest().run()
diff --git a/examples/widgets/label_mipmap.py b/examples/widgets/label_mipmap.py
index 365cfb065..57590e94e 100644
--- a/examples/widgets/label_mipmap.py
+++ b/examples/widgets/label_mipmap.py
@@ -22,5 +22,5 @@ class LabelMipmapTest(App):
s.add_widget(l2)
return s
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
LabelMipmapTest().run()
diff --git a/examples/widgets/sequenced_images/main.py b/examples/widgets/sequenced_images/main.py
index 7db777b22..f2bc045d7 100644
--- a/examples/widgets/sequenced_images/main.py
+++ b/examples/widgets/sequenced_images/main.py
@@ -143,5 +143,5 @@ class mainApp(App):
return upl
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
mainApp().run()
diff --git a/examples/widgets/spinner.py b/examples/widgets/spinner.py
new file mode 100644
index 000000000..23a0e808a
--- /dev/null
+++ b/examples/widgets/spinner.py
@@ -0,0 +1,15 @@
+from kivy.uix.spinner import Spinner
+from kivy.base import runTouchApp
+
+spinner = Spinner(
+ text='Home',
+ values=('Home', 'Work', 'Other', 'Custom'),
+ size_hint=(None, None), size=(100, 44),
+ pos_hint={'center_x': .5, 'center_y': .5})
+
+def show_selected_value(spinner, text):
+ print 'The spinner', spinner, 'have text', text
+
+spinner.bind(text=show_selected_value)
+
+runTouchApp(spinner)
diff --git a/examples/widgets/tabbed_panel_showcase.py b/examples/widgets/tabbed_panel_showcase.py
index 343246e71..2bdff4ba9 100644
--- a/examples/widgets/tabbed_panel_showcase.py
+++ b/examples/widgets/tabbed_panel_showcase.py
@@ -330,5 +330,5 @@ class TestTabApp(App):
def build(self):
return TabShowcase()
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
TestTabApp().run()
diff --git a/examples/widgets/videoplayer.py b/examples/widgets/videoplayer.py
index 433b233c3..fa444213b 100644
--- a/examples/widgets/videoplayer.py
+++ b/examples/widgets/videoplayer.py
@@ -18,5 +18,5 @@ class VideoPlayerApp(App):
return VideoPlayer(source=filename, play=True)
-if __name__ in ('__main__', '__android__'):
+if __name__ == '__main__':
VideoPlayerApp().run()
diff --git a/kivy/__init__.py b/kivy/__init__.py
index 0e366b59d..f0787eb27 100644
--- a/kivy/__init__.py
+++ b/kivy/__init__.py
@@ -24,7 +24,7 @@ __all__ = (
'kivy_configure', 'kivy_register_post_configuration',
'kivy_options', 'kivy_base_dir',
'kivy_modules_dir', 'kivy_data_dir', 'kivy_shader_dir',
- 'kivy_icons_dir', 'kivy_home_dir',
+ 'kivy_icons_dir', 'kivy_home_dir', 'kivy_userexts_dir',
'kivy_config_fn', 'kivy_usermodules_dir',
)
@@ -145,7 +145,7 @@ def kivy_register_post_configuration(callback):
def kivy_usage():
- '''Kivy Usage: %s [OPTION...] ::
+ '''Kivy Usage: %s [OPTION...]::
-h, --help
Prints this help message.
@@ -226,20 +226,24 @@ kivy_shader_dir = join(kivy_data_dir, 'glsl')
#: Kivy icons config path (don't remove the last '')
kivy_icons_dir = join(kivy_data_dir, 'icons', '')
#: Kivy user-home storage directory
-kivy_home_dir = None
+kivy_home_dir = ''
#: Kivy configuration filename
-kivy_config_fn = None
+kivy_config_fn = ''
#: Kivy user modules directory
-kivy_usermodules_dir = None
+kivy_usermodules_dir = ''
+#: Kivy user extensions directory
+kivy_userexts_dir = ''
+
# Don't go further if we generate documentation
-if basename(sys.argv[0]) in ('sphinx-build', 'autobuild.py'):
+if any(name in sys.argv[0] for name in ('sphinx-build', 'autobuild.py')):
environ['KIVY_DOC'] = '1'
-if basename(sys.argv[0]) in ('sphinx-build', ):
+if 'sphinx-build' in sys.argv[0]:
environ['KIVY_DOC_INCLUDE'] = '1'
-if basename(sys.argv[0]) in ('nosetests', ) or 'nosetests' in sys.argv:
+if any('nosetests' in arg for arg in sys.argv):
environ['KIVY_UNITTEST'] = '1'
-if not 'KIVY_DOC_INCLUDE' in environ:
+
+if not environ.get('KIVY_DOC_INCLUDE'):
# Configuration management
user_home_dir = expanduser('~')
if platform() == 'android':
diff --git a/kivy/_event.pyx b/kivy/_event.pyx
index b6abf1dd6..8e1f57f2d 100644
--- a/kivy/_event.pyx
+++ b/kivy/_event.pyx
@@ -6,9 +6,7 @@ All objects that produce events in Kivy implement :class:`EventDispatcher`,
providing a consistent interface for registering and manipulating event
handlers.
-
.. versionchanged:: 1.0.9
-
Properties discovering and methods have been moved from
:class:`~kivy.uix.widget.Widget` to :class:`EventDispatcher`
diff --git a/kivy/animation.py b/kivy/animation.py
index ca50ce8bd..4270d0ab3 100644
--- a/kivy/animation.py
+++ b/kivy/animation.py
@@ -69,10 +69,6 @@ from kivy.clock import Clock
class Animation(EventDispatcher):
'''Create an animation definition that can be used to animate a Widget
- .. versionchanged:: 1.4.0
-
- Added s/step parameter.
-
:Parameters:
`duration` or `d`: float, default to 1.
Duration of the animation, in seconds
@@ -89,6 +85,10 @@ class Animation(EventDispatcher):
Fired when the animation is completed or stopped on a widget
`on_progress`: widget, progression
Fired when the progression of the animation is changing
+
+ .. versionchanged:: 1.4.0
+ Added s/step parameter.
+
'''
_instances = set()
@@ -136,7 +136,7 @@ class Animation(EventDispatcher):
'''Stop all animations that concern a specific widget / list of
properties.
- Example ::
+ Example::
anim = Animation(x=50)
anim.start(widget)
diff --git a/kivy/app.py b/kivy/app.py
index ae71a2c4a..ef387b91c 100644
--- a/kivy/app.py
+++ b/kivy/app.py
@@ -151,7 +151,7 @@ However, you might want to know when a config value has been changed by the
user, in order to adapt or reload your UI. You can overload the
:meth:`on_config_change` method::
- class TestApp(self):
+ class TestApp(App):
# ...
def on_config_change(self, config, section, key, value):
if config is self.config:
@@ -309,7 +309,7 @@ class App(EventDispatcher):
widget and added to the window.
:return: None or a root :class:`~kivy.uix.widget.Widget` instance is no
- self.root exist.
+ self.root exist.
'''
if not self.root:
return Widget()
@@ -370,6 +370,8 @@ class App(EventDispatcher):
'''
try:
default_kv_directory = dirname(getfile(self.__class__))
+ if default_kv_directory == '':
+ default_kv_directory = '.'
except TypeError:
# if it's a builtin module.. use the current dir.
default_kv_directory = '.'
@@ -408,7 +410,6 @@ class App(EventDispatcher):
'''.. versionadded:: 1.0.7
.. versionchanged:: 1.4.0
-
Customize the default path for iOS and Android platform. Add
defaultpath parameter for desktop computer (not applicatable for iOS
and Android.)
@@ -492,6 +493,8 @@ class App(EventDispatcher):
if self._app_directory is None:
try:
self._app_directory = dirname(getfile(self.__class__))
+ if self._app_directory == '':
+ self._app_directory = '.'
except TypeError:
# if it's a builtin module.. use the current dir.
self._app_directory = '.'
diff --git a/kivy/base.py b/kivy/base.py
index 9e0cd746f..d9aa1cf2f 100644
--- a/kivy/base.py
+++ b/kivy/base.py
@@ -1,3 +1,4 @@
+# pylint: disable=W0611
'''
Event loop management
=====================
@@ -25,7 +26,7 @@ EventLoop = None
class ExceptionHandler:
'''Base handler that catch exception in runTouchApp().
- You can derivate and use it like this ::
+ You can derivate and use it like this::
class E(ExceptionHandler):
def handle_exception(self, inst):
@@ -105,7 +106,7 @@ class EventLoopBase(EventDispatcher):
def ensure_window(self):
'''Ensure that we have an window
'''
- __import__('kivy.core.window')
+ import kivy.core.window
def set_window(self, window):
'''Set the window used for event loop
diff --git a/kivy/clock.py b/kivy/clock.py
index ae20832ec..b0fcd5e27 100644
--- a/kivy/clock.py
+++ b/kivy/clock.py
@@ -3,7 +3,7 @@ Clock object
============
The :class:`Clock` object allows you to schedule a function call in the
-future; once or on interval. ::
+future; once or on interval::
def my_callback(dt):
pass
@@ -59,6 +59,8 @@ module::
# and keep the instance of foo, until you don't need it anymore!
+.. _schedule-before-frame:
+
Schedule before frame
---------------------
@@ -85,6 +87,8 @@ If you need to increase the limit, set the :data:`max_iteration` property::
from kivy.clock import Clock
Clock.max_iteration = 20
+.. _triggered-events:
+
Triggered Events
----------------
@@ -93,7 +97,7 @@ Triggered Events
A triggered event is a way to defer a callback exactly like schedule_once(),
but with some added convenience. The callback will only be scheduled once per
frame, even if you call the trigger twice (or more). This is not the case
-with :func:`Clock.schedule_once` ::
+with :func:`Clock.schedule_once`::
# will run the callback twice before the next frame
Clock.schedule_once(my_callback)
@@ -104,7 +108,7 @@ with :func:`Clock.schedule_once` ::
t()
t()
-Before triggered events, you may have used this approach in a widget ::
+Before triggered events, you may have used this approach in a widget::
def trigger_callback(self, *largs):
Clock.unschedule(self.callback)
@@ -112,7 +116,7 @@ Before triggered events, you may have used this approach in a widget ::
As soon as you call `trigger_callback()`, it will correctly schedule the
callback once in the next frame. It is more convenient to create and bind to
-the triggered event than using :func:`Clock.schedule_once` in a function ::
+the triggered event than using :func:`Clock.schedule_once` in a function::
from kivy.clock import Clock
from kivy.uix.widget import Widget
@@ -353,7 +357,6 @@ class ClockBase(object):
'''Schedule an event in seconds.
.. versionchanged:: 1.0.5
-
If the timeout is -1, the callback will be called before the next
frame (at :func:`tick_draw`).
diff --git a/kivy/config.py b/kivy/config.py
index f5c046fa4..d1b20e5e9 100644
--- a/kivy/config.py
+++ b/kivy/config.py
@@ -23,24 +23,6 @@ Change the configuration and save it::
Available configuration tokens
------------------------------
-.. versionchanged:: 1.0.8
-
- * `scroll_timeout`, `scroll_distance` and `scroll_friction` have been added
- * `list_friction`, `list_trigger_distance` and `list_friction_bound` have
- been removed.
- * `keyboard_type` and `keyboard_layout` have been removed from widget
- * `keyboard_mode` and `keyboard_layout` have been added to kivy section
-
-.. versionchanged:: 1.1.0
-
- * tuio is not listening by default anymore.
- * windows icons are not copied to user directory anymore. You can still set
- a new window icon by using ``window_icon`` config setting.
-
-.. versionchanged:: 1.2.0
-
- * `resizable` has been added to graphics section
-
:kivy:
`log_level`: (debug, info, warning, error, critical)
@@ -164,6 +146,21 @@ Available configuration tokens
Anything after the = will be passed to the module as arguments.
Check the specific module's documentation for a list of accepted arguments.
+
+.. versionchanged:: 1.2.0
+ `resizable` has been added to graphics section
+
+.. versionchanged:: 1.1.0
+ tuio is not listening by default anymore. windows icons are not copied to
+ user directory anymore. You can still set a new window icon by using
+ ``window_icon`` config setting.
+
+.. versionchanged:: 1.0.8
+ `scroll_timeout`, `scroll_distance` and `scroll_friction` have been added.
+ `list_friction`, `list_trigger_distance` and `list_friction_bound` have been
+ removed. `keyboard_type` and `keyboard_layout` have been removed from
+ widget. `keyboard_mode` and `keyboard_layout` have been added to kivy
+ section.
'''
__all__ = ('Config', 'ConfigParser')
@@ -258,7 +255,7 @@ class ConfigParser(PythonConfigParser):
return True
-if not 'KIVY_DOC_INCLUDE' in environ:
+if not environ.get('KIVY_DOC_INCLUDE'):
#
# Read, analyse configuration file
diff --git a/kivy/core/audio/audio_pygame.py b/kivy/core/audio/audio_pygame.py
index 633e963c9..7d064ad6d 100644
--- a/kivy/core/audio/audio_pygame.py
+++ b/kivy/core/audio/audio_pygame.py
@@ -10,9 +10,9 @@ from kivy.core.audio import Sound, SoundLoader
try:
if platform() == 'android':
- mixer = __import__('android_mixer')
+ import android_mixer as mixer
else:
- mixer = __import__('pygame.mixer', fromlist='.')
+ from pygame import mixer
except:
raise
diff --git a/kivy/core/camera/camera_opencv.py b/kivy/core/camera/camera_opencv.py
index 2d6fcba7b..8eed6634d 100644
--- a/kivy/core/camera/camera_opencv.py
+++ b/kivy/core/camera/camera_opencv.py
@@ -14,10 +14,10 @@ from kivy.graphics.texture import Texture
from kivy.core.camera import CameraBase
try:
- cv = __import__('opencv', fromlist='.')
- hg = __import__('opencv.highgui', fromlist='.')
+ import opencv as cv
+ import opencv.highgui as hg
except ImportError:
- cv = __import__('cv')
+ import cv
class Hg(object):
'''
diff --git a/kivy/core/clipboard/__init__.py b/kivy/core/clipboard/__init__.py
index 3f80d1a13..a3bf72c7a 100644
--- a/kivy/core/clipboard/__init__.py
+++ b/kivy/core/clipboard/__init__.py
@@ -5,23 +5,23 @@ Clipboard
Core class for accessing to the Clipboard. If we are not able to access to the
system clipboard, a fake one will be used.
-Usage example (i have copied 'Hello World' somewhere else)::
+Usage example::
>>> from kivy.core.clipboard import Clipboard
>>> Clipboard.get_types()
['TIMESTAMP', 'TARGETS', 'MULTIPLE', 'SAVE_TARGETS', 'UTF8_STRING',
'COMPOUND_TEXT', 'TEXT', 'STRING', 'text/plain;charset=utf-8',
'text/plain']
- >>> Clipboard.get('TEXT')
- 'Hello World'
- >>> Clipboard.put('Great', 'UTF8_STRING')
- >>> Clipboard.get_types()
- ['UTF8_STRING']
- >>> Clipboard.get('UTF8_STRING')
- 'Great'
+ >>> Clipboard.get('TEXT')
+ 'Hello World'
+ >>> Clipboard.put('Great', 'UTF8_STRING')
+ >>> Clipboard.get_types()
+ ['UTF8_STRING']
+ >>> Clipboard.get('UTF8_STRING')
+ 'Great'
-Note that the main implementation rely on Pygame, and works great with
-text/string. Anything else might not work the same on all platform.
+.. note:: The main implementation rely on Pygame, and works great with
+ text/string. Anything else might not work the same on all platform.
'''
__all__ = ('ClipboardBase', 'Clipboard')
diff --git a/kivy/core/gl/__init__.py b/kivy/core/gl/__init__.py
index f90389ea0..502b88c31 100644
--- a/kivy/core/gl/__init__.py
+++ b/kivy/core/gl/__init__.py
@@ -1,3 +1,4 @@
+# pylint: disable=W0611
'''
OpenGL
======
@@ -49,4 +50,4 @@ if 'KIVY_DOC' not in environ:
# 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')
+ import kivy.core.window
diff --git a/kivy/core/image/__init__.py b/kivy/core/image/__init__.py
index d4a6de1f8..667f28bba 100644
--- a/kivy/core/image/__init__.py
+++ b/kivy/core/image/__init__.py
@@ -23,9 +23,9 @@ from kivy.resources import resource_find
from kivy.utils import platform
import zipfile
try:
- SIO = __import__('cStringIO')
+ import cStringIO as SIO
except ImportError:
- SIO = __import__('StringIO')
+ import StringIO as SIO
# late binding
@@ -137,11 +137,12 @@ class ImageLoaderBase(object):
'''Base to implement an image loader.'''
__slots__ = ('_texture', '_data', 'filename', 'keep_data',
- '_mipmap')
+ '_mipmap', '_nocache')
def __init__(self, filename, **kwargs):
self._mipmap = kwargs.get('mipmap', False)
self.keep_data = kwargs.get('keep_data', False)
+ self._nocache = kwargs.get('nocache', False)
self.filename = filename
self._data = self.load(filename)
self._textures = None
@@ -167,7 +168,8 @@ class ImageLoaderBase(object):
if texture is None:
texture = Texture.create_from_data(
self._data[count], mipmap=self._mipmap)
- Cache.append('kv.texture', uid, texture)
+ if not self._nocache:
+ Cache.append('kv.texture', uid, texture)
# set as our current texture
self._textures.append(texture)
@@ -364,7 +366,7 @@ class Image(EventDispatcher):
'''
copy_attributes = ('_size', '_filename', '_texture', '_image',
- '_mipmap')
+ '_mipmap', '_nocache')
def __init__(self, arg, **kwargs):
# this event should be fired on animation of sequenced img's
@@ -374,6 +376,7 @@ class Image(EventDispatcher):
self._mipmap = kwargs.get('mipmap', False)
self._keep_data = kwargs.get('keep_data', False)
+ self._nocache = kwargs.get('nocache', False)
self._size = [0, 0]
self._image = None
self._filename = None
@@ -579,7 +582,7 @@ class Image(EventDispatcher):
tmpfilename = self._filename
image = ImageLoader.load(
self._filename, keep_data=self._keep_data,
- mipmap=self._mipmap)
+ mipmap=self._mipmap, nocache=self._nocache)
self._filename = tmpfilename
# put the image into the cache if needed
@@ -624,7 +627,7 @@ class Image(EventDispatcher):
.. warning::
This function can be used only with images loaded with
- keep_data=True keyword. For examples ::
+ keep_data=True keyword. For examples::
m = Image.load('image.png', keep_data=True)
color = m.read_pixel(150, 150)
diff --git a/kivy/core/spelling/__init__.py b/kivy/core/spelling/__init__.py
index 1942e2918..b3ba9d003 100644
--- a/kivy/core/spelling/__init__.py
+++ b/kivy/core/spelling/__init__.py
@@ -5,6 +5,21 @@ Spelling
Provide abstracted access to a range of spellchecking backends. Also provides
word suggestions. The API is inspired by enchant, but other backends can be
added that implement the same API.
+
+Spelling currently require `python-enchant` for all platform, except OSX, where
+a native implementation exist.
+
+::
+
+ >>> from kivy.core.spelling import Spelling
+ >>> s = Spelling()
+ >>> s.list_languages()
+ ['en', 'en_CA', 'en_GB', 'en_US']
+ >>> s.select_language('en_US')
+ >>> s.check('helo')
+ [u'hole', u'help', u'helot', u'hello', u'halo', u'hero', u'hell', u'held',
+ u'helm', u'he-lo']
+
'''
__all__ = ('Spelling', 'SpellingBase', 'NoSuchLangError',
diff --git a/kivy/core/text/__init__.py b/kivy/core/text/__init__.py
index 5b11879e9..12c4d84c9 100644
--- a/kivy/core/text/__init__.py
+++ b/kivy/core/text/__init__.py
@@ -5,9 +5,9 @@ Text
Abstraction of text creation. Depending of the selected backend, the text
rendering can be more or less accurate.
-.. versionadded::
- Starting to 1.0.7, the :class:`LabelBase` don't generate any texture is the
- text have a width <= 1.
+.. versionchanged:: 1.0.7
+ The :class:`LabelBase` don't generate any texture is the text have a width
+ <= 1.
'''
__all__ = ('LabelBase', 'Label')
@@ -17,7 +17,6 @@ import os
from kivy import kivy_data_dir
from kivy.graphics.texture import Texture
from kivy.core import core_select_lib
-from kivy.utils import platform
from kivy.resources import resource_find
DEFAULT_FONT = 'DroidSans'
@@ -52,14 +51,14 @@ class LabelBase(object):
Activate "bold" text style
`italic`: bool, default to False
Activate "italic" text style
- `text_size`: list, default to (None, None)
+ `text_size`: tuple, default to (None, None)
Add constraint to render the text (inside a bounding box)
If no size is given, the label size will be set to the text size.
- `padding`: int, default to None
- If it's a integer, it will set padding_x and padding_y
- `padding_x`: int, default to 0
+ `padding`: float, default to None
+ If it's a float, it will set padding_x and padding_y
+ `padding_x`: float, default to 0.0
Left/right padding
- `padding_y`: int, default to 0
+ `padding_y`: float, default to 0.0
Top/bottom padding
`halign`: str, default to "left"
Horizontal text alignement inside bounding box
@@ -82,80 +81,43 @@ class LabelBase(object):
_fonts_cache = {}
- def __init__(self, **kwargs):
- if 'font_size' not in kwargs:
- kwargs['font_size'] = 12
- if 'font_name' not in kwargs:
- kwargs['font_name'] = DEFAULT_FONT
- if 'bold' not in kwargs:
- kwargs['bold'] = False
- if 'italic' not in kwargs:
- kwargs['italic'] = False
- if 'halign' not in kwargs:
- kwargs['halign'] = 'left'
- if 'valign' not in kwargs:
- kwargs['valign'] = 'bottom'
- if 'padding_x' not in kwargs:
- kwargs['padding_x'] = None
- if 'padding_y' not in kwargs:
- kwargs['padding_y'] = None
- if 'shorten' not in kwargs:
- kwargs['shorten'] = False
- if 'mipmap' not in kwargs:
- kwargs['mipmap'] = False
- if 'color' not in kwargs:
- kwargs['color'] = (1, 1, 1, 1)
- if 'padding' not in kwargs:
- kwargs['padding'] = padding = None
- else:
- padding = kwargs['padding']
+ def __init__(self, text='', font_size=12, font_name=DEFAULT_FONT,
+ bold=False, italic=False, halign='left', valign='bottom',
+ shorten=False, text_size=None, mipmap=False, color=None,
+ **kwargs):
- tp_padding = type(padding)
- padding_x = padding_y = None
- if 'padding_x' in kwargs:
- padding_x = kwargs['padding_x']
- if 'padding_y' in kwargs:
- padding_y = kwargs['padding_y']
- if not padding_x:
- if tp_padding is tuple or tp_padding is list:
- kwargs['padding_x'] = padding_x = float(padding[0])
- elif padding is not None:
- kwargs['padding_x'] = padding_x = float(padding)
+ options = {'text': text, 'font_size': font_size,
+ 'font_name': font_name, 'bold': bold, 'italic': italic,
+ 'halign': halign, 'valign': valign, 'shorten': shorten,
+ 'mipmap': mipmap}
+
+ options['color'] = color or (1, 1, 1, 1)
+ options['padding'] = kwargs.get('padding', 0)
+ options['padding_x'] = kwargs.get('padding_x', options['padding'])
+ options['padding_y'] = kwargs.get('padding_y', options['padding'])
+
+ if 'size' in kwargs:
+ options['text_size'] = kwargs['size']
+ else:
+ if text_size is None:
+ options['text_size'] = (None, None)
else:
- kwargs['padding_x'] = padding_x = 0
- if not padding_y:
- if tp_padding is tuple or tp_padding is list:
- kwargs['padding_y'] = float(padding[1])
- elif padding is not None:
- kwargs['padding_y'] = float(padding)
- else:
- kwargs['padding_y'] = 0
+ options['text_size'] = text_size
- if 'text_size' in kwargs:
- ts = kwargs['text_size']
- elif 'size' in kwargs:
- ts = kwargs['size']
+ text_width, text_height = options['text_size']
+ if text_width is not None:
+ self._text_size = (
+ text_width - options['padding_x'] * 2,
+ text_height)
else:
- ts = (None, None)
+ self._text_size = options['text_size']
- uw = ts[0]
- if uw is not None:
- self._text_size = uw - padding_x * 2, ts[1]
- else:
- self._text_size = ts
-
- super(LabelBase, self).__init__()
-
- self._text = None
+ self._text = options['text']
self._internal_height = 0
- self.options = kwargs
+ self.options = options
self.texture = None
self.resolve_font_name()
- if 'text' in kwargs:
- self.text = kwargs['text']
- else:
- self.text = ''
@staticmethod
def register(name, fn_regular, fn_italic=None, fn_bold=None,
@@ -164,7 +126,7 @@ class LabelBase(object):
.. versionadded:: 1.1.0
- If you're using directly a ttf, you might not be able to use bold/italic
+ If you're using a ttf directly, you might not be able to use bold/italic
of the ttf version. If the font is delivered with different version of
it (one regular, one italic and one bold), then you need to register it
and use the alias instead.
@@ -173,52 +135,38 @@ class LabelBase(object):
:func:`kivy.resources.resource_find`. If fn_italic/fn_bold are None,
fn_regular will be used instead.
'''
- _fn_regular = resource_find(fn_regular)
- if _fn_regular is None:
- raise IOError('File %r not found' % fn_regular)
- if fn_italic is None:
- _fn_italic = _fn_regular
- else:
- _fn_italic = resource_find(fn_italic)
- if _fn_italic is None:
- raise IOError('File %r not found' % fn_italic)
- if fn_bold is None:
- _fn_bold = _fn_regular
- else:
- _fn_bold = resource_find(fn_bold)
- if _fn_bold is None:
- raise IOError('File %r not found' % fn_bold)
- if fn_bolditalic is None:
- _fn_bolditalic = _fn_regular
- else:
- _fn_bolditalic = resource_find(fn_bolditalic)
- if _fn_bolditalic is None:
- raise IOError('Label: File %r not found' % fn_bolditalic)
- LabelBase._fonts[name] = (_fn_regular, _fn_italic, _fn_bold,
- _fn_bolditalic)
+
+ fonts = []
+
+ for font_type in fn_regular, fn_italic, fn_bold, fn_bolditalic:
+ if font_type is not None:
+ font = resource_find(font_type)
+
+ if font is None:
+ raise IOError('File {0}s not found'.format(font_type))
+ else:
+ fonts.append(font)
+ else:
+ fonts.append(fonts[-1]) # add regular font to list again
+
+ LabelBase._fonts[name] = tuple(fonts)
def resolve_font_name(self):
options = self.options
- if 'font_name' not in options:
- return
- fontname = options['font_name']
+ fontname = self.options['font_name']
fonts = self._fonts
fontscache = self._fonts_cache
# is the font is registered ?
if fontname in fonts:
# return the prefered font for the current bold/italic combinaison
- bold = options['bold']
- italic = options['italic']
- font = fonts[fontname]
- if not bold and not italic:
- options['font_name_r'] = font[FONT_REGULAR]
- elif bold and italic:
- options['font_name_r'] = font[FONT_BOLDITALIC]
- elif bold:
- options['font_name_r'] = font[FONT_BOLD]
+ italic = int(options['italic'])
+ if options['bold']:
+ bold = FONT_BOLD
else:
- options['font_name'] = font[FONT_ITALIC]
+ bold = FONT_REGULAR
+
+ options['font_name_r'] = fonts[fontname][italic | bold]
elif fontname in fontscache:
options['font_name_r'] = fontscache[fontname]
@@ -228,9 +176,7 @@ class LabelBase(object):
# XXX for compatibility, check directly in the data dir
filename = os.path.join(kivy_data_dir, fontname)
if not os.path.exists(filename):
- filename = None
- if filename is None:
- raise IOError('Label: File %r not found' % fontname)
+ raise IOError('Label: File %r not found' % fontname)
fontscache[fontname] = filename
options['font_name_r'] = filename
@@ -247,27 +193,26 @@ class LabelBase(object):
def _render_end(self):
pass
- def shorten(self, text):
+ def shorten(self, text, margin=2):
# Just a tiny shortcut
textwidth = lambda txt: self.get_extents(txt)[0]
- mid = len(text)/2
- begin = text[:mid].strip()
- end = text[mid:].strip()
- steps = 1
- middle = '...'
- width = textwidth(begin+end) + textwidth(middle)
- last_width = width
- while width > self.text_size[0]:
- begin = text[:mid - steps].strip()
- end = text[mid + steps:].strip()
- steps += 1
- width = textwidth(begin+end) + textwidth(middle)
- if width == last_width:
- # No more shortening possible. This is the best we can
- # do. :-( -- Prevent infinite while loop.
- break
- last_width = width
- return begin + middle + end
+ if self.text_size[0] is None:
+ width = 0
+ else:
+ width = int(self.text_size[0])
+
+ letters = text + '...'
+ letter_width = textwidth(letters) // len(letters)
+ max_letters = width // letter_width
+ segment = (max_letters // 2)
+
+ if segment - margin > 5:
+ segment -= margin
+ return '{0}...{1}'.format(text[:segment].strip(),
+ text[-segment:].strip())
+ else:
+ segment = max_letters - 3 # length of '...'
+ return '{0}...'.format(text[:segment].strip())
def render(self, real=False):
'''Return a tuple(width, height) to create the image
diff --git a/kivy/core/text/markup.py b/kivy/core/text/markup.py
index cc9cacc33..1d05d14db 100644
--- a/kivy/core/text/markup.py
+++ b/kivy/core/text/markup.py
@@ -260,22 +260,33 @@ class MarkupLabel(MarkupLabelBase):
r = self._render_text
# convert halign/valign to int, faster comparaison
- #av = {'top': 0, 'middle': 1, 'bottom': 2}[self.options['valign']]
+ av = {'top': 0, 'middle': 1, 'bottom': 2}[self.options['valign']]
ah = {'left': 0, 'center': 1, 'right': 2}[self.options['halign']]
y = 0
w, h = self._size
refs = self._refs
+ no_of_lines = len(self._lines)-1
+
for line in self._lines:
lh = line[1]
+ lw = line[0]
# horizontal alignement
if ah == 0:
x = 0
elif ah == 1:
- x = int((w - line[0]) / 2)
+ x = int((w - lw) / 2)
else:
- x = w - line[0]
+ x = w - lw
+
+ # vertical alignement
+ if y == 0:
+ if av == 1:
+ y = int((h - (lh*no_of_lines))/2)
+ elif av == 2:
+ y = h - (lh*(no_of_lines))
+
for pw, ph, part, options in line[2]:
self.options = options
diff --git a/kivy/core/window/__init__.py b/kivy/core/window/__init__.py
index d7cf98481..5f2bf5951 100755
--- a/kivy/core/window/__init__.py
+++ b/kivy/core/window/__init__.py
@@ -1,3 +1,4 @@
+# pylint: disable=W0611
'''
Window
======
@@ -194,18 +195,21 @@ class WindowBase(EventDispatcher):
`on_keyboard`: key, scancode, codepoint, modifier
Fired when the keyboard is in action
.. versionchanged:: 1.3.0
- The *unicode* parameter has be deprecated in favor of
- codepoint, and will be removed completely in future versions
+
+ The *unicode* parameter has be deprecated in favor of
+ codepoint, and will be removed completely in future versions
`on_key_down`: key, scancode, codepoint
Fired when a key is down
.. versionchanged:: 1.3.0
- The *unicode* parameter has be deprecated in favor of
- codepoint, and will be removed completely in future versions
+
+ The *unicode* parameter has be deprecated in favor of
+ codepoint, and will be removed completely in future versions
`on_key_up`: key, scancode, codepoint
Fired when a key is up
.. versionchanged:: 1.3.0
- The *unicode* parameter has be deprecated in favor of
- codepoint, and will be removed completely in future versions
+
+ The *unicode* parameter has be deprecated in favor of
+ codepoint, and will be removed completely in future versions
`on_dropfile`: str
Fired when a file is dropped on the application
'''
@@ -250,17 +254,22 @@ class WindowBase(EventDispatcher):
def _get_size(self):
r = self._rotation
w, h = self._size
- if r == 0 or r == 180:
+ if r in (0, 180):
return w, h
return h, w
def _set_size(self, size):
- if super(WindowBase, self)._set_size(size):
- Logger.debug('Window: Resize window to %s' % str(self.size))
+ if self._size != size:
+ r = self._rotation
+ if r in (0, 180):
+ self._size = size
+ else:
+ self._size = size[1], size[0]
+
self.dispatch('on_resize', *size)
return True
- return False
-
+ else:
+ return False
size = AliasProperty(_get_size, _set_size)
'''Get the rotated size of the window. If :data:`rotation` is set, then the
size will change to reflect the rotation.
@@ -281,7 +290,7 @@ class WindowBase(EventDispatcher):
bind=('_clearcolor', ))
'''Color used to clear window.
- ::
+ ::
from kivy.core.window import Window
# red background color
@@ -461,7 +470,7 @@ class WindowBase(EventDispatcher):
self.parent = self
# before creating the window
- __import__('kivy.core.gl')
+ import kivy.core.gl
# configure the window
self.create_window()
@@ -755,7 +764,7 @@ class WindowBase(EventDispatcher):
pass
def on_keyboard(self, key,
- scancode=None, codepoint=None, modifier=None ,**kwargs):
+ scancode=None, codepoint=None, modifier=None, **kwargs):
'''Event called when keyboard is in action
.. warning::
@@ -766,7 +775,6 @@ class WindowBase(EventDispatcher):
"and will be removed in future versions. Use codepoint "
"instead, which has identical semantics.")
-
def on_key_down(self, key,
scancode=None, codepoint=None, modifier=None, **kwargs):
'''Event called when a key is down (same arguments as on_keyboard)'''
@@ -883,7 +891,6 @@ class WindowBase(EventDispatcher):
and if configuration allowed it, a VKeyboard instance.
.. versionchanged:: 1.0.8
-
`target` have been added, and must be the widget source that request
the keyboard. If set, the widget must have one method named
`on_keyboard_text`, that will be called from the vkeyboard.
@@ -967,4 +974,3 @@ Window = core_select_lib('window', (
('pygame', 'window_pygame', 'WindowPygame'),
('sdl', 'window_sdl', 'WindowSDL'),
), True)
-
diff --git a/kivy/core/window/window_pygame.py b/kivy/core/window/window_pygame.py
index 53668bc2b..b18f688ec 100644
--- a/kivy/core/window/window_pygame.py
+++ b/kivy/core/window/window_pygame.py
@@ -345,7 +345,7 @@ class WindowPygame(WindowBase):
def request_keyboard(self, *largs):
keyboard = super(WindowPygame, self).request_keyboard(*largs)
- if android:
+ if android and not self.allow_vkeyboard:
android.show_keyboard()
return keyboard
diff --git a/kivy/data/images/defaulttheme-0.png b/kivy/data/images/defaulttheme-0.png
index 4d3b358d8..070430af8 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 73ab951c4..0588cc653 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], "progressbar_background": [160, 137, 24, 24], "media-playback-pause": [448, 344, 48, 48], "tab_btn_pressed": [465, 178, 32, 32], "image-missing": [399, 344, 48, 48], "filechooser_selected": [187, 393, 118, 118], "audio-volume-muted": [350, 344, 48, 48], "sliderv_background": [473, 404, 37, 41], "tab": [332, 228, 96, 32], "close": [490, 491, 20, 20], "ring": [1, 326, 185, 185], "vkeyboard_key_down": [61, 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], "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": [94, 129, 32, 32], "checkbox_radio_on": [399, 178, 32, 32], "checkbox_on": [333, 178, 32, 32], "tree_opened": [490, 449, 20, 20], "button_pressed": [31, 124, 29, 37], "media-playback-stop": [50, 162, 48, 48], "audio-volume-high": [424, 397, 48, 48], "audio-volume-low": [252, 344, 48, 48], "bubble_btn": [234, 178, 32, 32], "slider_cursor": [99, 162, 48, 48], "button": [1, 124, 29, 37], "progressbar": [127, 137, 32, 24], "switch-button": [190, 178, 43, 32], "filechooser_folder": [397, 261, 64, 64], "popup-background": [462, 271, 45, 54], "textinput_active": [1, 211, 114, 114], "textinput": [116, 214, 111, 111]}}
\ No newline at end of file
+{"defaulttheme-0.png": {"player-play-overlay": [306, 396, 117, 115], "spinner_pressed": [91, 124, 29, 37], "progressbar_background": [220, 137, 24, 24], "media-playback-pause": [448, 344, 48, 48], "tab_btn_pressed": [465, 178, 32, 32], "image-missing": [399, 344, 48, 48], "filechooser_selected": [187, 393, 118, 118], "audio-volume-muted": [350, 344, 48, 48], "sliderv_background": [473, 404, 37, 41], "tab": [332, 228, 96, 32], "close": [490, 491, 20, 20], "ring": [1, 326, 185, 185], "vkeyboard_key_down": [121, 129, 32, 32], "vkeyboard_background": [187, 328, 64, 64], "checkbox_off": [300, 178, 32, 32], "bubble_arrow": [148, 162, 16, 10], "player-background": [228, 222, 103, 103], "bubble": [424, 446, 65, 65], "spinner": [61, 124, 29, 37], "sliderh_background": [148, 173, 41, 37], "audio-volume-medium": [301, 344, 48, 48], "media-playback-start": [1, 162, 48, 48], "tab_btn": [432, 178, 32, 32], "bubble_btn_pressed": [267, 178, 32, 32], "tree_closed": [490, 470, 20, 20], "switch-background": [429, 228, 83, 32], "filechooser_file": [332, 261, 64, 64], "checkbox_radio_off": [366, 178, 32, 32], "vkeyboard_key_normal": [154, 129, 32, 32], "checkbox_radio_on": [399, 178, 32, 32], "checkbox_on": [333, 178, 32, 32], "tree_opened": [490, 449, 20, 20], "button_pressed": [31, 124, 29, 37], "media-playback-stop": [50, 162, 48, 48], "audio-volume-high": [424, 397, 48, 48], "audio-volume-low": [252, 344, 48, 48], "bubble_btn": [234, 178, 32, 32], "slider_cursor": [99, 162, 48, 48], "button": [1, 124, 29, 37], "progressbar": [187, 137, 32, 24], "switch-button": [190, 178, 43, 32], "filechooser_folder": [397, 261, 64, 64], "popup-background": [462, 271, 45, 54], "textinput_active": [1, 211, 114, 114], "textinput": [116, 214, 111, 111]}}
\ No newline at end of file
diff --git a/kivy/data/keyboards/qwerty.json b/kivy/data/keyboards/qwerty.json
index 9127ba467..d1dac3c97 100644
--- a/kivy/data/keyboards/qwerty.json
+++ b/kivy/data/keyboards/qwerty.json
@@ -7,28 +7,28 @@
["`", "`", "`", 1], ["1", "1", "1", 1], ["2", "2", "2", 1],
["3", "3", "3", 1], ["4", "4", "4", 1], ["5", "5", "5", 1],
["6", "6", "6", 1], ["7", "7", "7", 1], ["8", "8", "8", 1],
- ["9", "9", "9", 1], ["0", "0", "0", 1], ["+", "+", "+", 1],
+ ["9", "9", "9", 1], ["0", "0", "0", 1], ["-", "-", "-", 1],
["=", "=", "=", 1], ["\u232b", null, "backspace", 2]
],
"normal_2" : [
["\u21B9", "\t", "tab", 1.5], ["q", "q", "q", 1], ["w", "w", "w", 1],
["e", "e", "e", 1], ["r", "r", "r", 1], ["t", "t", "t", 1],
["y", "y", "y", 1], ["u", "u", "u", 1], ["i", "i", "i", 1],
- ["o", "o", "o", 1], ["p", "p", "p", 1], ["{", "{", "{", 1],
- ["}", "}", "}", 1], ["|", "|", "|", 1.5]
+ ["o", "o", "o", 1], ["p", "p", "p", 1], ["[", "[", "[", 1],
+ ["]", "]", "j", 1], ["\\", "\\", "\\", 1]
],
"normal_3": [
["\u21ea", null, "capslock", 1.8], ["a", "a", "a", 1], ["s", "s", "s", 1],
["d", "d", "d", 1], ["f", "f", "f", 1], ["g", "g", "g", 1],
["h", "h", "h", 1], ["j", "j", "j", 1], ["k", "k", "k", 1],
- ["l", "l", "l", 1], [":", ":", ":", 1], ["\"", "\"", "\"", 1],
+ ["l", "l", "l", 1], [":", ":", ":", 1], ["'", "'", "'", 1],
["\u23ce", null, "enter", 2.2]
],
"normal_4": [
["\u21e7", null, "shift", 2.5], ["z", "z", null, 1], ["x", "x", "x", 1],
["c", "c", "c", 1], ["v", "v", "v", 1], ["b", "b", "b", 1],
- ["n", "n", "n", 1], ["m", "m", "m", 1], ["<", "<", "<", 1],
- [">", ">", ">", 1], ["?", "?", "?", 1], ["\u21e7", null, "shift", 2.5]
+ ["n", "n", "n", 1], ["m", "m", "m", 1], [",", ",", ",", 1],
+ [".", ".", ".", 1], ["/", "/", "/", 1], ["\u21e7", null, "shift", 2.5]
],
"normal_5": [
[" ", " ", "spacebar", 12], ["\u2b12", null, "layout", 1.5], ["\u2a2f", null, "escape", 1.5]
@@ -44,21 +44,21 @@
["\u21B9", "\t", "tab", 1.5], ["Q", "Q", null, 1], ["W", "W", null, 1],
["E", "E", "e", 1], ["R", "R", "r", 1], ["T", "T", "t", 1],
["Y", "Y", "y", 1], ["U", "U", "u", 1], ["I", "I", "i", 1],
- ["O", "O", "o", 1], ["P", "P", "p", 1], ["[", "[", "[", 1],
- ["]", "]", "j", 1], ["?", "?", "?", 1.5]
+ ["O", "O", "o", 1], ["P", "P", "p", 1], ["{", "{", "{", 1],
+ ["}", "}", "}", 1], ["|", "|", "|", 1.5]
],
"shift_3": [
["\u21ea", null, "capslock", 1.8], ["A", "A", "a", 1], ["S", "S", "s", 1],
["D", "D", "d", 1], ["F", "F", "f", 1], ["G", "G", "g", 1],
["H", "H", "h", 1], ["J", "J", "j", 1], ["K", "K", "k", 1],
- ["L", "L", "l", 1], [":", ":", ":", 1], ["'", "'", "'", 1],
+ ["L", "L", "l", 1], [";", ";", ";", 1], ["\"", "\"", "\"", 1],
["\u23ce", null, "enter", 2.2]
],
"shift_4": [
["\u21e7", null, "shift", 2.5], ["Z", "Z", "z", 1], ["X", "X", "x", 1],
["C", "C", "c", 1], ["V", "V", "v", 1], ["B", "B", "b", 1],
- ["N", "N", "n", 1], ["M", "M", "m", 1], [",", ",", ",", 1],
- [".", ".", ".", 1], ["/", "/", "/", 1], ["\u21e7", null, "shift", 2.5]
+ ["N", "N", "n", 1], ["M", "M", "m", 1], ["<", "<", "<", 1],
+ [">", ">", ">", 1], ["?", "?", "?", 1.5], ["\u21e7", null, "shift", 2.5]
],
"shift_5": [
[" ", " ", "spacebar", 12], ["\u2b12", null, "layout", 1.5], ["\u2a2f", null, "escape", 1.5]
diff --git a/kivy/data/style.kv b/kivy/data/style.kv
index b489e7a5e..53b8271b1 100644
--- a/kivy/data/style.kv
+++ b/kivy/data/style.kv
@@ -388,9 +388,9 @@
size: 43, 32
pos: int(self.center_x - 41 + self.active_norm_pos * 41), int(self.center_y - 16)
-# Popup widget
-:
- _container: container
+
+# ModalView widget
+:
canvas:
Color:
rgba: root.background_color[:3] + [root.background_color[-1] * self._anim_alpha]
@@ -405,6 +405,10 @@
pos: self.pos
size: self.size
+
+# Popup widget
+:
+ _container: container
GridLayout:
padding: 12
cols: 1
@@ -757,3 +761,23 @@
do_rotation: False
do_scale: False
auto_bring_to_front: False
+
+
+# =============================================================================
+# Screen Manager
+# =============================================================================
+
+:
+ canvas.before:
+ StencilPush
+ Rectangle:
+ pos: self.pos
+ size: self.size
+ StencilUse
+ canvas.after:
+ StencilUnUse
+ Rectangle:
+ pos: self.pos
+ size: self.size
+ StencilPop
+
diff --git a/kivy/factory.py b/kivy/factory.py
index a99f3c956..c7ef953ec 100644
--- a/kivy/factory.py
+++ b/kivy/factory.py
@@ -90,7 +90,7 @@ Factory = FactoryBase()
# Now import the file with all registers
# automatically generated by build_factory
-__import__('kivy.factory_registers')
+import kivy.factory_registers
Logger.info('Factory: %d symbols loaded' % len(Factory.classes))
if __name__ == '__main__':
diff --git a/kivy/factory_registers.py b/kivy/factory_registers.py
index df21bd547..6e469bb29 100644
--- a/kivy/factory_registers.py
+++ b/kivy/factory_registers.py
@@ -81,6 +81,7 @@ r('Bubble', module='kivy.uix.bubble')
r('BubbleButton', module='kivy.uix.bubble')
r('Camera', module='kivy.uix.camera')
r('CheckBox', module='kivy.uix.checkbox')
+r('DropDown', module='kivy.uix.dropdown')
r('FloatLayout', module='kivy.uix.floatlayout')
r('RelativeLayout', module='kivy.uix.relativelayout')
r('FileChooserListView', module='kivy.uix.filechooser')
@@ -89,6 +90,7 @@ r('Image', module='kivy.uix.image')
r('AsyncImage', module='kivy.uix.image')
r('Label', module='kivy.uix.label')
r('Layout', module='kivy.uix.layout')
+r('ModalView', module='kivy.uix.modalview')
r('ProgressBar', module='kivy.uix.progressbar')
r('Popup', module='kivy.uix.popup')
r('Scatter', module='kivy.uix.scatter')
@@ -98,6 +100,7 @@ r('Settings', module='kivy.uix.settings')
r('Slider', module='kivy.uix.slider')
r('Screen', module='kivy.uix.screenmanager')
r('ScreenManager', module='kivy.uix.screenmanager')
+r('Spinner', module='kivy.uix.spinner')
r('StackLayout', module='kivy.uix.stacklayout')
r('StencilView', module='kivy.uix.stencilview')
r('Switch', module='kivy.uix.switch')
diff --git a/kivy/graphics/compiler.pyx b/kivy/graphics/compiler.pyx
index cb5f8b39f..c53d7987f 100644
--- a/kivy/graphics/compiler.pyx
+++ b/kivy/graphics/compiler.pyx
@@ -9,7 +9,7 @@ at rendering time.
Reducing the context instructions
---------------------------------
-Imagine that you have a scheme like this ::
+Imagine that you have a scheme like this::
Color(1, 1, 1)
Rectangle(source='button.png', pos=(0, 0), size=(20, 20))
@@ -18,7 +18,7 @@ Imagine that you have a scheme like this ::
Color(1, 1, 1)
Rectangle(source='button.png', pos=(10, 20), size=(20, 20))
-The real instruction seen by the graphics canvas would be ::
+The real instruction seen by the graphics canvas would be::
Color: change 'color' context to 1, 1, 1
BindTexture: change 'texture0' to `button.png texture`
@@ -32,7 +32,7 @@ The real instruction seen by the graphics canvas would be ::
Only the first :class:`~kivy.graphics.context_instructions.Color` and
:class:`~kivy.graphics.context_instructions.BindTexture` are useful, and really
-change the context. We can reduce them to ::
+change the context. We can reduce them to::
Color: change 'color' context to 1, 1, 1
BindTexture: change 'texture0' to `button.png texture`
diff --git a/kivy/graphics/context.pyx b/kivy/graphics/context.pyx
index 011c5c614..1a841f413 100644
--- a/kivy/graphics/context.pyx
+++ b/kivy/graphics/context.pyx
@@ -122,11 +122,16 @@ cdef class Context:
cdef Shader shader
cdef Canvas canvas
- Cache.remove('kv.atlas')
+ image_objects = Cache._objects['kv.image']
Cache.remove('kv.image')
- Cache.remove('kv.texture')
Cache.remove('kv.shader')
+ # For texture cache, save the objects. We need to clean the cache as the
+ # others to prevent of using it during the reloading part.
+ # We'll restore the object later.
+ texture_objects = Cache._objects['kv.texture']
+ Cache.remove('kv.texture')
+
start = time()
Logger.info('Context: Reloading graphics data...')
Logger.debug('Context: Collect and flush all garbage')
@@ -143,6 +148,7 @@ cdef class Context:
texture = item()
if texture is None:
continue
+ Logger.trace('Context: unset texture id %r' % texture)
texture._id = -1
# First time, only reload base texture
@@ -150,14 +156,24 @@ cdef class Context:
texture = item()
if texture is None or isinstance(texture, TextureRegion):
continue
+ Logger.trace('Context: >> reload base texture %r' % texture)
texture.reload()
+ Logger.trace('Context: << reload base texture %r' % texture)
# Second time, update texture region id
for item in l:
texture = item()
if texture is None or not isinstance(texture, TextureRegion):
continue
+ Logger.trace('Context: >> reload region texture %r' % texture)
texture.reload()
+ Logger.trace('Context: << reload region texture %r' % texture)
+
+ # Restore texture cache
+ texture_objects.update(Cache._objects['kv.texture'])
+ Cache._objects['kv.texture'] = texture_objects
+ image_objects.update(Cache._objects['kv.image'])
+ Cache._objects['kv.image'] = image_objects
Logger.debug('Context: Reload vbos')
for item in self.l_vbo[:]:
diff --git a/kivy/graphics/context_instructions.pyx b/kivy/graphics/context_instructions.pyx
index 3f314152e..7b3482d82 100644
--- a/kivy/graphics/context_instructions.pyx
+++ b/kivy/graphics/context_instructions.pyx
@@ -267,6 +267,8 @@ cdef class BindTexture(ContextInstruction):
def __set__(self, object texture):
if not texture:
texture = get_default_texture()
+ Logger.trace('BindTexture: setting texture %r (previous is %r)' % (
+ texture, self._texture))
self._texture = texture
property index:
diff --git a/kivy/graphics/fbo.pyx b/kivy/graphics/fbo.pyx
index 4391bdd09..9b400fef8 100644
--- a/kivy/graphics/fbo.pyx
+++ b/kivy/graphics/fbo.pyx
@@ -7,7 +7,7 @@ texture, and use your fbo as a texture for another drawing.
Fbo act as a :class:`kivy.graphics.instructions.Canvas`.
-Exemple of using an fbo for some color rectangles ::
+Example of using an fbo for some color rectangles::
from kivy.graphics import Fbo, Color, Rectangle
diff --git a/kivy/graphics/stencil_instructions.pyx b/kivy/graphics/stencil_instructions.pyx
index e34a1ac86..092fe3146 100644
--- a/kivy/graphics/stencil_instructions.pyx
+++ b/kivy/graphics/stencil_instructions.pyx
@@ -5,7 +5,6 @@ Stencil instructions
.. versionadded:: 1.0.4
.. versionchanged:: 1.3.0
-
The stencil operation have been updated to resolve some issues appearing
when nested. You **must** know have a StencilUnUse and repeat the same
operation as you did after StencilPush.
diff --git a/kivy/graphics/texture.pyx b/kivy/graphics/texture.pyx
index 8af25b9b7..a88810531 100644
--- a/kivy/graphics/texture.pyx
+++ b/kivy/graphics/texture.pyx
@@ -184,11 +184,8 @@ include "config.pxi"
include "common.pxi"
include "opengl_utils_def.pxi"
-from os import environ
from array import array
from kivy.weakmethod import WeakMethod
-from kivy.logger import Logger
-from kivy.cache import Cache
from kivy.graphics.context cimport get_context
from kivy.graphics.c_opengl cimport *
@@ -761,7 +758,7 @@ cdef class Texture:
self._id = texture.id
else:
from kivy.core.image import Image
- image = Image(self._source)
+ image = Image(self._source, nocache=True)
self._id = image.texture.id
texture = image.texture
texture._nofree = 1
diff --git a/kivy/graphics/transformation.pyx b/kivy/graphics/transformation.pyx
index 381d5d377..5a22aa090 100644
--- a/kivy/graphics/transformation.pyx
+++ b/kivy/graphics/transformation.pyx
@@ -29,7 +29,8 @@ cdef extern from "string.h":
cdef double _EPS = 8.8817841970012523e-16
cdef class Matrix:
- '''Optimized matrix class for OpenGL ::
+ '''
+ Optimized matrix class for OpenGL::
>>> from kivy.graphics.transformation import Matrix
>>> m = Matrix()
@@ -42,8 +43,7 @@ cdef class Matrix:
[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
- [12 13 14 15]
-
+ [ 12 13 14 15]
'''
def __cinit__(self):
diff --git a/kivy/input/__init__.py b/kivy/input/__init__.py
index 5aa8fa6a6..9c68f9fce 100644
--- a/kivy/input/__init__.py
+++ b/kivy/input/__init__.py
@@ -1,3 +1,4 @@
+# pylint: disable=W0611
'''
Input management
================
@@ -29,7 +30,7 @@ from kivy.input.motionevent import MotionEvent
from kivy.input.postproc import kivy_postproc_modules
from kivy.input.provider import MotionEventProvider
from kivy.input.factory import MotionEventFactory
-__import__('kivy.input.providers')
+import kivy.input.providers
__all__ = (
MotionEvent.__name__,
diff --git a/kivy/input/factory.py b/kivy/input/factory.py
index 82e1ec9fa..94a092ff8 100644
--- a/kivy/input/factory.py
+++ b/kivy/input/factory.py
@@ -10,8 +10,8 @@ __all__ = ('MotionEventFactory', )
class MotionEventFactory:
'''MotionEvent factory is a class who register all availables input
- factories. If you create a new input factory, don't forget to register it
- ::
+ factories. If you create a new input factory, don't forget to register
+ it::
MotionEventFactory.register('myproviderid', MyInputProvider)
diff --git a/kivy/input/motionevent.py b/kivy/input/motionevent.py
index 41084be0b..3f2460f40 100644
--- a/kivy/input/motionevent.py
+++ b/kivy/input/motionevent.py
@@ -30,7 +30,7 @@ Listen to Motion Event
----------------------
If you want to receive all Motion Event, Touch or not, you can bind motion event
-from :class:`~kivy.core.window.Window` to your own callbacks ::
+from :class:`~kivy.core.window.Window` to your own callbacks::
def on_motion(self, etype, motionevent):
# will receive all motion event.
@@ -64,13 +64,13 @@ pressure Pressure of the contact. Use property `pressure`
shape Contact shape. Use property `shape`
============== ================================================================
-If yo want to know if the current :class:`MotionEvent` have an angle ::
+If yo want to know if the current :class:`MotionEvent` have an angle::
def on_touch_move(self, touch):
if 'angle' in touch.profile:
print 'The touch angle is', touch.a
-If you want to select only the fiducials ::
+If you want to select only the fiducials::
def on_touch_move(self, touch):
if 'markerid' not in touch.profile:
@@ -276,7 +276,7 @@ class MotionEvent(object):
def grab(self, class_instance, exclusive=False):
'''Grab this motion event. You can grab a touch if you absolutly want to
receive on_touch_move() and on_touch_up(), even if the touch is not
- dispatched by your parent ::
+ dispatched by your parent::
def on_touch_down(self, touch):
touch.grab(self)
diff --git a/kivy/input/postproc/dejitter.py b/kivy/input/postproc/dejitter.py
index 63fbdb21d..3cb3febf2 100644
--- a/kivy/input/postproc/dejitter.py
+++ b/kivy/input/postproc/dejitter.py
@@ -20,7 +20,7 @@ from kivy.config import Config
class InputPostprocDejitter(object):
'''
Get rid of jitterish BLOBs.
- Example ::
+ Example::
[postproc]
jitter_distance = 0.004
diff --git a/kivy/input/postproc/doubletap.py b/kivy/input/postproc/doubletap.py
index d1292125d..280db0b10 100644
--- a/kivy/input/postproc/doubletap.py
+++ b/kivy/input/postproc/doubletap.py
@@ -16,7 +16,7 @@ class InputPostprocDoubleTap(object):
'''
InputPostProcDoubleTap is a post-processor to check if
a touch is a double tap or not.
- Double tap can be configured in the Kivy config file ::
+ Double tap can be configured in the Kivy config file::
[postproc]
double_tap_time = 250
diff --git a/kivy/input/postproc/ignorelist.py b/kivy/input/postproc/ignorelist.py
index 2e691c081..6997b04a1 100644
--- a/kivy/input/postproc/ignorelist.py
+++ b/kivy/input/postproc/ignorelist.py
@@ -14,7 +14,7 @@ from kivy.utils import strtotuple
class InputPostprocIgnoreList(object):
'''
InputPostprocIgnoreList is a post-processor who remove touch in ignore list.
- Ignore list can be configured in the Kivy config file ::
+ Ignore list can be configured in the Kivy config file::
[postproc]
# Format: [(xmin, ymin, xmax, ymax), ...]
diff --git a/kivy/input/postproc/retaintouch.py b/kivy/input/postproc/retaintouch.py
index 8fed551d8..be9e31759 100644
--- a/kivy/input/postproc/retaintouch.py
+++ b/kivy/input/postproc/retaintouch.py
@@ -18,7 +18,7 @@ class InputPostprocRetainTouch(object):
touch, to reuse it under certains conditions. This module is designed to
prevent finger lost on some hardware/setup.
- Retain touch can be configured in the Kivy config file ::
+ Retain touch can be configured in the Kivy config file::
[postproc]
retain_time = 100
diff --git a/kivy/input/providers/__init__.py b/kivy/input/providers/__init__.py
index 70064f46e..acb14947b 100644
--- a/kivy/input/providers/__init__.py
+++ b/kivy/input/providers/__init__.py
@@ -1,3 +1,4 @@
+# pylint: disable=W0611
'''
Providers
=========
@@ -9,51 +10,51 @@ import os
from kivy.utils import platform as core_platform
from kivy.logger import Logger
-__import__('kivy.input.providers.tuio')
-__import__('kivy.input.providers.mouse')
+import kivy.input.providers.tuio
+import kivy.input.providers.mouse
platform = core_platform()
if platform == 'win' or 'KIVY_DOC' in os.environ:
try:
- __import__('kivy.input.providers.wm_touch')
- __import__('kivy.input.providers.wm_pen')
+ import kivy.input.providers.wm_touch
+ import kivy.input.providers.wm_pen
except:
err = 'Input: WM_Touch/WM_Pen not supported by your version of Windows'
Logger.warning(err)
if platform == 'macosx' or 'KIVY_DOC' in os.environ:
try:
- __import__('kivy.input.providers.mactouch')
+ import kivy.input.providers.mactouch
except:
err = 'Input: MacMultitouchSupport is not supported by your system'
Logger.exception(err)
if platform == 'linux' or 'KIVY_DOC' in os.environ:
try:
- __import__('kivy.input.providers.probesysfs')
+ import kivy.input.providers.probesysfs
except:
err = 'Input: ProbeSysfs is not supported by your version of linux'
Logger.exception(err)
try:
- __import__('kivy.input.providers.mtdev')
+ import kivy.input.providers.mtdev
except:
err = 'Input: MTDev is not supported by your version of linux'
Logger.exception(err)
try:
- __import__('kivy.input.providers.hidinput')
+ import kivy.input.providers.hidinput
except:
err = 'Input: HIDInput is not supported by your version of linux'
Logger.exception(err)
+ try:
+ import kivy.input.providers.linuxwacom
+ except:
+ err = 'Input: LinuxWacom is not supported by your version of linux'
+ Logger.exception(err)
if platform == 'android' or 'KIVY_DOC' in os.environ:
try:
- __import__('kivy.input.providers.linuxwacom')
- except:
- err = 'Input: LinuxWacom is not supported by your version of linux'
- Logger.exception(err)
- try:
- __import__('kivy.input.providers.androidjoystick')
+ import kivy.input.providers.androidjoystick
except:
err = 'Input: AndroidJoystick is not supported by your version of linux'
Logger.exception(err)
diff --git a/kivy/input/providers/androidjoystick.py b/kivy/input/providers/androidjoystick.py
index f279ce29a..74a073bfa 100644
--- a/kivy/input/providers/androidjoystick.py
+++ b/kivy/input/providers/androidjoystick.py
@@ -1,9 +1,10 @@
+# pylint: disable=W0611
__all__ = ('AndroidMotionEventProvider', )
import os
try:
- __import__('android')
+ import android
except ImportError:
if 'KIVY_DOC' not in os.environ:
raise Exception('android lib not found.')
diff --git a/kivy/input/providers/hidinput.py b/kivy/input/providers/hidinput.py
index b4aa2b8c1..e6ee0a55d 100644
--- a/kivy/input/providers/hidinput.py
+++ b/kivy/input/providers/hidinput.py
@@ -4,7 +4,7 @@ Native support of HID input from linux kernel
Support start from 2.6.32-ubuntu, or 2.6.34.
-To configure HIDInput, put in your configuration ::
+To configure HIDInput, put in your configuration::
[input]
# devicename = hidinput,/dev/input/eventXX
@@ -29,7 +29,7 @@ To fix that, you can add one of theses options on the argument line :
For example, on Asus T101M, the touchscreen report a range from 0-4095 for X and
Y value, but real value are in a range from 0-32768. You can put it on
-configuration ::
+configuration::
[input]
t101m = hidinput,/dev/input/event7,max_position_x=32768,max_position_y=32768
diff --git a/kivy/input/providers/linuxwacom.py b/kivy/input/providers/linuxwacom.py
index f147b6ba2..ae9b17c0e 100644
--- a/kivy/input/providers/linuxwacom.py
+++ b/kivy/input/providers/linuxwacom.py
@@ -2,7 +2,7 @@
Native support of Wacom tablet from linuxwacom driver
=====================================================
-To configure LinuxWacom, put in your configuration ::
+To configure LinuxWacom, put in your configuration::
[input]
pen = linuxwacom,/dev/input/event2,mode=pen
diff --git a/kivy/input/providers/mouse.py b/kivy/input/providers/mouse.py
index 48b4dce6e..bb9c45da3 100644
--- a/kivy/input/providers/mouse.py
+++ b/kivy/input/providers/mouse.py
@@ -8,7 +8,7 @@ touch can generate one event from mouse provider and from multitouch provider.
To avoid this behavior, you can activate the "disable_on_activity" token in
mouse. Then, if they are any touch active from another provider, the mouse will
-be discarded. Put in your configuration ::
+be discarded. Put in your configuration::
[input]
mouse = mouse,disable_on_activity
@@ -20,7 +20,7 @@ Disabling multitouch interaction with mouse
By default middle and right mouse buttons ared used for multitouch emulation.
If you want to use them for other purpose you can disable this behavior by
-activating the "disable_multitouch" token ::
+activating the "disable_multitouch" token::
[input]
mouse = mouse,disable_multitouch
diff --git a/kivy/input/providers/mtdev.py b/kivy/input/providers/mtdev.py
index 819c55727..c7531644a 100644
--- a/kivy/input/providers/mtdev.py
+++ b/kivy/input/providers/mtdev.py
@@ -8,7 +8,7 @@ You can read more on http://wiki.ubuntu.com/Multitouch
To configure MTDev, it's preferable to use probesysfs providers.
Check :py:class:`~kivy.input.providers.probesysfs` for more information.
-Otherwise, you can put in your configuration ::
+Otherwise, you can put in your configuration::
[input]
# devicename = hidinput,/dev/input/eventXX
diff --git a/kivy/input/providers/probesysfs.py b/kivy/input/providers/probesysfs.py
index 1125ce284..4fa055b0f 100644
--- a/kivy/input/providers/probesysfs.py
+++ b/kivy/input/providers/probesysfs.py
@@ -9,7 +9,7 @@ be made by other providers like: hidinput, mtdev, linuxwacom. mtdev is used
prior to other providers. For more information about mtdev, check
:py:class:`~kivy.input.providers.mtdev`.
-Here is an example of auto creation ::
+Here is an example of auto creation::
[input]
# using mtdev
diff --git a/kivy/input/providers/wm_touch.py b/kivy/input/providers/wm_touch.py
index 12f897fb2..259bab51e 100644
--- a/kivy/input/providers/wm_touch.py
+++ b/kivy/input/providers/wm_touch.py
@@ -119,7 +119,7 @@ else:
windll.user32.GetTouchInputInfo.restype = BOOL
windll.user32.GetTouchInputInfo.argtypes = [HANDLE, UINT,
POINTER(TOUCHINPUT), c_int]
-
+
class WM_MotionEventProvider(MotionEventProvider):
def start(self):
diff --git a/kivy/lang.py b/kivy/lang.py
index c3ebfc7d6..16b6bef05 100644
--- a/kivy/lang.py
+++ b/kivy/lang.py
@@ -265,7 +265,7 @@ For example, for a list, you'll need to create a entry with a image on the left,
and a label on the right. You can create a template for making that definition
more easy to use.
So, we'll create a template that require 2 entry in the context: a image
-filename and a title ::
+filename and a title::
[IconItem@BoxLayout]:
Image:
@@ -275,7 +275,7 @@ filename and a title ::
.. highlight:: python
-Then in Python, you can create instanciate the template with ::
+Then in Python, you can create instanciate the template with::
from kivy.lang import Builder
diff --git a/kivy/lib/debug.py b/kivy/lib/debug.py
index 75cf1b59a..f88943912 100644
--- a/kivy/lib/debug.py
+++ b/kivy/lib/debug.py
@@ -21,7 +21,7 @@ from types import TracebackType, CodeType
# on pypy we can take advantage of transparent proxies
try:
- tproxy = __import__('__pypy__')
+ import __pypy__ as tproxy
except ImportError:
tproxy = None
diff --git a/kivy/lib/osc/oscAPI.py b/kivy/lib/osc/oscAPI.py
index edaf455eb..6a7af537c 100644
--- a/kivy/lib/osc/oscAPI.py
+++ b/kivy/lib/osc/oscAPI.py
@@ -1,3 +1,4 @@
+# pylint: disable=W0611
''' simpleOSC 0.2
ixi software - July, 2006
www.ixi-software.net
@@ -38,7 +39,7 @@ try:
raise
use_multiprocessing = True
from multiprocessing import Process, Queue, Value
- __import__('multiprocessing.synchronize')
+ import multiprocessing.synchronize
Logger.info('OSC: using for socket')
except:
use_multiprocessing = False
diff --git a/kivy/loader.py b/kivy/loader.py
index 1ef061992..3c400d245 100644
--- a/kivy/loader.py
+++ b/kivy/loader.py
@@ -21,7 +21,7 @@ If you want to change the default loading image, you can do::
__all__ = ('Loader', 'LoaderBase', 'ProxyImage')
-from kivy import kivy_data_dir
+from kivy import kivy_data_dir, kivy_base_dir
from kivy.logger import Logger
from kivy.clock import Clock
from kivy.cache import Cache
@@ -96,7 +96,8 @@ class LoaderBase(object):
def error_image(self):
'''Image used for error (readonly)'''
if not self._error_image:
- error_png_fn = join(kivy_data_dir, 'images', 'image-missing.png')
+ error_png_fn = join(kivy_base_dir, 'tools/theming/defaulttheme',
+ 'image-missing.png')
self._error_image = ImageLoader.load(filename=error_png_fn)
return self._error_image
@@ -206,7 +207,7 @@ class LoaderBase(object):
'''Load a image using loader. A Proxy image is returned with a loading
image.
- ::
+ ::
img = Loader.image(filename)
# img will be a ProxyImage.
# You'll use it the same as an Image class.
diff --git a/kivy/logger.py b/kivy/logger.py
index 90ab9f8e2..b3a356851 100644
--- a/kivy/logger.py
+++ b/kivy/logger.py
@@ -288,7 +288,8 @@ if 'KIVY_NO_CONSOLELOG' not in os.environ:
Logger.addHandler(getattr(sys, '_kivy_logging_handler'))
else:
use_color = os.name != 'nt'
- color_fmt = formatter_message('[%(levelname)-18s] %(message)s', use_color)
+ color_fmt = formatter_message(
+ '[%(levelname)-18s] %(message)s', use_color)
formatter = ColoredFormatter(color_fmt, use_color=use_color)
console = ConsoleHandler()
console.setFormatter(formatter)
@@ -299,4 +300,3 @@ sys.stderr = LogFile('stderr', Logger.warning)
#: Kivy history handler
LoggerHistory = LoggerHistory
-
diff --git a/kivy/network/urlrequest.py b/kivy/network/urlrequest.py
index f77a38614..9e144cfed 100644
--- a/kivy/network/urlrequest.py
+++ b/kivy/network/urlrequest.py
@@ -74,7 +74,7 @@ from kivy.clock import Clock
class UrlRequest(Thread):
'''Url request. See module documentation for usage.
- .. versionchanged::
+ .. versionchanged:: 1.0.10
Add `method` parameters
:Parameters:
diff --git a/kivy/properties.pyx b/kivy/properties.pyx
index fbf95186d..7d57d5c25 100644
--- a/kivy/properties.pyx
+++ b/kivy/properties.pyx
@@ -244,7 +244,7 @@ cdef class Property:
'''
cdef list observers = obj.__storage[self._name]['observers']
for obj in observers[:]:
- if obj is observer:
+ if obj == observer:
observers.remove(obj)
def __set__(self, obj, val):
@@ -305,7 +305,6 @@ cdef class Property:
'''Dispatch the value change to all observers
.. versionchanged:: 1.1.0
-
The method is now accessible from Python.
This can be used to force the dispatch of the property, even if the
@@ -667,10 +666,10 @@ cdef class BoundedNumericProperty(Property):
'''
cdef dict s = obj.__storage[self._name]
if value is None:
- s['use_min'] = 0
+ s['use_max'] = 0
else:
- s['min'] = value
- s['use_min'] = 1
+ s['max'] = value
+ s['use_max'] = 1
def get_max(self, obj):
'''Return the maximum value acceptable for the BoundedNumericProperty in
@@ -836,7 +835,7 @@ cdef class AliasProperty(Property):
If you didn't find a Property class that fits to your needs, you can still
create Python getters and setters and create a property with both of them.
- Example from kivy/uix/widget.py ::
+ Example from kivy/uix/widget.py::
def get_right(self):
return self.x + self.width
diff --git a/kivy/tests/test_issue_599.py b/kivy/tests/test_issue_599.py
new file mode 100644
index 000000000..c369ef251
--- /dev/null
+++ b/kivy/tests/test_issue_599.py
@@ -0,0 +1,21 @@
+import unittest
+from kivy.event import EventDispatcher
+from kivy.properties import BoundedNumericProperty
+
+
+class PropertyWidget(EventDispatcher):
+ foo = BoundedNumericProperty(1, min=-5, max=5)
+
+
+class Issue599(unittest.TestCase):
+
+ def test_minmax(self):
+ wid = PropertyWidget()
+
+ self.assertEqual(wid.property('foo').get_min(wid), -5)
+ wid.property('foo').set_min(wid, 0)
+ self.assertEqual(wid.property('foo').get_min(wid), 0)
+
+ self.assertEqual(wid.property('foo').get_max(wid), 5)
+ wid.property('foo').set_max(wid, 10)
+ self.assertEqual(wid.property('foo').get_max(wid), 10)
diff --git a/kivy/tests/test_properties.py b/kivy/tests/test_properties.py
index a0980fcfb..162da7bf7 100644
--- a/kivy/tests/test_properties.py
+++ b/kivy/tests/test_properties.py
@@ -3,19 +3,11 @@ Test properties attached to a widget
'''
import unittest
+from kivy.event import EventDispatcher
-
-class Widget(object):
- '''Fake widget class'''
-
- def __init__(self, **kwargs):
- super(Widget, self).__init__(**kwargs)
- self.__dict__['__uid'] = 1
- self.__dict__['__storage'] = {}
-
-
-wid = Widget()
-
+class TestProperty(EventDispatcher):
+ pass
+wid = TestProperty()
class PropertiesTestCase(unittest.TestCase):
@@ -78,7 +70,7 @@ class PropertiesTestCase(unittest.TestCase):
self.assertEqual(a.get(wid), 'hello')
try:
- a.set(wid, 88) # number shouldn't be accepted
+ a.set(wid, 88) # number shouldn't be accepted
self.fail('string accept number, fail.')
except ValueError:
pass
@@ -94,7 +86,7 @@ class PropertiesTestCase(unittest.TestCase):
self.assertEqual(a.get(wid), 99)
try:
- a.set(wid, '') # string shouldn't be accepted
+ a.set(wid, '') # string shouldn't be accepted
self.fail('number accept string, fail.')
except ValueError:
pass
@@ -119,7 +111,6 @@ class PropertiesTestCase(unittest.TestCase):
a.set(wid, {'foo': 'bar'})
self.assertEqual(a.get(wid), {'foo': 'bar'})
-
def test_propertynone(self):
from kivy.properties import NumericProperty
@@ -269,4 +260,3 @@ class PropertiesTestCase(unittest.TestCase):
observe_called = 0
x.get(wid).update({'bleh': 5})
self.assertEqual(observe_called, 1)
-
diff --git a/kivy/tools/benchmark.py b/kivy/tools/benchmark.py
index 4c4b246bb..0057f7629 100644
--- a/kivy/tools/benchmark.py
+++ b/kivy/tools/benchmark.py
@@ -222,10 +222,8 @@ if reply.lower().strip() in ('', 'y'):
payload = {
'public': True, 'files': {
'benchmark.txt': {
- 'content': '\n'.join(report)
- }
- }
- }
+ 'content': '\n'.join(report)}}}
+
r = requests.post('https://api.github.com/gists', data=json.dumps(payload))
diff --git a/kivy/tools/pep8checker/pre-commit.githook b/kivy/tools/pep8checker/pre-commit.githook
index d66034b1e..0681f48bd 100755
--- a/kivy/tools/pep8checker/pre-commit.githook
+++ b/kivy/tools/pep8checker/pre-commit.githook
@@ -12,7 +12,7 @@
the styleguide checker over your code and abort the commit if there are any
errors. If that happens, please fix & retry.
- To install ::
+ To install::
cp kivy/tools/pep8checker/pre-commit.githook .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
diff --git a/kivy/tools/theming/defaulttheme/spinner.png b/kivy/tools/theming/defaulttheme/spinner.png
new file mode 100644
index 000000000..e7a45ac1e
Binary files /dev/null and b/kivy/tools/theming/defaulttheme/spinner.png differ
diff --git a/kivy/tools/theming/defaulttheme/spinner_pressed.png b/kivy/tools/theming/defaulttheme/spinner_pressed.png
new file mode 100644
index 000000000..77136b609
Binary files /dev/null and b/kivy/tools/theming/defaulttheme/spinner_pressed.png differ
diff --git a/kivy/uix/__init__.py b/kivy/uix/__init__.py
index 18fc08443..d4e3b709e 100644
--- a/kivy/uix/__init__.py
+++ b/kivy/uix/__init__.py
@@ -28,8 +28,9 @@ Read first: :doc:`api-kivy.uix.widget`
combinations. We call them complex because the assembly and usages are not as
generic as the classicals widgets.
- :doc:`api-kivy.uix.bubble`,
+ :doc:`api-kivy.uix.bubble`, :doc:`api-kivy.uix.dropdown`,
:doc:`api-kivy.uix.filechooser`, :doc:`api-kivy.uix.popup`,
+ :doc:`api-kivy.uix.spinner`,
:doc:`api-kivy.uix.tabbedpanel`, :doc:`api-kivy.uix.videoplayer`,
:doc:`api-kivy.uix.vkeyboard`,
diff --git a/kivy/uix/accordion.py b/kivy/uix/accordion.py
index 9f9f03041..f8e37a93b 100644
--- a/kivy/uix/accordion.py
+++ b/kivy/uix/accordion.py
@@ -110,8 +110,8 @@ class AccordionItem(FloatLayout):
title = StringProperty('')
'''Title string of the item. The title might be used in conjuction with the
- `AccordionItemTitle` template. If you are using a custom template, you can
- use that property as a text entry, or not. By default, it's used for the
+ `AccordionItemTitle` template. If you are using a custom template, you can
+ use that property as a text entry, or not. By default, it's used for the
title text. See title_template and the example below.
:data:`title` is a :class:`~kivy.properties.StringProperty`, default to ''
@@ -330,11 +330,11 @@ class Accordion(Widget):
super(Accordion, self).__init__(**kwargs)
self._trigger_layout = Clock.create_trigger(self._do_layout, -1)
self.bind(
- orientation = self._trigger_layout,
- children = self._trigger_layout,
- size = self._trigger_layout,
- pos = self._trigger_layout,
- min_space = self._trigger_layout)
+ orientation=self._trigger_layout,
+ children=self._trigger_layout,
+ size=self._trigger_layout,
+ pos=self._trigger_layout,
+ min_space=self._trigger_layout)
def add_widget(self, widget, *largs):
if not isinstance(widget, AccordionItem):
diff --git a/kivy/uix/anchorlayout.py b/kivy/uix/anchorlayout.py
index e4c81ed4c..932196afc 100644
--- a/kivy/uix/anchorlayout.py
+++ b/kivy/uix/anchorlayout.py
@@ -16,7 +16,7 @@ Anchor Layout
or center.
-To draw a button in the lower-right corner ::
+To draw a button in the lower-right corner::
layout = AnchorLayout(
anchor_x='right', anchor_y='bottom')
@@ -61,6 +61,7 @@ class AnchorLayout(Layout):
def __init__(self, **kwargs):
super(AnchorLayout, self).__init__(**kwargs)
self.bind(
+ children = self._trigger_layout,
parent = self._trigger_layout,
padding = self._trigger_layout,
anchor_x = self._trigger_layout,
diff --git a/kivy/uix/bubble.py b/kivy/uix/bubble.py
index 3fb6cc084..d76907758 100644
--- a/kivy/uix/bubble.py
+++ b/kivy/uix/bubble.py
@@ -157,6 +157,7 @@ class Bubble(GridLayout):
color=self.background_color)
self.content = content = BubbleContent(parent=self)
super(Bubble, self).__init__(**kwargs)
+ content.parent = None
self.add_widget(content)
self.on_arrow_pos()
@@ -226,6 +227,10 @@ class Bubble(GridLayout):
self_arrow_img.height = self_arrow_img.texture_size[1]
widget_list = []
arrow_list = []
+ parent = self_arrow_img.parent
+ if parent:
+ parent.remove_widget(self_arrow_img)
+
if self_arrow_pos[0] == 'b' or self_arrow_pos[0] == 't':
self.cols = 1
self.rows = 2
diff --git a/kivy/uix/button.py b/kivy/uix/button.py
index 4c905e13d..1569246e2 100644
--- a/kivy/uix/button.py
+++ b/kivy/uix/button.py
@@ -13,7 +13,7 @@ the Label class::
button = Button(text='Hello world', font_size=14)
To attach a callback when the button is pressed (clicked/touched), use
-:class:`~kivy.uix.widget.Widget.bind` ::
+:class:`~kivy.uix.widget.Widget.bind`::
def callback(instance):
print 'The button <%s> is being pressed' % instance.text
@@ -24,7 +24,7 @@ To attach a callback when the button is pressed (clicked/touched), use
btn2.bind(on_press=callback)
If you want to be notified every time the button state changes, you can attach
-to the :data:`Button.state` property ::
+to the :data:`Button.state` property::
def callback(instance, value):
print 'My button <%s> state is <%s>' % (instance, value)
diff --git a/kivy/uix/camera.py b/kivy/uix/camera.py
index b836dfd73..81eb5dfb9 100644
--- a/kivy/uix/camera.py
+++ b/kivy/uix/camera.py
@@ -40,7 +40,7 @@ class Camera(Image):
play = BooleanProperty(True)
'''Boolean indicate if the camera is playing.
- You can start/stop the camera by setting this property. ::
+ You can start/stop the camera by setting this property::
# start the camera playing at creation (default)
cam = Camera(play=True)
@@ -63,7 +63,7 @@ class Camera(Image):
resolution = ListProperty([-1, -1])
'''Preferred resolution to use when invoking the camera. If you are using
- [-1, -1], the resolution will be the default one. ::
+ [-1, -1], the resolution will be the default one::
# create a camera object with the best image available
cam = Camera()
diff --git a/kivy/uix/dropdown.py b/kivy/uix/dropdown.py
new file mode 100644
index 000000000..94ded676b
--- /dev/null
+++ b/kivy/uix/dropdown.py
@@ -0,0 +1,302 @@
+'''
+Drop-Down List
+==============
+
+.. versionadded:: 1.4.0
+
+A versatile drop-down list, that can be used with custom widget. It allow you to
+display a list of widgets under a displayed widget. Unlike others toolkits, the
+list of widgets is what you want, it can be simple button, or images etc.
+
+The positionning of the drop-down list is fully automatic: we will always try to
+place the dropdown list in a way that the user can select an item in the list.
+
+Basic example
+-------------
+
+A button with a dropdown list of 10 possibles values. All the button within the
+dropdown list will trigger the dropdown :meth:`DropDown.select` method. And
+then, the mainbutton text will display the selection of the dropdown. ::
+
+ from kivy.uix.dropdown import DropDown
+ from kivy.uix.button import Button
+
+ # create a dropdown with 10 button
+ dropdown = DropDown()
+ for index in xrange(10):
+ btn = Button(text='Value %d' % index, size_hint_y=None, height=44)
+
+ # for each button, attach a callback that will call the select() method
+ # on the dropdown. We'll pass the text of the button as the data of the
+ # selection.
+ btn.bind(on_release=lambda btn: dropdown.select(btn.text))
+
+ # then add the button inside the dropdown
+ dropdown.add_widget(btn)
+
+ # create a big main button
+ mainbutton = Button(text='Hello', size_hint=(None, None))
+
+ # show the dropdown menu when the main button is released
+ # note: all the bind() always pass the instance of the caller (here, the
+ # mainbutton instance) as first argument of the callback (here,
+ # dropdown.open.).
+ mainbutton.bind(on_release=dropdown.open)
+
+ # one last thing, listen to the selection done in the dropdown list. assign
+ # the data to the button text.
+ dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
+
+
+Extending dropdown in Kv
+------------------------
+
+You could create a dropdown directly from kv::
+
+ #:kivy 1.4.0
+ :
+ Button:
+ text: 'My first Item'
+ size_hint_y: None
+ height: 44
+ on_release: root.select('item1')
+ Label:
+ text: 'Unselectable item'
+ size_hint_y: None
+ height: 44
+ Button:
+ text: 'My second Item'
+ size_hint_y: None
+ height: 44
+ on_release: root.select('item2')
+
+And then, create the associated python class, and use it::
+
+ class CustomDropDown(DropDown):
+ pass
+
+ dropdown = CustomDropDown()
+ mainbutton = Button(text='Hello', size_hint=(None, None))
+ mainbutton.bind(on_release=dropdown.open)
+ dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
+'''
+
+__all__ = ('DropDown', )
+
+from kivy.uix.scrollview import ScrollView
+from kivy.properties import ObjectProperty, NumericProperty, BooleanProperty
+from kivy.lang import Builder
+
+Builder.load_string('''
+:
+ container: container
+ do_scroll_x: False
+ size_hint: None, None
+
+ GridLayout:
+ id: container
+ size_hint_y: None
+ height: self.minimum_size[1]
+ cols: 1
+''')
+
+
+class DropDownException(Exception):
+ '''DropDownException class.
+ '''
+ pass
+
+
+class DropDown(ScrollView):
+ '''DropDown class. See module documentation for more information.
+
+ :Events:
+ `on_select`: data
+ Fired when a selection is done, with the data of the selection as
+ first argument. Data is what you pass in the :meth:`select` method
+ as first argument.
+ '''
+
+ auto_width = BooleanProperty(True)
+ '''By default, the width of the dropdown will be the same as the width of
+ the attached widget. Set to False if you want to provide your own width.
+ '''
+
+ max_height = NumericProperty(None, allownone=True)
+ '''Indicate the maximum height that the dropdown can take. If None, it will
+ take the maximum height available, until the top or bottom of the screen
+ will be reached.
+
+ :data:`max_height` is a :class:`~kivy.properties.NumericProperty`, default
+ to None.
+ '''
+
+ dismiss_on_select = BooleanProperty(True)
+ '''By default, the dropdown will be automatically dismissed when a selection
+ have been done. Set to False to prevent the dismiss.
+
+ :data:`dismiss_on_select` is a :class:`~kivy.properties.BooleanProperty`,
+ default to True.
+ '''
+
+ attach_to = ObjectProperty(allownone=True)
+ '''(internal) Property that will be set to the widget on which the drop down
+ list is attached to.
+
+ The method :meth:`open` will automatically set that property, while
+ :meth:`dismiss` will set back to None.
+ '''
+
+ container = ObjectProperty()
+ '''(internal) Property that will be set to the container of the dropdown
+ list, which is a :class:`~kivy.uix.gridlayout.GridLayout` by default.
+ '''
+
+ def __init__(self, **kwargs):
+ self._win = None
+ self.register_event_type('on_select')
+ super(DropDown, self).__init__(**kwargs)
+ self.container.bind(minimum_size=self._container_minimum_size)
+ self.bind(size=self._reposition)
+
+ def open(self, widget):
+ '''Open the dropdown list, and attach to a specific widget.
+ Depending the position of the widget on the window and the height of the
+ dropdown, the placement might be lower or upper to that widget.
+ '''
+ # ensure we are not already attached
+ if self.attach_to is not None:
+ self.dismiss()
+
+ # we will attach ourself to the main window, so ensure the widget we are
+ # looking for have a window
+ self._win = widget.get_parent_window()
+ if self._win is None:
+ raise DropDownException(
+ 'Cannot open a dropdown list on a hidden widget')
+
+ self.attach_to = widget
+ widget.bind(pos=self._reposition, size=self._reposition)
+ self._reposition()
+
+ # attach ourself to the main window
+ self._win.add_widget(self)
+
+ def dismiss(self, *largs):
+ '''Remove the dropdown widget from the iwndow, and detach itself from
+ the attached widget.
+ '''
+ if self.parent:
+ self.parent.remove_widget(self)
+ if self.attach_to:
+ self.attach_to.unbind(pos=self._reposition, size=self._reposition)
+ self.attach_to = None
+
+ def select(self, data):
+ '''Call this method to trigger the `on_select` event, with the `data`
+ selection. The `data` can be anything you want.
+ '''
+ self.dispatch('on_select', data)
+ if self.dismiss_on_select:
+ self.dismiss()
+
+ def on_select(self, data):
+ pass
+
+ def _container_minimum_size(self, instance, size):
+ if self.max_height:
+ self.height = min(size[1], self.max_height)
+ self.do_scroll_y = size[1] > self.max_height
+ else:
+ self.height = size[1]
+ self.do_scroll_y = True
+
+ def add_widget(self, *largs):
+ if self.container:
+ return self.container.add_widget(*largs)
+ return super(DropDown, self).add_widget(*largs)
+
+ def remove_widget(self, *largs):
+ if self.container:
+ return self.container.remove_widget(*largs)
+ return super(DropDown, self).remove_widget(*largs)
+
+ def clear_widgets(self):
+ if self.container:
+ return self.container.clear_widgets()
+ return super(DropDown, self).clear_widgets()
+
+ def on_touch_down(self, touch):
+ if super(DropDown, self).on_touch_down(touch):
+ return True
+ if self.collide_point(*touch.pos):
+ return True
+ self.dismiss()
+
+ def on_touch_up(self, touch):
+ if super(DropDown, self).on_touch_up(touch):
+ return True
+ self.dismiss()
+
+ def _reposition(self, *largs):
+ # calculate the coordinate of the attached widget in the window
+ # coordinate sysem
+ win = self._win
+ widget = self.attach_to
+ if not widget or not win:
+ return
+ wx, wy = widget.to_window(*widget.pos)
+ wtop, wright = widget.to_window(widget.top, widget.right)
+
+ # set width and x
+ if self.auto_width:
+ self.width = wright - wx
+
+ # ensure the dropdown list doesn't get out on the X axis, with a
+ # preference to 0 in case the list is too wide.
+ x = wx
+ if x + self.width > win.width:
+ x = win.width - self.width
+ if x < 0:
+ x = 0
+ self.x = x
+
+ # determine if we display the dropdown upper or lower to the widget
+ h_bottom = wy - self.height
+ h_top = win.height - (wtop + self.height)
+ if h_bottom > 0:
+ self.top = wy
+ elif h_top > 0:
+ self.y = wtop
+ else:
+ # none of both top/bottom have enough place to display the widget at
+ # the current size. Take the best side, and fit to it.
+ height = max(h_bottom, h_top)
+ if height == h_bottom:
+ self.top = wy
+ self.height = wy
+ else:
+ self.y = wtop
+ self.height = win.height - wtop
+
+
+if __name__ == '__main__':
+ from kivy.uix.button import Button
+ from kivy.base import runTouchApp
+
+ def show_dropdown(button, *largs):
+ dp = DropDown()
+ dp.bind(on_select=lambda instance, x: setattr(button, 'text', x))
+ for i in xrange(10):
+ item = Button(text='hello %d' % i, size_hint_y=None, height=44)
+ item.bind(on_release=lambda btn: dp.select(btn.text))
+ dp.add_widget(item)
+ dp.open(button)
+
+ def touch_move(instance, touch):
+ instance.center = touch.pos
+
+ btn = Button(text='SHOW', size_hint=(None, None), pos=(300, 200))
+ btn.bind(on_release=show_dropdown, on_touch_move=touch_move)
+
+ runTouchApp(btn)
diff --git a/kivy/uix/filechooser.py b/kivy/uix/filechooser.py
index fb07a3663..44a9c087e 100644
--- a/kivy/uix/filechooser.py
+++ b/kivy/uix/filechooser.py
@@ -6,13 +6,12 @@ FileChooser
.. warning::
- This is experimental and subject to change as long as this warning notice is
- present.
+ This is experimental and subject to change as long as this warning notice
+ is present.
.. versionchanged:: 1.2.0
-
- In chooser template, the `controller` is not a direct reference anymore, but
- a weak-reference.
+ In chooser template, the `controller` is not a direct reference anymore,
+ but a weak-reference.
You must update all the notation `root.controller.xxx` to
`root.controller().xxx`.
@@ -140,19 +139,29 @@ class FileChooserController(FloatLayout):
filters = ListProperty([])
''':class:`~kivy.properties.ListProperty`, defaults to [], equal to '\*'.
- The filters to be applied to the files in the directory, e.g. ['\*.png'].
+ The filters to be applied to the files in the directory.
+
The filters are not reset when the path changes. You need to do that
- yourself if desired. You can use the following patterns:
+ yourself if desired.
- ========== =================================
- Pattern Meaning
- ========== =================================
- \* matches everything
- ? matches any single character
- [seq] matches any character in seq
- [!seq] matches any character not in seq
- ========== =================================
+ There are two kinds of filters :
+ filename patterns : e.g. ['\*.png'].
+ You can use the following patterns:
+
+ ========== =================================
+ Pattern Meaning
+ ========== =================================
+ \* matches everything
+ ? matches any single character
+ [seq] matches any character in seq
+ [!seq] matches any character not in seq
+ ========== =================================
+
+ .. versionchanged:: 1.4.0
+ if the filter is a callable (function or method). It will be called
+ with the path and the file name as arguments for each file in dir. The
+ callable should returns True to indicate a match and False overwise.
'''
filter_dirs = BooleanProperty(False)
@@ -164,8 +173,8 @@ class FileChooserController(FloatLayout):
sort_func = ObjectProperty(alphanumeric_folders_first)
'''
:class:`~kivy.properties.ObjectProperty`.
- Provides a function to be called with a list of filenames as the only argument.
- Returns a list of filenames sorted for display in the view.
+ Provides a function to be called with a list of filenames as the only
+ argument. Returns a list of filenames sorted for display in the view.
'''
files = ListProperty([])
@@ -204,9 +213,9 @@ class FileChooserController(FloatLayout):
rootpath = StringProperty(None, allownone=True)
'''
Root path to use, instead of the system root path. If set, it will not show
- a ".." directory to go upper the root path. For example, if you set rootpath
- to /Users/foo, the user will be unable to go to /Users, or to any other
- directory not starting with /Users/foo.
+ a ".." directory to go upper the root path. For example, if you set
+ rootpath to /Users/foo, the user will be unable to go to /Users, or to any
+ other directory not starting with /Users/foo.
.. versionadded:: 1.2.0
@@ -222,7 +231,6 @@ class FileChooserController(FloatLayout):
:class:`FileChooserProgress`
'''
-
file_encodings = ListProperty(['utf-8', 'latin1', 'cp1252'])
'''Possible encodings for decoding a filename to unicode. In the case that
the user has a weird filename, undecodable without knowing it's
@@ -360,7 +368,10 @@ class FileChooserController(FloatLayout):
return files
filtered = []
for filter in self.filters:
- filtered.extend([fn for fn in files if fnmatch(fn, filter)])
+ if callable(filter):
+ filtered.extend([fn for fn in files if filter(self.path, fn)])
+ else:
+ filtered.extend([fn for fn in files if fnmatch(fn, filter)])
if not self.filter_dirs:
dirs = [fn for fn in files if isdir(fn)]
filtered.extend(dirs)
@@ -405,8 +416,8 @@ class FileChooserController(FloatLayout):
def _create_files_entries(self, *args):
# create maximum entries during 50ms max, or 10 minimum (slow system)
- # (on a "fast system" (core i7 2700K), we can create up to 40 entries in
- # 50 ms. So 10 is fine for low system.
+ # (on a "fast system" (core i7 2700K), we can create up to 40 entries
+ # in 50 ms. So 10 is fine for low system.
start = time()
finished = False
index = total = count = 1
@@ -447,16 +458,17 @@ class FileChooserController(FloatLayout):
return False
def cancel(self, *largs):
- '''Cancel any background action started by filechooser, such as loading a
- new directory.
+ '''Cancel any background action started by filechooser, such as loading
+ a new directory.
.. versionadded:: 1.2.0
'''
Clock.unschedule(self._create_files_entries)
self._hide_progress()
if len(self._previous_path) > 1:
- # if we cancel any action, the path will be set same as the previous
- # one, so we can safely cancel the update of the previous path.
+ # if we cancel any action, the path will be set same as the
+ # previous one, so we can safely cancel the update of the previous
+ # path.
self.path = self._previous_path[-2]
Clock.unschedule(self._update_files)
@@ -558,8 +570,8 @@ class FileChooserController(FloatLayout):
yield index, total, entry
def _force_unicode(self, s):
- # the idea is, whatever is the filename, unicode or str, even if the str
- # can't be directly returned as a unicode, return something.
+ # the idea is, whatever is the filename, unicode or str, even if the
+ # str can't be directly returned as a unicode, return something.
if type(s) is unicode:
return s
encodings = self.file_encodings
diff --git a/kivy/uix/floatlayout.py b/kivy/uix/floatlayout.py
index c15d11779..3e449c428 100644
--- a/kivy/uix/floatlayout.py
+++ b/kivy/uix/floatlayout.py
@@ -15,28 +15,27 @@ The :class:`FloatLayout` class will only honor the :data:`Widget.pos_hint` and
.. image:: images/floatlayout.png
:align: right
-For example, if you create a FloatLayout with size of (300, 300)::
+For example, if you create a FloatLayout with size a of (300, 300)::
layout = FloatLayout(size=(300, 300))
- # By default, all widgets have size_hint=(1, 1)
- # So this button will have the same size as
- # the layout
+By default, all widgets have size_hint=(1, 1), so this button will have the
+same size as the layout::
+
button = Button(text='Hello world')
layout.add_widget(button)
- # To create a button of 50% width and 25%
- # height of the layout and positioned at
- # 20, 20, you can do
+To create a button of 50% width and 25% height of the layout and positioned at
+(20, 20), you can do::
+
button = Button(
text='Hello world',
size_hint=(.5, .25),
pos=(20, 20))
-::
+If you want to create a button that will always be the size of layout minus
+20% on each side::
- # If you want to create a button that will always be the size of layout
- # minus 20% on each side:
button = Button(text='Hello world', size_hint=(.6, .6),
pos_hint={'x':.2, 'y':.2})
@@ -53,7 +52,7 @@ For example, if you create a FloatLayout with size of (300, 300)::
'''
-__all__ = ('FloatLayout', 'RelativeFloatLayout')
+__all__ = ('FloatLayout', )
from kivy.uix.layout import Layout
@@ -66,11 +65,11 @@ class FloatLayout(Layout):
kwargs.setdefault('size', (1, 1))
super(FloatLayout, self).__init__(**kwargs)
self.bind(
- children = self._trigger_layout,
- pos = self._trigger_layout,
- pos_hint = self._trigger_layout,
- size_hint = self._trigger_layout,
- size = self._trigger_layout)
+ children=self._trigger_layout,
+ pos=self._trigger_layout,
+ pos_hint=self._trigger_layout,
+ size_hint=self._trigger_layout,
+ size=self._trigger_layout)
def do_layout(self, *largs):
# optimization, until the size is 1, 1, don't do layout
@@ -95,10 +94,14 @@ class FloatLayout(Layout):
c.x = x + value * w
elif key == 'right':
c.right = x + value * w
+ elif key == 'pos':
+ c.pos = x + value[0] * w, y + value[1] * h
elif key == 'y':
c.y = y + value * h
elif key == 'top':
c.top = y + value * h
+ elif key == 'center':
+ c.center = x + value[0] * w, y + value[1] * h
elif key == 'center_x':
c.center_x = x + value * w
elif key == 'center_y':
@@ -106,17 +109,16 @@ class FloatLayout(Layout):
def add_widget(self, widget, index=0):
widget.bind(
- size = self._trigger_layout,
- size_hint = self._trigger_layout,
- pos = self._trigger_layout,
- pos_hint = self._trigger_layout)
+ size=self._trigger_layout,
+ size_hint=self._trigger_layout,
+ pos=self._trigger_layout,
+ pos_hint=self._trigger_layout)
return super(Layout, self).add_widget(widget, index)
def remove_widget(self, widget):
widget.unbind(
- size = self._trigger_layout,
- size_hint = self._trigger_layout,
- pos = self._trigger_layout,
- pos_hint = self._trigger_layout)
+ size=self._trigger_layout,
+ size_hint=self._trigger_layout,
+ pos=self._trigger_layout,
+ pos_hint=self._trigger_layout)
return super(Layout, self).remove_widget(widget)
-
diff --git a/kivy/uix/image.py b/kivy/uix/image.py
index e798c8547..7abbac3fb 100644
--- a/kivy/uix/image.py
+++ b/kivy/uix/image.py
@@ -2,7 +2,7 @@
Image
=====
-The :class:`Image` widget is used to display an image. ::
+The :class:`Image` widget is used to display an image::
wimg = Image(source='mylogo.png')
@@ -10,7 +10,7 @@ Asynchronous Loading
--------------------
To load an image asynchronously (for example from an external webserver), use
-the :class:`AsyncImage` subclass ::
+the :class:`AsyncImage` subclass::
aimg = AsyncImage(source='http://mywebsite.com/logo.png')
@@ -21,12 +21,12 @@ By default, the image is centered and fit inside the widget bounding box.
If you don't want that, you can inherit from Image and create your own style.
For example, if you want your image to take the same size of your widget, you
-can do ::
+can do::
class FullImage(Image):
pass
-And in your kivy language file, you can do ::
+And in your kivy language file, you can do::
:
canvas:
@@ -137,6 +137,16 @@ class Image(Widget):
default to True
'''
+ keep_data = BooleanProperty(False)
+ '''If true the underlaying _coreimage have to keep the raw image data.
+ Useful to perform pixel based collision detection
+
+ .. versionadded:: 1.3.0
+
+ :data:`keep_ratio` is a :class:`~kivy.properties.BooleanProperty`, default
+ to False
+ '''
+
anim_delay = NumericProperty(.25)
'''Delay of animation if the image is sequenced (like an animated gif).
If the anim_delay is set to -1, the animation will be stopped.
@@ -205,7 +215,7 @@ class Image(Widget):
if self._coreimage is not None:
self._coreimage.unbind(on_texture=self._on_tex_change)
self._coreimage = ci = CoreImage(filename, mipmap=mipmap,
- anim_delay=self.anim_delay)
+ anim_delay=self.anim_delay, keep_data=self.keep_data)
ci.bind(on_texture=self._on_tex_change)
self.texture = ci.texture
diff --git a/kivy/uix/label.py b/kivy/uix/label.py
index 8803223fd..b73239333 100644
--- a/kivy/uix/label.py
+++ b/kivy/uix/label.py
@@ -3,7 +3,7 @@ Label
=====
The :class:`Label` widget is for rendering text. It supports ascii and unicode
-strings ::
+strings::
# hello world text
l = Label(text='Hello world')
@@ -78,7 +78,7 @@ to detect when the user clicks on part of the text, and to react.
The tag ``[ref=xxx]`` is used for that.
In this example, we are creating a reference on the word "World". When a click
-happens on it, the function ``print_it`` will be called with the name of the
+happens on it, the function ``print_it`` will be called with the name of the
reference::
def print_it(instance, value):
@@ -211,11 +211,11 @@ class Label(Widget):
text = StringProperty('')
'''Text of the label.
- Creation of a simple hello world ::
+ Creation of a simple hello world::
widget = Label(text='Hello world')
- If you want to create the widget with an unicode string, use ::
+ If you want to create the widget with an unicode string, use::
widget = Label(text=u'My unicode string')
@@ -354,7 +354,7 @@ class Label(Widget):
texture = ObjectProperty(None, allownone=True)
'''Texture object of the text.
The text is rendered automatically when a property changes. The OpenGL texture
- created in this operation is stored in this property. You can use this
+ created in this operation is stored in this property. You can use this
:data:`texture` for any graphics elements.
Depending on the texture creation, the value will be a
@@ -365,7 +365,7 @@ class Label(Widget):
The :data:`texture` update is scheduled for the next frame. If you need
the texture immediately after changing a property, you have to call
- the :func:`texture_update` function before accessing :data:`texture` ::
+ the :func:`texture_update` function before accessing :data:`texture`::
l = Label(text='Hello world')
# l.texture is good
@@ -467,7 +467,7 @@ class Label(Widget):
[anchor=content]Hello world
"""
- Then, all the ``[anchor=]`` references will be removed, and you'll get all
+ Then, all the ``[anchor=]`` references will be removed, and you'll get all
the anchor positions in this property (only after rendering)::
>>> widget = Label(text=text, markup=True)
@@ -481,4 +481,3 @@ class Label(Widget):
True.
'''
-
diff --git a/kivy/uix/layout.py b/kivy/uix/layout.py
index 000497d19..ee8490753 100644
--- a/kivy/uix/layout.py
+++ b/kivy/uix/layout.py
@@ -20,7 +20,7 @@ size in percent, not in pixels. The format is::
widget.size_hint = (width_percent, height_percent)
-The percent is specified as a floating point number in the range 0-1. For
+The percent is specified as a floating point number in the range 0-1. For
example, 0.5 is 50%, 1 is 100%.
If you want a widget's width to be half of the parent's width and the
diff --git a/kivy/uix/modalview.py b/kivy/uix/modalview.py
new file mode 100644
index 000000000..1ed15af5b
--- /dev/null
+++ b/kivy/uix/modalview.py
@@ -0,0 +1,264 @@
+'''
+ModalView
+=========
+
+.. versionadded:: 1.4.0
+
+The :class:`ModalView` widget is used to create modal views. By default, the
+view will cover the whole "parent" window.
+
+Remember that the default size of a Widget is size_hint=(1, 1). If you don't
+want your view to be fullscreen, deactivate the size_hint and use a specific
+size attribute.
+
+Examples
+--------
+
+Example of a simple 400x400 Hello world view::
+
+ view = ModalView(size_hint=(None, None), size=(400, 400))
+ view.add_widget(Label(text='Hello world'))
+
+By default, any click outside the view will dismiss it. If you don't
+want that, you can set :data:`ModalView.auto_dismiss` to False::
+
+ view = ModalView(auto_dismiss=False)
+ view.add_widget(Label(text='Hello world'))
+ view.open()
+
+To manually dismiss/close the view, use :meth:`ModalView.dismiss`::
+
+ ModalView.dismiss()
+
+The :meth:`ModalView.open` and :meth:`ModalView.dismiss` are bindable. That
+means you can directly bind the function to an action, e.g., to a button's
+on_press ::
+
+ # create content and assign to the view
+ content = Button(text='Close me!')
+ view = ModalView(auto_dismiss=False)
+ view.add_widget(content)
+
+ # bind the on_press event of the button to the dismiss function
+ content.bind(on_press=view.dismiss)
+
+ # open the view
+ view.open()
+
+
+ModalView Events
+----------------
+
+There are two events available: `on_open` when the view is opening, and
+`on_dismiss` when it is closed. For `on_dismiss`, you can prevent the
+view from closing by explictly returning True from your callback ::
+
+ def my_callback(instance):
+ print 'ModalView', instance, 'is being dismissed, but is prevented!'
+ return True
+ view = ModalView()
+ view.add_widget(Label(text='Hello world'))
+ view.bind(on_dismiss=my_callback)
+ view.open()
+
+'''
+
+__all__ = ('ModalView', )
+
+from kivy.logger import Logger
+from kivy.animation import Animation
+from kivy.uix.anchorlayout import AnchorLayout
+from kivy.properties import StringProperty, BooleanProperty, ObjectProperty, \
+ NumericProperty, ListProperty
+
+
+class ModalView(AnchorLayout):
+ '''ModalView class. See module documentation for more information.
+
+ :Events:
+ `on_open`:
+ Fired when the ModalView is opened
+ `on_dismiss`:
+ Fired when the ModalView is closed. If the callback returns True,
+ the dismiss will be canceled.
+ '''
+
+ auto_dismiss = BooleanProperty(True)
+ '''Default to True, this property determines if the view is automatically
+ dismissed when the user clicks outside it.
+
+ :data:`auto_dismiss` is a :class:`~kivy.properties.BooleanProperty`,
+ default to True.
+ '''
+
+ attach_to = ObjectProperty(None)
+ '''If a widget is set on attach_to, the view will attach to the nearest
+ parent window of the widget. If none is found, it will attach to the
+ main/global Window.
+
+ :data:`attach_to` is a :class:`~kivy.properties.ObjectProperty`, default to
+ None.
+ '''
+
+ background_color = ListProperty([0, 0, 0, .7])
+ '''Background color, in the format (r, g, b, a).
+
+ :data:`background_color` is a :class:`~kivy.properties.ListProperty`,
+ default to [0, 0, 0, .7].
+ '''
+
+ background = StringProperty(
+ 'atlas://data/images/defaulttheme/modalview-background')
+ '''Background image of the view used for the view background.
+
+ :data:`background` is an :class:`~kivy.properties.StringProperty`,
+ default to 'atlas://data/images/defaulttheme/modalview-background'
+ '''
+
+ border = ListProperty([16, 16, 16, 16])
+ '''Border used for :class:`~kivy.graphics.vertex_instructions.BorderImage`
+ graphics instruction. Used for :data:`background_normal` and
+ :data:`background_down`. Can be used when using custom background.
+
+ It must be a list of four values: (top, right, bottom, left). Read the
+ BorderImage instructions for more information about how to use it.
+
+ :data:`border` is a :class:`~kivy.properties.ListProperty`, default to (16,
+ 16, 16, 16)
+ '''
+
+ # Internals properties used for graphical representation.
+
+ _anim_alpha = NumericProperty(0)
+
+ _anim_duration = NumericProperty(.100)
+
+ _window = ObjectProperty(None, allownone=True)
+
+ def __init__(self, **kwargs):
+ self.register_event_type('on_open')
+ self.register_event_type('on_dismiss')
+ self._parent = None
+ super(ModalView, self).__init__(**kwargs)
+
+ def _search_window(self):
+ # get window to attach to
+ window = None
+ if self.attach_to is not None:
+ window = self.attach_to.get_parent_window()
+ if not window:
+ window = self.attach_to.get_root_window()
+ if not window:
+ from kivy.core.window import Window
+ window = Window
+ return window
+
+ def open(self, *largs):
+ '''Show the view window from the :data:`attach_to` widget. If set, it
+ will attach to the nearest window. If the widget is not attached to any
+ window, the view will attach to the global
+ :class:`~kivy.core.window.Window`.
+ '''
+ # search window
+ self._window = self._search_window()
+ if not self._window:
+ Logger.warning('ModalView: cannot open view, no window found.')
+ return self
+ self._window.add_widget(self)
+ self._window.bind(on_resize=self._align_center)
+ self.center = self._window.center
+ Animation(_anim_alpha=1., d=self._anim_duration).start(self)
+ self.dispatch('on_open')
+ return self
+
+ def dismiss(self, *largs, **kwargs):
+ '''Close the view if it is open. If you really want to close the
+ view, whatever the on_dismiss event returns, you can do this:
+ ::
+
+ view = ModalView(...)
+ view.dismiss(force=True)
+
+ When the view is dismissed, it will be faded out, before
+ removal from the parent. If you don't want animation, use:
+
+ view.dismiss(animation=False)
+
+ '''
+ if self._window is None:
+ return self
+ if self.dispatch('on_dismiss') is True:
+ if kwargs.get('force', False) is not True:
+ return self
+ if kwargs.get('animation', True):
+ Animation(_anim_alpha=0., d=self._anim_duration).start(self)
+ else:
+ self._anim_alpha = 0
+ return self
+
+ def on_size(self, instance, value):
+ self._align_center()
+
+ def _align_center(self, *l):
+ if self._window:
+ self.center = self._window.center
+ # hack to resize dark background on window resize
+ _window = self._window
+ self._window = None
+ self._window = _window
+
+ def on_touch_down(self, touch):
+ if not self.collide_point(*touch.pos):
+ if self.auto_dismiss:
+ self.dismiss()
+ return True
+ super(ModalView, self).on_touch_down(touch)
+ return True
+
+ def on_touch_move(self, touch):
+ super(ModalView, self).on_touch_move(touch)
+ return True
+
+ def on_touch_up(self, touch):
+ super(ModalView, self).on_touch_up(touch)
+ return True
+
+ def on__anim_alpha(self, instance, value):
+ if value == 0 and self._window is not None:
+ self._window.remove_widget(self)
+ self._window.unbind(on_resize=self._align_center)
+ self._window = None
+
+ def on_open(self):
+ pass
+
+ def on_dismiss(self):
+ pass
+
+
+if __name__ == '__main__':
+ from kivy.base import runTouchApp
+ from kivy.uix.button import Button
+ from kivy.uix.label import Label
+ from kivy.uix.gridlayout import GridLayout
+ from kivy.core.window import Window
+
+ # add view
+ content = GridLayout(cols=1)
+ content.add_widget(Label(text='This is a hello world'))
+ view = ModalView(size_hint=(None, None), size=(256, 256), auto_dismiss=True)
+ view.add_widget(content)
+
+ def open_view(btn):
+ view.open()
+
+ layout = GridLayout(cols=3)
+ for x in xrange(9):
+ btn = Button(text='click me %s' % x)
+ btn.bind(on_release=view.open)
+ layout.add_widget(btn)
+ Window.add_widget(layout)
+
+ view.open()
+
+ runTouchApp()
diff --git a/kivy/uix/popup.py b/kivy/uix/popup.py
index a5571c264..1d5dfcc8d 100644
--- a/kivy/uix/popup.py
+++ b/kivy/uix/popup.py
@@ -15,6 +15,11 @@ Remember that the default size of a Widget is size_hint=(1, 1). If you don't
want your popup to be fullscreen, deactivate the size_hint and use a specific
size attribute.
+
+.. versionchanged:: 1.4.0
+ The :class:`Popup` class now inherits from :class:`~kivy.uix.modalview.ModalView`.
+ The :class:`Popup` offers a default layout with a title and a separation bar.
+
Examples
--------
@@ -36,7 +41,7 @@ To manually dismiss/close the popup, use :meth:`Popup.dismiss`::
popup.dismiss()
The :meth:`Popup.open` and :meth:`Popup.dismiss` are bindable. That means you
-can directly bind the function to an action, e.g., to a button's on_press ::
+can directly bind the function to an action, e.g., to a button's on_press::
# create content and assign to the popup
content = Button(text='Close me!')
@@ -54,7 +59,7 @@ Popup Events
There are two events available: `on_open` when the popup is opening, and
`on_dismiss` when it is closed. For `on_dismiss`, you can prevent the
-popup from closing by explictly returning True from your callback ::
+popup from closing by explictly returning True from your callback::
def my_callback(instance):
print 'Popup', instance, 'is being dismissed, but is prevented!'
@@ -67,11 +72,9 @@ popup from closing by explictly returning True from your callback ::
__all__ = ('Popup', 'PopupException')
-from kivy.logger import Logger
-from kivy.animation import Animation
-from kivy.uix.floatlayout import FloatLayout
-from kivy.properties import StringProperty, BooleanProperty, ObjectProperty, \
- NumericProperty, ListProperty
+from kivy.uix.modalview import ModalView
+from kivy.properties import (StringProperty, ObjectProperty,
+ NumericProperty, ListProperty)
class PopupException(Exception):
@@ -81,7 +84,7 @@ class PopupException(Exception):
'''
-class Popup(FloatLayout):
+class Popup(ModalView):
'''Popup class. See module documentation for more information.
:Events:
@@ -99,21 +102,12 @@ class Popup(FloatLayout):
title'.
'''
- auto_dismiss = BooleanProperty(True)
- '''Default to True, this property determines if the popup is automatically
- dismissed when the user clicks outside it.
+ background = StringProperty(
+ 'atlas://data/images/defaulttheme/popup-background')
+ '''Background image of the view used for the view background.
- :data:`auto_dismiss` is a :class:`~kivy.properties.BooleanProperty`, default
- to True.
- '''
-
- attach_to = ObjectProperty(None)
- '''If a widget is set on attach_to, the popup will attach to the nearest
- parent window of the widget. If none is found, it will attach to the
- main/global Window.
-
- :data:`attach_to` is a :class:`~kivy.properties.ObjectProperty`, default to
- None.
+ :data:`background` is an :class:`~kivy.properties.StringProperty`,
+ default to 'atlas://data/images/defaulttheme/popup-background'
'''
content = ObjectProperty(None)
@@ -123,39 +117,6 @@ class Popup(FloatLayout):
None.
'''
- background_color = ListProperty([0, 0, 0, .7])
- '''Background color, in the format (r, g, b, a).
-
- .. versionadded:: 1.1.0
-
- :data:`background_color` is a :class:`~kivy.properties.ListProperty`,
- default to [0, 0, 0, .7].
- '''
-
- background = StringProperty(
- 'atlas://data/images/defaulttheme/popup-background')
- '''Background image of the popup used for the popup background.
-
- .. versionadded:: 1.1.0
-
- :data:`background` is an :class:`~kivy.properties.StringProperty`,
- default to 'atlas://data/images/defaulttheme/popup-background'
- '''
-
- border = ListProperty([16, 16, 16, 16])
- '''Border used for :class:`~kivy.graphics.vertex_instructions.BorderImage`
- graphics instruction. Used for :data:`background_normal` and
- :data:`background_down`. Can be used when using custom background.
-
- .. versionadded:: 1.1.0
-
- It must be a list of four values: (top, right, bottom, left). Read the
- BorderImage instructions for more information about how to use it.
-
- :data:`border` is a :class:`~kivy.properties.ListProperty`, default to (16,
- 16, 16, 16)
- '''
-
separator_color = ListProperty([47 / 255., 167 / 255., 212 / 255., 1.])
'''Color used by the separator between title and content.
@@ -178,30 +139,6 @@ class Popup(FloatLayout):
_container = ObjectProperty(None)
- _anim_alpha = NumericProperty(0)
-
- _anim_duration = NumericProperty(.100)
-
- _window = ObjectProperty(None, allownone=True)
-
- def __init__(self, **kwargs):
- self.register_event_type('on_open')
- self.register_event_type('on_dismiss')
- self._parent = None
- super(Popup, self).__init__(**kwargs)
-
- def _search_window(self):
- # get window to attach to
- window = None
- if self.attach_to is not None:
- window = self.attach_to.get_parent_window()
- if not window:
- window = self.attach_to.get_root_window()
- if not window:
- from kivy.core.window import Window
- window = Window
- return window
-
def add_widget(self, widget):
if self._container:
if self.content:
@@ -210,84 +147,6 @@ class Popup(FloatLayout):
else:
super(Popup, self).add_widget(widget)
- def open(self, *largs):
- '''Show the popup window from the :data:`attach_to` widget. If set, it
- will attach to the nearest window. If the widget is not attached to any
- window, the popup will attach to the global
- :class:`~kivy.core.window.Window`.
- '''
- # search window
- self._window = self._search_window()
- if not self._window:
- Logger.warning('Popup: cannot open popup, no window found.')
- return self
- self._window.add_widget(self)
- self._window.bind(on_resize=self._align_center)
- self.center = self._window.center
- Animation(_anim_alpha=1., d=self._anim_duration).start(self)
- self.dispatch('on_open')
- return self
-
- def dismiss(self, *largs, **kwargs):
- '''Close the popup if it is open. If you really want to close the
- popup, whatever the on_dismiss event returns, you can do this:
- ::
-
- popup = Popup(...)
- popup.dismiss(force=True)
-
- .. versionchanged:: 1.3.0
-
- When the popup is dismissed, it will be faded out, before
- removal from the parent. If you don't want animation, use:
-
- popup.dismiss(animation=False)
-
- '''
- if self._window is None:
- return self
- if self.dispatch('on_dismiss') is True:
- if kwargs.get('force', False) is not True:
- return self
- if kwargs.get('animation', True):
- Animation(_anim_alpha=0., d=self._anim_duration).start(self)
- else:
- self._anim_alpha = 0
- return self
-
- def on_size(self, instance, value):
- self._align_center()
-
- def _align_center(self, *l):
- if self._window:
- self.center = self._window.center
- # hack to resize dark background on window resize
- _window = self._window
- self._window = None
- self._window = _window
-
- def on_touch_down(self, touch):
- if not self.collide_point(*touch.pos):
- if self.auto_dismiss:
- self.dismiss()
- return True
- super(Popup, self).on_touch_down(touch)
- return True
-
- def on_touch_move(self, touch):
- super(Popup, self).on_touch_move(touch)
- return True
-
- def on_touch_up(self, touch):
- super(Popup, self).on_touch_up(touch)
- return True
-
- def on__anim_alpha(self, instance, value):
- if value == 0 and self._window is not None:
- self._window.remove_widget(self)
- self._window.unbind(on_resize=self._align_center)
- self._window = None
-
def on_content(self, instance, value):
if not hasattr(value, 'popup'):
value.create_property('popup')
@@ -302,12 +161,6 @@ class Popup(FloatLayout):
self._container.clear_widgets()
self._container.add_widget(self.content)
- def on_open(self):
- pass
-
- def on_dismiss(self):
- pass
-
if __name__ == '__main__':
from kivy.base import runTouchApp
@@ -316,12 +169,6 @@ if __name__ == '__main__':
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
- layout = GridLayout(cols=3)
- for x in xrange(9):
- btn = Button(text=str(x))
- layout.add_widget(btn)
- Window.add_widget(layout)
-
# add popup
content = GridLayout(cols=1)
content_cancel = Button(text='Cancel', size_hint_y=None, height=40)
@@ -331,7 +178,14 @@ if __name__ == '__main__':
size_hint=(None, None), size=(256, 256),
content=content)
content_cancel.bind(on_release=popup.dismiss)
- btn.bind(on_release=popup.open)
+
+ layout = GridLayout(cols=3)
+ for x in xrange(9):
+ btn = Button(text=str(x))
+ btn.bind(on_release=popup.open)
+ layout.add_widget(btn)
+
+ Window.add_widget(layout)
popup.open()
diff --git a/kivy/uix/progressbar.py b/kivy/uix/progressbar.py
index 943348f92..f7ef59be0 100644
--- a/kivy/uix/progressbar.py
+++ b/kivy/uix/progressbar.py
@@ -12,7 +12,7 @@ Only horizontal mode is supported, vertical mode is not available yet.
The progress bar has no interactive elements, It is a display-only widget.
-To use it, simply assign a value to indicate the current progress ::
+To use it, simply assign a value to indicate the current progress::
from kivy.uix.progressbar import ProgressBar
pb = ProgressBar(max=1000)
diff --git a/kivy/uix/relativelayout.py b/kivy/uix/relativelayout.py
index 79f08a357..f929ffc6f 100644
--- a/kivy/uix/relativelayout.py
+++ b/kivy/uix/relativelayout.py
@@ -7,24 +7,24 @@ Relative Layout
This layout allows you to set relative coordinate for children. If you want
absolute positioning, check :class:`~kivy.uix.floatlayout.FloatLayout`.
-The :class:`RelativeFloatLayout` class behaves just like the regular Float
+The :class:`RelativeLayout` class behaves just like the regular Float
Layout, except that its child widgets are positioned relative to the layout.
-For example, if you create a RelativeFloatLayout, add a widgets with
+For example, if you create a RelativeLayout, add a widgets with
position = (0,0), the child widget will also move, when you change the
-position of the RelativeFloatLayout. The child widgets coordiantes remain
+position of the RelativeLayout. The child widgets coordiantes remain
(0,0), i.e. they are relative to the containing layout.
..note::
- The :class:`RelativeFloatLayout` is implemented as a :class`FloatLayout`
+ The :class:`RelativeLayout` is implemented as a :class`FloatLayout`
inside a :class:`Scatter`.
.. warning::
- Since the actual RelativeFloatLayout is a Scatter, its add_widget and
+ Since the actual RelativeLayout is a Scatter, its add_widget and
remove_widget functions are overwritten to add children to the embedded
- FloatLayout (accessible as `content` property of RelativeFloatLayout)
+ FloatLayout (accessible as `content` property of RelativeLayout)
automatically. So if you want to access the added child elements,
you need self.content.children, instead of self.children.
'''
diff --git a/kivy/uix/rst.py b/kivy/uix/rst.py
index 1763c4654..795f5af57 100644
--- a/kivy/uix/rst.py
+++ b/kivy/uix/rst.py
@@ -146,6 +146,7 @@ Builder.load_string('''
:
markup: True
+ valign: 'top'
font_size: 24 - self.section * 2
size_hint_y: None
height: self.texture_size[1] + 20
@@ -162,6 +163,7 @@ Builder.load_string('''
:
markup: True
+ valign: 'top'
size_hint_y: None
height: self.texture_size[1] + self.my
text_size: self.width - self.mx, None
@@ -174,6 +176,7 @@ Builder.load_string('''
id: label
text: root.text
markup: True
+ valign: 'top'
size_hint: None, None
size: self.texture_size[0] + 10, self.texture_size[1] + 10
@@ -210,6 +213,7 @@ Builder.load_string('''
Label:
id: content
markup: True
+ valign: 'top'
text_size: self.width - 20, None
font_name: 'data/fonts/DroidSansMono.ttf'
color: (0, 0, 0, 1)
@@ -296,6 +300,7 @@ Builder.load_string('''
:
markup: True
+ valign: 'top'
size_hint: None, 1
color: (0, 0, 0, 1)
bold: True
@@ -343,6 +348,7 @@ Builder.load_string('''
:
markup: True
+ valign: 'top'
size_hint_x: None
width: self.texture_size[0] + 10
text_size: None, self.height - 10
@@ -431,8 +437,8 @@ class RstDocument(ScrollView):
when a rst document is explicitly loaded, or where :func:`preload` has been
called.
- If the document has no filename, e.g., when the document is loaded from a text file,
- the key will be ''.
+ If the document has no filename, e.g., when the document is loaded from a
+ text file, the key will be ''.
:data:`toctrees` is a :class:`~kivy.properties.DictProperty`, default to {}.
'''
@@ -541,10 +547,10 @@ class RstDocument(ScrollView):
.. note::
- It is preferable to delay the call of the goto if you just loaded the
- document, because the layout might not be finished, or if the size
- of the RstDocument is not fixed yet, then the calculation of the
- scrolling would be wrong.
+ It is preferable to delay the call of the goto if you just loaded
+ the document, because the layout might not be finished, or if the
+ size of the RstDocument is not fixed yet, then the calculation of
+ the scrolling would be wrong.
However, you can do a direct call if the document is already loaded.
@@ -687,6 +693,7 @@ class RstTransition(Widget):
class RstEmptySpace(Widget):
pass
+
class RstDefinitionSpace(Widget):
pass
diff --git a/kivy/uix/scatter.py b/kivy/uix/scatter.py
index d50002ec9..ebd86a8fe 100644
--- a/kivy/uix/scatter.py
+++ b/kivy/uix/scatter.py
@@ -29,7 +29,7 @@ Usage
By default, the widget does not have a graphical representation. It is a
container only. The idea is to combine Scatter with another widget, for
-example :class:`~kivy.uix.image.Image` ::
+example :class:`~kivy.uix.image.Image`::
scatter = Scatter()
image = Image(source='sun.jpg')
@@ -41,15 +41,15 @@ Control Interactions
By default, all interactions are enabled. You can selectively disable
them using the do_{rotation, translation, scale} properties.
-Disable rotation ::
+Disable rotation::
scatter = Scatter(do_rotation=False)
-Allow only translation ::
+Allow only translation::
scatter = Scatter(do_rotation=False, do_scale=False)
-Allow only translation on x axis ::
+Allow only translation on x axis::
scatter = Scatter(do_rotation=False, do_scale=False,
do_translation_y=False)
@@ -72,7 +72,7 @@ a limit for scaling. You cannot do infinite scale down/up with our
implementation. Generally, you don't hit the minimum scale (because you don't
see it on the screen), but the maximum scale is 9.99506983235e+19 (2^66).
-You can also limit the minimum and maximum scale allowed. ::
+You can also limit the minimum and maximum scale allowed::
scatter = Scatter(scale_min=.5, scale_max=3.)
@@ -80,7 +80,6 @@ Behaviors
---------
.. versionchanged:: 1.1.0
-
If no control interactions are enabled, then touch handler will never return
True.
@@ -204,7 +203,7 @@ class Scatter(Widget):
return (xmin, ymin), (xmax-xmin, ymax-ymin)
bbox = AliasProperty(_get_bbox, None, bind=(
'transform', 'width', 'height'))
- '''Bounding box of the widget in parent space. ::
+ '''Bounding box of the widget in parent space::
((x, y), (w, h))
# x, y = lower left corner
diff --git a/kivy/uix/screenmanager.py b/kivy/uix/screenmanager.py
index 2de9a7066..78352aabf 100644
--- a/kivy/uix/screenmanager.py
+++ b/kivy/uix/screenmanager.py
@@ -118,7 +118,7 @@ __all__ = ('Screen', 'ScreenManager', 'ScreenManagerException',
from kivy.event import EventDispatcher
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty, ObjectProperty, \
- NumericProperty, ListProperty, OptionProperty
+ NumericProperty, ListProperty, OptionProperty, BooleanProperty
from kivy.animation import Animation, AnimationTransition
from kivy.uix.relativelayout import RelativeLayout
from kivy.lang import Builder
@@ -151,7 +151,7 @@ class Screen(RelativeLayout):
None, read-only.
'''
- transition_alpha = NumericProperty(0.)
+ transition_progress = NumericProperty(0.)
'''Value that represent the completion of the current transition, if any is
occuring.
@@ -159,7 +159,7 @@ class Screen(RelativeLayout):
to 1. If you want to know if it's an entering or leaving animation, check
the :data:`transition_state`
- :data:`transition_alpha` is a :class:`~kivy.properties.NumericProperty`,
+ :data:`transition_progress` is a :class:`~kivy.properties.NumericProperty`,
default to 0.
'''
@@ -222,6 +222,13 @@ class TransitionBase(EventDispatcher):
None, read-only.
'''
+ is_active = BooleanProperty()
+ '''Indicate if the transition is currently active
+
+ :data:`is_active` is a :class:`~kivy.properties.BooleanProperty`, default to
+ False, read-only.
+ '''
+
# privates
_anim = ObjectProperty(allownone=True)
@@ -241,11 +248,12 @@ class TransitionBase(EventDispatcher):
on_complete=self._on_complete)
self.add_screen(self.screen_in)
- self.screen_in.transition_value = 0.
+ self.screen_in.transition_progress = 0.
self.screen_in.transition_mode = 'in'
- self.screen_out.transition_value = 0.
+ self.screen_out.transition_progress = 0.
self.screen_out.transition_mode = 'out'
+ self.is_active = True
self._anim.start(self)
self.dispatch('on_progress', 0)
@@ -257,6 +265,7 @@ class TransitionBase(EventDispatcher):
self._anim.cancel(self)
self.dispatch('on_complete')
self._anim = None
+ self.is_active = False
def add_screen(self, screen):
'''(internal) Used to add a screen into the :class:`ScreenManager`
@@ -275,12 +284,13 @@ class TransitionBase(EventDispatcher):
pass
def _on_progress(self, *l):
- alpha = l[-1]
- self.screen_in.transition_value = alpha
- self.screen_out.transition_value = 1. - alpha
- self.dispatch('on_progress', alpha)
+ progress = l[-1]
+ self.screen_in.transition_progress = progress
+ self.screen_out.transition_progress = 1. - progress
+ self.dispatch('on_progress', progress)
def _on_complete(self, *l):
+ self.is_active = False
self.dispatch('on_complete')
self._anim = None
@@ -407,8 +417,8 @@ class SlideTransition(TransitionBase):
a.y = y - height * (1 - progression)
def on_complete(self):
- self.screen_in.pos = (0, 0)
- self.screen_out.pos = (0, 0)
+ self.screen_in.pos = self.manager.pos
+ self.screen_out.pos = self.manager.pos
super(SlideTransition, self).on_complete()
@@ -423,8 +433,8 @@ class SwapTransition(TransitionBase):
def on_complete(self):
self.screen_in.scale = 1.
self.screen_out.scale = 1.
- self.screen_in.pos = (0, 0)
- self.screen_out.pos = (0, 0)
+ self.screen_in.pos = self.manager.pos
+ self.screen_out.pos = self.manager.pos
super(SwapTransition, self).on_complete()
def on_progress(self, progression):
@@ -503,7 +513,7 @@ class ScreenManager(FloatLayout):
current = StringProperty(None)
'''Name of the screen currently show, or the screen to show.
- ::
+ ::
from kivy.uix.screenmanager import ScreenManager, Screen
@@ -550,6 +560,10 @@ class ScreenManager(FloatLayout):
default to None, read-only.
'''
+ def __init__(self, **kwargs):
+ super(ScreenManager, self).__init__(**kwargs)
+ self.bind(pos=self._update_pos)
+
def add_widget(self, screen):
if not isinstance(screen, Screen):
raise ScreenManagerException(
@@ -584,6 +598,7 @@ class ScreenManager(FloatLayout):
self.transition.screen_out = previous_screen
self.transition.start(self)
else:
+ screen.pos = self.pos
self.real_add_widget(screen)
def get_screen(self, name):
@@ -620,6 +635,14 @@ class ScreenManager(FloatLayout):
except ValueError:
return
+ def _update_pos(self, instance, value):
+ for child in self.children:
+ if self.transition.is_active and \
+ (child == self.transition.screen_in or \
+ child == self.transition.screen_out):
+ continue
+ child.pos = value
+
if __name__ == '__main__':
from kivy.app import App
from kivy.uix.button import Button
diff --git a/kivy/uix/scrollview.py b/kivy/uix/scrollview.py
index 4fcef19ad..8a0e311e3 100644
--- a/kivy/uix/scrollview.py
+++ b/kivy/uix/scrollview.py
@@ -50,7 +50,7 @@ the size_hint instructions (x or y) of the child to enable scrolling.
To scroll a :class:`GridLayout` on Y-axis/vertically, set the child's width
identical to that of the ScrollView (size_hint_x=1, default), and set the
-size_hint_y property to None ::
+size_hint_y property to None::
layout = GridLayout(cols=1, spacing=10, size_hint_y=None)
#Make sure the height is such that there is something to scroll.
@@ -387,6 +387,11 @@ class ScrollView(StencilView):
else:
if self._touch is not touch:
super(ScrollView, self).on_touch_up(touch)
+
+ # if we do mouse scrolling, always accept it
+ if 'button' in touch.profile and touch.button.startswith('scroll'):
+ return True
+
return self._get_uid() in touch.ud
diff --git a/kivy/uix/slider.py b/kivy/uix/slider.py
index cd3693dfd..90e31f660 100644
--- a/kivy/uix/slider.py
+++ b/kivy/uix/slider.py
@@ -7,12 +7,12 @@ Slider
The :class:`Slider` widget looks like a scrollbar. It supports horizontal and
vertical orientation, min/max and a default value.
-To create a slider from -100 to 100 starting at 25 ::
+To create a slider from -100 to 100 starting at 25::
from kivy.uix.slider import Slider
s = Slider(min=-100, max=100, value=25)
-To create a vertical slider ::
+To create a vertical slider::
from kivy.uix.slider import Slider
s = Slider(orientation='vertical')
@@ -71,7 +71,7 @@ class Slider(Widget):
'''
range = ReferenceListProperty(min, max)
- '''Range of the slider, in the format (minimum value, maximum value). ::
+ '''Range of the slider, in the format (minimum value, maximum value)::
>>> slider = Slider(min=10, max=80)
>>> slider.range
@@ -98,7 +98,7 @@ class Slider(Widget):
self.value = value * (self.max - vmin) + vmin
value_normalized = AliasProperty(get_norm_value, set_norm_value,
bind=('value', 'min', 'max'))
- '''Normalized value inside the :data:`range` (min/max) to 0-1 range. ::
+ '''Normalized value inside the :data:`range` (min/max) to 0-1 range::
>>> slider = Slider(value=50, min=0, max=100)
>>> slider.value
@@ -113,7 +113,7 @@ class Slider(Widget):
1
You can also use it for setting the real value without knowing the minimum
- and maximum. ::
+ and maximum::
>>> slider = Slider(min=0, max=200)
>>> slider.value_normalized = .5
diff --git a/kivy/uix/spinner.py b/kivy/uix/spinner.py
new file mode 100644
index 000000000..2ed45a64f
--- /dev/null
+++ b/kivy/uix/spinner.py
@@ -0,0 +1,131 @@
+'''
+Spinner
+=======
+
+.. versionadded:: 1.4.0
+
+.. image:: images/spinner.jpg
+ :align: right
+
+Spinner is a widget that provide a quick way to select one value from a set. In
+the default state, a spinner show its currently selected value. Touching the
+spinner displays a dropdown menu with all other available values. from which the
+user can select a new one.
+
+Example::
+
+ from kivy.base import runTouchApp
+ from kivy.uix.spinner import Spinner
+
+ spinner = Spinner(
+ # default value showed
+ text='Home',
+ # available values
+ values=('Home', 'Work', 'Other', 'Custom'),
+ # just for positioning in our example
+ size_hint=(None, None),
+ size=(100, 44),
+ pos_hint={'center_x': .5, 'center_y': .5})
+
+ def show_selected_value(spinner, text):
+ print 'The spinner', spinner, 'have text', text
+
+ spinner.bind(text=show_selected_value)
+
+ runTouchApp(spinner)
+
+'''
+
+__all__ = ('Spinner', 'SpinnerOption')
+
+from kivy.properties import ListProperty, ObjectProperty
+from kivy.uix.button import Button
+from kivy.uix.dropdown import DropDown
+from kivy.lang import Builder
+
+
+Builder.load_string('''
+:
+ size_hint_y: None
+ height: 44
+
+:
+ background_normal: 'atlas://data/images/defaulttheme/spinner'
+ background_down: 'atlas://data/images/defaulttheme/spinner_pressed'
+''')
+
+
+class SpinnerOption(Button):
+ '''Special button used in the dropdown list. We just set the default
+ size_hint_y and height.
+ '''
+ pass
+
+
+class Spinner(Button):
+ '''Spinner class, see module documentation for more information
+ '''
+
+ values = ListProperty()
+ '''Values that can be selected by the user. It must be a list of strings.
+
+ :data:`values` is a :class:`~kivy.properties.ListProperty`, default to [].
+ '''
+
+ option_cls = ObjectProperty(SpinnerOption)
+ '''Class used to display the options within the dropdown list displayed
+ under the Spinner. The `text` property in the class will represent the
+ value.
+
+ The option class require at least:
+
+ - one `text` property where the value will be put
+ - one `on_release` event that you need to trigger when the option is
+ touched.
+
+ :data:`option_cls` is a :class:`~kivy.properties.ObjectProperty`, default
+ to :class:`SpinnerOption`.
+ '''
+
+ dropdown_cls = ObjectProperty(DropDown)
+ '''Class used to display the dropdown list when the Spinner is pressed.
+
+ :data:`dropdown_cls` is a :class:`~kivy.properties.ObjectProperty`, default
+ to :class:`~kivy.uix.dropdown.DropDown`.
+ '''
+
+ def __init__(self, **kwargs):
+ self._dropdown = None
+ super(Spinner, self).__init__(**kwargs)
+ self.bind(
+ on_release=self._open_dropdown,
+ dropdown_cls=self._build_dropdown,
+ option_cls=self._build_dropdown,
+ values=self._update_dropdown)
+ self._build_dropdown()
+
+ def _build_dropdown(self, *largs):
+ if self._dropdown:
+ self._dropdown.unbind(on_select=self._on_dropdown_select)
+ self._dropdown.dismiss()
+ self._dropdown = None
+ self._dropdown = self.dropdown_cls()
+ self._dropdown.bind(on_select=self._on_dropdown_select)
+ self._update_dropdown()
+
+ def _update_dropdown(self, *largs):
+ dp = self._dropdown
+ cls = self.option_cls
+
+ dp.clear_widgets()
+ for value in self.values:
+ item = cls(text=value)
+ item.bind(on_release=lambda option: dp.select(option.text))
+ dp.add_widget(item)
+
+ def _open_dropdown(self, *largs):
+ self._dropdown.open(self)
+
+ def _on_dropdown_select(self, instance, data, *largs):
+ self.text = data
+
diff --git a/kivy/uix/tabbedpanel.py b/kivy/uix/tabbedpanel.py
index c6faef39c..802ae2b85 100644
--- a/kivy/uix/tabbedpanel.py
+++ b/kivy/uix/tabbedpanel.py
@@ -20,7 +20,7 @@ The :class:`TabbedPanel` provides one default tab.
Simple example
--------------
-.. include :: ../../examples/widgets/tabbedpanel.py
+.. include:: ../../examples/widgets/tabbedpanel.py
:literal:
Customize the Tabbed Panel
@@ -252,7 +252,7 @@ class TabbedPanel(GridLayout):
'''Specifies the text displayed on the default tab header.
:data:`default_tab_text` is a :class:`~kivy.properties.StringProperty`,
- default to 'default tab'.
+ defaults to 'default tab'.
'''
default_tab_cls = ObjectProperty(TabbedPanelHeader)
@@ -307,7 +307,7 @@ class TabbedPanel(GridLayout):
'''Holds the default tab.
.. Note:: For convenience, the automatically provided default tab is deleted
- when you change default_tab to something else.
+ when you change default_tab to something else.
:data:`default_tab` is a :class:`~kivy.properties.AliasProperty`
'''
@@ -393,6 +393,9 @@ class TabbedPanel(GridLayout):
content = self.content
if content is None:
return
+ parent = widget.parent
+ if widget.parent:
+ parent.remove_widget(widget)
if widget == content or widget == self._tab_layout:
super(TabbedPanel, self).add_widget(widget, index)
elif isinstance(widget, TabbedPanelHeader):
@@ -491,6 +494,9 @@ class TabbedPanel(GridLayout):
tab_layout.clear_widgets()
scrl_v = ScrollView(size_hint=(None, 1))
tabs = self._tab_strip
+ parent = tabs.parent
+ if parent:
+ parent.remove_widget(tabs)
scrl_v.add_widget(tabs)
scrl_v.pos = (0, 0)
self_update_scrollview = self._update_scrollview
diff --git a/kivy/uix/textinput.py b/kivy/uix/textinput.py
index 4ecc6d672..541349cf4 100644
--- a/kivy/uix/textinput.py
+++ b/kivy/uix/textinput.py
@@ -30,7 +30,7 @@ To create a multiline textinput ('enter' key adds a new line)::
textinput = TextInput(text='Hello world')
To create a monoline textinput, set the multiline property to false ('enter'
-key will defocus the textinput and emit on_text_validate event) ::
+key will defocus the textinput and emit on_text_validate event)::
def on_enter(instance, value):
print 'User pressed enter in', instance
@@ -38,7 +38,7 @@ key will defocus the textinput and emit on_text_validate event) ::
textinput = TextInput(text='Hello world', multiline=False)
textinput.bind(on_text_validate=on_enter)
-To run a callback when the text changes ::
+To run a callback when the text changes::
def on_text(instance, value):
print 'The widget', instance, 'have:', value
@@ -47,13 +47,13 @@ To run a callback when the text changes ::
textinput.bind(text=on_text)
You can 'focus' a textinput, meaning that the input box will be highlighted,
-and keyboard focus will be requested ::
+and keyboard focus will be requested::
textinput = TextInput(focus=True)
The textinput is defocused if the 'escape' key is pressed, or if another
widget requests the keyboard. You can bind a callback to the focus property to
-get notified of focus changes ::
+get notified of focus changes::
def on_focus(instance, value):
if value:
@@ -133,6 +133,23 @@ class TextInputCutCopyPaste(Bubble):
textinput = ObjectProperty(None)
+ def __init__(self, **kwargs):
+ super(TextInputCutCopyPaste, self).__init__(**kwargs)
+ Clock.schedule_interval(self._check_parent, .5)
+
+ def _check_parent(self, dt):
+ # this is a prevention to get the Bubble staying on the screen, if the
+ # attached textinput is not on the screen anymore.
+ parent = self.textinput
+ while parent is not None:
+ if parent == parent.parent:
+ break
+ parent = parent.parent
+ if parent is None:
+ Clock.unschedule(self._check_parent)
+ if self.textinput:
+ self.textinput._hide_cut_copy_paste()
+
def do(self, action):
textinput = self.textinput
@@ -161,8 +178,8 @@ class TextInput(Widget):
self._selection_finished = True
self._selection_touch = None
self.selection_text = ''
- self.selection_from = None
- self.selection_to = None
+ self.__selection_from = None
+ self._selection_to = None
self._bubble = None
self._lines_flags = []
self._lines_labels = []
@@ -259,7 +276,26 @@ class TextInput(Widget):
i = ni
return index, row
- def insert_text(self, substring, from_undo = False):
+ def select_text(self, start, end):
+ ''' Select portion of text displayed in this TextInput
+
+ .. versionadded:: 1.4.0
+ '''
+ if end < start:
+ raise Exception('end must be superior to start')
+ m = len(self.text)
+ self._selection_from = boundary(start, 0, m)
+ self._selection_to = boundary(end, 0, m)
+ self._update_selection(True)
+
+ def select_all(self):
+ ''' Select all of the text displayed in this TextInput
+
+ .. versionadded:: 1.4.0
+ '''
+ self.select_text(0, len(self.text))
+
+ def insert_text(self, substring, from_undo=False):
'''Insert new text on the current cursor position.
'''
if self.readonly:
@@ -291,14 +327,8 @@ class TextInput(Widget):
elif count > 0:
cursor = cursor[0], cursor[1] + count
- self._undo.append({'undo_command': \
- 'self.cursor = (%i, %i)\n' %(cursor[0], cursor[1]) +\
- 'self.selection_from = %i\nself.selection_to = %i\n' %(ci, sci())+
- 'self._selection = True\n'+\
- 'self.delete_selection(True)\n',\
- 'redo_command': \
- 'self.cursor = (%i, %i)\nself.insert_text(u\'%s\', True)'\
- %(cc, cr, substring.replace('\n', '\\n').replace('\'', '\\\''))})
+ self._undo.append({'undo_command': ('insert', cursor, ci, sci()),
+ 'redo_command': (cc, cr, substring)})
#reset redo when undo is appended to
self._redo = []
@@ -320,7 +350,25 @@ class TextInput(Widget):
'''
try:
x_item = self._redo.pop()
- exec(x_item['redo_command'])
+ undo_type = x_item['undo_command'][0]
+
+ if undo_type == 'insert':
+ cc, cr, substring = x_item['redo_command']
+ self.cursor = cc, cr
+ self.insert_text(substring, True)
+ #substring.replace('\n', '\\n').replace('\'', '\\\'')
+ elif undo_type == 'bkspc':
+ cc, cr = x_item['redo_command']
+ self.cursor = cc, cr
+ self.do_backspace(True)
+ else:
+ # delsel
+ ci, sci, cc, cr = x_item['redo_command']
+ self._selection_from = ci
+ self._selection_to = sci
+ self._selection = True
+ self.delete_selection(True)
+ self.cursor = (cc, cr)
self._undo.append(x_item)
except IndexError:
# reached at top of undo list
@@ -337,13 +385,28 @@ class TextInput(Widget):
'''
try:
x_item = self._undo.pop()
- exec(x_item['undo_command'])
+ undo_type = x_item['undo_command'][0]
+ self.cursor = x_item['undo_command'][1]
+
+ if undo_type == 'insert':
+ ci, sci = x_item['undo_command'][2:]
+ self._selection_from = ci
+ self._selection_to = sci
+ self._selection = True
+ self.delete_selection(True)
+ elif undo_type == 'bkspc':
+ substring = x_item['undo_command'][2:]
+ self.insert_text(substring, True)
+ else:
+ # delsel
+ substring = x_item['undo_command'][2:][0]
+ self.insert_text(substring, True)
self._redo.append(x_item)
except IndexError:
# reached at top of undo list
pass
- def do_backspace(self, from_undo = False):
+ def do_backspace(self, from_undo=False):
'''Do backspace operation from the current cursor position.
This action might do several things:
@@ -366,8 +429,8 @@ class TextInput(Widget):
substring = '\n'
else:
#ch = text[cc-1]
- substring = text[cc-1]
- new_text = text[:cc-1] + text[cc:]
+ substring = text[cc - 1]
+ new_text = text[:cc - 1] + text[cc:]
self._set_line_text(cr, new_text)
# refresh_text seems to be unnecessary here
@@ -383,13 +446,9 @@ class TextInput(Widget):
if from_undo:
return
- self._undo.append({'undo_command': \
- 'self.cursor = (%i, %i)\n' %(cursor[0], cursor[1]) +\
- 'self.insert_text(u\'%s\', True)'\
- %(substring.replace('\n', '\\n').replace('\'', '\\\'')),
- 'redo_command': \
- 'self.cursor = (%i, %i)\n' %(cc, cr)+\
- 'self.do_backspace(True)'})
+ self._undo.append({
+ 'undo_command': ('bkspc', cursor, substring),
+ 'redo_command': (cc, cr)})
#reset redo when undo is appended to
self._redo = []
@@ -443,11 +502,11 @@ class TextInput(Widget):
dy = self.line_height + self._line_spacing
cx = x - self.x
scrl_y = self.scroll_y
- scrl_y = scrl_y/ dy if scrl_y > 0 else 0
+ scrl_y = scrl_y / dy if scrl_y > 0 else 0
cy = (self.top - self.padding_y + scrl_y * dy) - y
cy = int(boundary(round(cy / dy), 0, len(l) - 1))
dcx = 0
- for i in xrange(1, len(l[cy])+1):
+ for i in xrange(1, len(l[cy]) + 1):
if self._get_text_width(l[cy][:i]) >= cx:
break
dcx = i
@@ -465,7 +524,7 @@ class TextInput(Widget):
self._selection_touch = None
self._trigger_update_graphics()
- def delete_selection(self, from_undo = False):
+ def delete_selection(self, from_undo=False):
'''Delete the current text selection (if any).
'''
if self.readonly:
@@ -473,12 +532,12 @@ class TextInput(Widget):
scrl_x = self.scroll_x
scrl_y = self.scroll_y
cc, cr = self.cursor
- sci = self.cursor_index
- ci = sci()
+ #sci = self.cursor_index
+ #ci = sci()
if not self._selection:
return
v = self.text
- a, b = self.selection_from, self.selection_to
+ a, b = self._selection_from, self._selection_to
if a > b:
a, b = b, a
text = v[:a] + v[b:]
@@ -496,15 +555,9 @@ class TextInput(Widget):
if from_undo:
return
- self._undo.append({'undo_command': \
- 'self.cursor = (%i, %i)\n' %(cursor[0], cursor[1]) +\
- 'self.insert_text(u\'%s\', True)'\
- %(substring.replace('\n', '\\n').replace('\'', '\\\'')),
- 'redo_command': \
- 'self.selection_from = %i\nself.selection_to = %i\n' %(ci, sci)+
- 'self._selection = True\n'+\
- 'self.delete_selection(True)\n'+\
- 'self.cursor = (%i, %i)\n' %(cc, cr)})
+ self._undo.append({
+ 'undo_command': ('delsel', cursor, substring),
+ 'redo_command': (ci, sci, cc, cr)})
#reset redo when undo is appended to
self._redo = []
@@ -512,7 +565,7 @@ class TextInput(Widget):
'''Update selection text and order of from/to if finished is True.
Can be called multiple times until finished is True.
'''
- a, b = self.selection_from, self.selection_to
+ a, b = self._selection_from, self._selection_to
if a > b:
a, b = b, a
self._selection_finished = finished
@@ -541,7 +594,7 @@ class TextInput(Widget):
if not self._selection_touch:
self.cancel_selection()
self._selection_touch = touch
- self.selection_from = self.selection_to = self.cursor_index()
+ self._selection_from = self._selection_to = self.cursor_index()
self._update_selection()
return True
@@ -555,7 +608,7 @@ class TextInput(Widget):
return False
if self._selection_touch is touch:
self.cursor = self.get_cursor_from_xy(touch.x, touch.y)
- self.selection_to = self.cursor_index()
+ self._selection_to = self.cursor_index()
self._update_selection()
return True
@@ -566,7 +619,7 @@ class TextInput(Widget):
if not self.focus:
return False
if self._selection_touch is touch:
- self.selection_to = self.cursor_index()
+ self._selection_to = self.cursor_index()
self._update_selection(True)
# show Bubble
win = self._win
@@ -576,18 +629,21 @@ class TextInput(Widget):
Logger.warning('Textinput: '
'Cannot show bubble, unable to get root window')
return True
- if self.selection_to != self.selection_from:
+ if self._selection_to != self._selection_from:
self._show_cut_copy_paste(touch.pos, win)
else:
self._hide_cut_copy_paste(win)
return True
- def _hide_cut_copy_paste(self, win):
+ def _hide_cut_copy_paste(self, win=None):
+ win = win or self._win
+ if win is None:
+ return
bubble = self._bubble
if bubble is not None:
win.remove_widget(bubble)
- def _show_cut_copy_paste(self, pos, win, parent_changed = False, *l):
+ def _show_cut_copy_paste(self, pos, win, parent_changed=False, *l):
# Show a bubble with cut copy and paste buttons
bubble = self._bubble
if bubble is None:
@@ -613,7 +669,7 @@ class TextInput(Widget):
# FIXME found a way to have that feature available for everybody
if bubble_pos[0] < 0:
# bubble beyond left of window
- if bubble.pos[1] > (win_size[1]- bubble_size[1]):
+ if bubble.pos[1] > (win_size[1] - bubble_size[1]):
# bubble above window height
bubble.pos = (0, (t_pos[1]) - (bubble_size[1] + lh + ls))
bubble.arrow_pos = 'top_left'
@@ -622,7 +678,7 @@ class TextInput(Widget):
bubble.arrow_pos = 'bottom_left'
elif bubble.right > win_size[0]:
# bubble beyond right of window
- if bubble_pos[1] > (win_size[1]- bubble_size[1]):
+ if bubble_pos[1] > (win_size[1] - bubble_size[1]):
# bubble above window height
bubble.pos = (win_size[0] - bubble_size[0],
(t_pos[1]) - (bubble_size[1] + lh + ls))
@@ -631,7 +687,7 @@ class TextInput(Widget):
bubble.right = win_size[0]
bubble.arrow_pos = 'bottom_right'
else:
- if bubble_pos[1] > (win_size[1]- bubble_size[1]):
+ if bubble_pos[1] > (win_size[1] - bubble_size[1]):
# bubble above window height
bubble.pos = (bubble_pos[0],
(t_pos[1]) - (bubble_size[1] + lh + ls))
@@ -686,6 +742,7 @@ class TextInput(Widget):
return
if Clipboard is None:
from kivy.core.clipboard import Clipboard
+ Clipboard
_platform = platform()
if _platform == 'win':
self._clip_mime_type = 'text/plain;charset=utf-8'
@@ -888,7 +945,15 @@ class TextInput(Widget):
tch = (vh / float(lh)) * oh
size[1] = vh
- texc = (tcx, tcy+tch, tcx+tcw, tcy+tch, tcx+tcw, tcy, tcx, tcy)
+ texc = (
+ tcx,
+ tcy + tch,
+ tcx + tcw,
+ tcy + tch,
+ tcx + tcw,
+ tcy,
+ tcx,
+ tcy)
# add rectangle.
r = rects[line_num]
@@ -914,8 +979,8 @@ class TextInput(Widget):
miny = self.y + _padding_y
maxy = _top - _padding_y
draw_selection = self._draw_selection
- scroll_y = self.scroll_y
- a, b = self.selection_from, self.selection_to
+ #scroll_y = self.scroll_y
+ a, b = self._selection_from, self._selection_to
if a > b:
a, b = b, a
get_cursor_from_index = self.get_cursor_from_index
@@ -933,7 +998,7 @@ class TextInput(Widget):
def _draw_selection(self, pos, size, line_num):
# Draw the current selection on the widget.
- a, b = self.selection_from, self.selection_to
+ a, b = self._selection_from, self._selection_to
if a > b:
a, b = b, a
get_cursor_from_index = self.get_cursor_from_index
@@ -1042,8 +1107,8 @@ class TextInput(Widget):
continue
if oldindex != index:
yield text[oldindex:index]
- yield text[index:index+1]
- oldindex = index+1
+ yield text[index:index + 1]
+ oldindex = index + 1
yield text[oldindex:]
def _split_smart(self, text):
@@ -1097,13 +1162,13 @@ class TextInput(Widget):
self.insert_text(displayed_str)
elif internal_action in ('shift', 'shift_L', 'shift_R'):
if not self._selection:
- self.selection_from = self.selection_to = self.cursor_index()
+ self._selection_from = self._selection_to = self.cursor_index()
self._selection = True
self._selection_finished = False
elif internal_action.startswith('cursor_'):
self.do_cursor_movement(internal_action)
if self._selection and not self._selection_finished:
- self.selection_to = self.cursor_index()
+ self._selection_to = self.cursor_index()
self._update_selection()
else:
self.cancel_selection()
@@ -1145,19 +1210,17 @@ class TextInput(Widget):
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('x'): # cut selection
+ if key == ord('x'): # cut selection
self._cut(self.selection_text)
- elif key == ord('c'): # copy selection
+ elif key == ord('c'): # copy selection
self._copy(self.selection_text)
- elif key == ord('v'): # paste selection
+ elif key == ord('v'): # paste selection
self._paste()
- elif key == ord('a'): # select all
- self.selection_from = 0
- self.selection_to = len(self.text)
- self._update_selection(True)
- elif key == ord('z'): # undo
+ elif key == ord('a'): # select all
+ self.select_all()
+ elif key == ord('z'): # undo
self.do_undo()
- elif key == ord('r'): # redo
+ elif key == ord('r'): # redo
self.do_redo()
else:
if self._selection:
@@ -1166,10 +1229,10 @@ class TextInput(Widget):
#self._recalc_size()
return
- if key == 27: # escape
+ if key == 27: # escape
self.focus = False
return True
- elif key == 9: # tab
+ elif key == 9: # tab
self.insert_text('\t')
return True
@@ -1209,7 +1272,7 @@ class TextInput(Widget):
'''
password = BooleanProperty(False)
- '''If True, the widget will display its characters as the character *.
+ '''If True, the widget will display its characters as the character '*'.
.. versionadded:: 1.2.0
@@ -1390,27 +1453,37 @@ class TextInput(Widget):
default to [0, 0, 0, 1] #Black
'''
- selection_from = NumericProperty(None, allownone=True)
+ def get_sel_from(self):
+ return self._selection_from
+
+ selection_from = AliasProperty(get_sel_from, None)
'''If a selection is happening, or finished, this property will represent
the cursor index where the selection started.
- :data:`selection_from` is a :class:`~kivy.properties.NumericProperty`,
- default to None
+ .. versionchanged:: 1.4.0
+
+ :data:`selection_from` is a :class:`~kivy.properties.AliasProperty`,
+ default to None, readonly.
'''
- selection_to = NumericProperty(None, allownone=True)
- '''If a selection is happening, or finished, this property will represent
- the cursor index where the selection ended.
+ def get_sel_to(self):
+ return self._selection_to
- :data:`selection_to` is a :class:`~kivy.properties.NumericProperty`,
- default to None
+ selection_to = AliasProperty(get_sel_to, None)
+ '''If a selection is happening, or finished, this property will represent
+ the cursor index where the selection started.
+
+ .. versionchanged:: 1.4.0
+
+ :data:`selection_to` is a :class:`~kivy.properties.AliasProperty`,
+ default to None, readonly.
'''
selection_text = StringProperty('')
'''Current content selection.
:data:`selection_text` is a :class:`~kivy.properties.StringProperty`,
- default to ''
+ default to '', readonly.
'''
focus = BooleanProperty(False)
@@ -1437,11 +1510,11 @@ class TextInput(Widget):
text = AliasProperty(_get_text, _set_text, bind=('_lines', ))
'''Text of the widget.
- Creation of a simple hello world ::
+ Creation of a simple hello world::
widget = TextInput(text='Hello world')
- If you want to create the widget with an unicode string, use ::
+ If you want to create the widget with an unicode string, use::
widget = TextInput(text=u'My unicode string')
diff --git a/kivy/uix/togglebutton.py b/kivy/uix/togglebutton.py
index f5db3a677..88d1197a5 100644
--- a/kivy/uix/togglebutton.py
+++ b/kivy/uix/togglebutton.py
@@ -8,7 +8,7 @@ that is only 'down' as long as it is pressed).
Toggle buttons can also be grouped to make radio buttons - only one button in
a group can be in 'down' state. The group name can be a string or any other
-hashable Python object ::
+hashable Python object::
btn1 = ToggleButton(text='Male', group='sex',)
btn2 = ToggleButton(text='Female', group='sex', state='down')
diff --git a/kivy/uix/treeview.py b/kivy/uix/treeview.py
index 1cfc7fecb..4e365e0bf 100644
--- a/kivy/uix/treeview.py
+++ b/kivy/uix/treeview.py
@@ -355,7 +355,8 @@ class TreeView(Widget):
def iterate_open_nodes(self, node=None):
'''Generator to iterate over expanded nodes.
- To get all the open nodes:
+
+ To get all the open nodes::
treeview = TreeView()
# ... add nodes ...
diff --git a/kivy/uix/video.py b/kivy/uix/video.py
index 7b8198543..5c30ae9ee 100644
--- a/kivy/uix/video.py
+++ b/kivy/uix/video.py
@@ -11,7 +11,7 @@ are installed). Our :class:`~kivy.core.video.VideoBase` implementation is used
under the hood.
Video loading is asynchronous - many properties are not available until
-the video is loaded (when the texture is created). ::
+the video is loaded (when the texture is created)::
def on_position_change(instance, value):
print 'The position in the video is', value
@@ -38,7 +38,7 @@ class Video(Image):
play = BooleanProperty(False)
'''Boolean, indicates if the video is playing.
- You can start/stop the video by setting this property. ::
+ You can start/stop the video by setting this property::
# start playing the video at creation
video = Video(source='movie.mkv', play=True)
diff --git a/kivy/uix/videoplayer.py b/kivy/uix/videoplayer.py
index 3ecb51076..85ecf2b82 100644
--- a/kivy/uix/videoplayer.py
+++ b/kivy/uix/videoplayer.py
@@ -288,7 +288,7 @@ class VideoPlayer(GridLayout):
play = BooleanProperty(False)
'''Boolean, indicates if the video is playing.
- You can start/stop the video by setting this property. ::
+ You can start/stop the video by setting this property::
# start playing the video at creation
video = VideoPlayer(source='movie.mkv', play=True)
diff --git a/kivy/uix/widget.py b/kivy/uix/widget.py
index 8c39a94e0..8256d12b2 100644
--- a/kivy/uix/widget.py
+++ b/kivy/uix/widget.py
@@ -85,7 +85,6 @@ class Widget(EventDispatcher):
Fired when an existing touch disappears
.. versionchanged:: 1.0.9
-
Everything related to event properties has been moved to
:class:`~kivy.event.EventDispatcher`. Event properties can now be used
in contructing a simple class, without subclassing :class:`Widget`.
@@ -227,6 +226,11 @@ class Widget(EventDispatcher):
if not isinstance(widget, Widget):
raise WidgetException(
'add_widget() can be used only with Widget classes.')
+ parent = widget.parent
+ # check if widget is already a child of another widget
+ if parent:
+ raise WidgetException('Cannot add %r, it already has a parent %r'
+ % (widget, parent))
widget.parent = self
if index == 0 or len(self.children) == 0:
self.children.insert(0, widget)
diff --git a/kivy/utils.py b/kivy/utils.py
index bab42874f..31dd53bce 100644
--- a/kivy/utils.py
+++ b/kivy/utils.py
@@ -1,3 +1,4 @@
+# pylint: disable=W0611
'''
Utils
=====
@@ -37,7 +38,7 @@ def difference(set1, set2):
def interpolate(value_from, value_to, step=10):
'''Interpolate a value to another. Can be useful to smooth some transition.
- For example ::
+ For example::
# instead of setting directly
self.pos = pos
@@ -61,7 +62,7 @@ def interpolate(value_from, value_to, step=10):
def strtotuple(s):
'''Convert a tuple string into tuple,
with some security check. Designed to be used
- with eval() function ::
+ with eval() function::
a = (12, 54, 68)
b = str(a) # return '(12, 54, 68)'
@@ -272,7 +273,7 @@ class QueryDict(dict):
.. versionadded:: 1.0.4
- ::
+ ::
d = QueryDict()
# create a key named toto, with the value 1
@@ -333,7 +334,7 @@ def platform():
if _platform_android is None:
try:
- __import__('android')
+ import android
_platform_android = True
except ImportError:
_platform_android = False
@@ -355,6 +356,7 @@ def platform():
return 'linux'
return 'unknown'
+
def escape_markup(text):
'''
Escape markup characters found in the text. Intended to be used when markup