Merge branch 'master' of ssh://github.com/kivy/kivy

This commit is contained in:
Mathieu Virbel 2012-11-29 15:44:00 +01:00
commit fe9b3f9eff
9 changed files with 254 additions and 49 deletions

View File

@ -42,7 +42,7 @@ Creating a kivy application is as simple as:
- subclassing the :class:`kivy.app.App` class
- implementing its :meth:`kivy.app.App.build` method so it returns a
:class:`kivy.uix.Widget` instance (the root of your widget tree) -
instanciating this class, and call its :meth:`kiyv.app.App.run`
instantiating this class, and call its :meth:`kiyv.app.App.run`
method.
here is an example of such a minimum application::

View File

@ -15,7 +15,7 @@ Introduction to Event Dispatcher
One of the most important base classes of the framework is the
:class:`kivy.event.EventDispatcher` class, this class allows to register event
types, and to dispatch them to interrested parties (usually other event
types, and to dispatch them to interested parties (usually other event
dispatchers). :class:`kivy.uix.widget.Widget`,
:class:`kivy.animation.Animation` and :obj:`kivy.clock.Clock` for example are
event dispatchers.
@ -33,7 +33,7 @@ See the following example::
self.register_event_type('on_test')
super(MyEventDispatcher, self).__init__(**kwargs)
def test(self, value):
def do_something(self, value):
# when test is called, the 'on_test' event will be
# dispatched with the value
self.dispatch('on_test', value)
@ -53,7 +53,7 @@ A callback can be any python callable, but you need to be sure it can accept
the arguments the event will use, for this, it's usually safer to accept the
`*args` argument, that will catch any remaining arguments in the `args` list.
example::
Example::
def my_callback(value, *args):
print "Hello, I got an event!", value
@ -85,8 +85,8 @@ Declaration of a Property
-------------------------
To declare a property, you must create it at class level, the class will do the
work to instanciate the real attributes when the object will be created, the
properties is not the attribute, it's a mecanism to create events for your
work to instantiate the real attributes when the object will be created, the
properties is not the attribute, it's a mechanism to create events for your
attributes::
class MyWidget(Widget):

View File

@ -41,8 +41,8 @@ Context instructions
--------------------
Context instructions manipulate the opengl context, you can rotate, translate,
and scale your canvas and attach a texture or change the drawing color, this
one is the most commonly used, but others are really useful too::
and scale your canvas, attach a texture or change the drawing color, this one
is the most commonly used, but others are really useful too::
with self.canvas.before:
Color(1, 0, .4, mode='rgb')
@ -50,6 +50,52 @@ one is the most commonly used, but others are really useful too::
Drawing instructions
--------------------
Drawing instructions are ranging from very simple ones, to draw a line or a
polygon, to more complex ones, like meshes or bezier curves::
with self.canvas:
# draw a line using the default color
Line(points=(x1, y1, x2, y2, x3, y3))
# lets draw a semi-transparent red square
Color(1, 0, 0, .5, mode='rgba')
Rect(pos=self.pos, size=self.size)
Manipulating instructions
-------------------------
Sometime, you want to update or remove the instructions you added to an canvas,
this can be done in various ways depending on your needs:
You can keep a reference to your instruction and update them::
class MyWidget(Widget):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
with self.canvas:
self.rect = Rectangle(pos=self.pos, size=self.size)
self.bind(pos=self.update_rect)
self.bind(size=self.update_rect)
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
Or you can clean your canvas and start fresh::
class MyWidget(Widget):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.draw_my_stuff()
self.bind(pos=self.draw_my_stuff)
self.bind(size=self.draw_my_stuff)
def draw_my_stuff(self):
self.canvas.clear()
with self.canvas:
self.rect = Rectangle(pos=self.pos, size=self.size)

View File

@ -6,14 +6,153 @@ Kv language
Concept behind the language
---------------------------
As your applications grow more complexes, you will notice that construction of
widget trees and explicit declaration of bindings, become verbose, and hard to
maintain. Thus, a language more suited to this need has been developed.
The KV language (sometime called kvlang, or kivy language), allows you to
create your widget tree in a very declarative way, and to bind properties of
widget to each other, or to callbacks very naturally, you allows very fast
prototyping and agile changes to your UI, and a good separation between the
logic of your application, and the look it have.
How to load kv
--------------
There are two ays to load kv code into your application:
- By name convention:
Kivy looks if there is a kv file with the same name as your App class, lowercase,
minus "app" if it endswith 'app', e.g: MyApp -> my.kv. If this file define a
root rule it will be attached to the App's `root` attribute and used as the
application widget tree.
- :obj:`kivy.lang.Builder`:
you can tell directly kivy to load a string or a file, if it defines a root
widget, it will be returned by the method::
Builder.load_file('path/to/file.kv')
or::
Builder.load_string(kv_string)
Rule context
------------
Instanciate children
A kv source is constituted from `rules`, which are used to describe the content
of a Widget, you can have one `root` rule, and any number of `class` rules.
The `root` rule is declared by declaring the class of your root widget, without
any indentation, followed by `:` and will be set as the `root` attribute of the
App instance::
Widget:
A `class` rule, which define how any instance of that widget class will be created
is declared by declaring the name of the class, between chevrons, followed by `:`::
<MyWidget>:
Rules use indentation for delimitation, as python, indentation should be of
four spaces per level, like the python good practice recommendations.
There are three keywords specific to kv languages:
- `app`: always refer to the instance of your application.
- `root`: always refer to the base widget of the current rule.
- `self`: always refer to the current widget.
Special syntaxes
----------------
There are two special syntax to define values for the whole kv context:
To import something from python::
#:import name x.y.z
Is equivalent to::
from x.y import z as name
in python.
To set a global value::
#:set name value
Is equivalent to::
name = value
in python.
Instantiate children
--------------------
To declare the widget has a child widget, instance of some class, just declare
this child inside the rule::
MyRootWidget:
BoxLayout:
Button:
Button:
The example above define that our root widget, which is an instance of `MyRootWidget`
has a child, which is an instance of the :class:`kivy.uix.boxlayout.BoxLayout` and that
this child has itself two child, instances of the :class:`kivy.uix.button.Button` class.
A python equivalent of this code could be::
root = MyRootWidget()
box = BoxLayout()
box.add_widget(Button())
box.add_widget(Button())
root.add_widget(box)
Which you way find maybe less nice, both to read and to write.
Of course, in python, you can pass keyword arguments to your widgets at
creation, to specify their behaviour, for example, to set the number of columns
of a :mod:`kivy.uix.gridlayout`, we would do::
grid = GridLayout(cols=3)
To do the same thing in kv, you can set properties of the child widget directly
in the rule::
GridLayout:
cols: 3
The value is evaluated as a python expression, and all the properties used in
the expression will be observed, that means that if you had something like this
in python (this assume `self` is a widget with a `data`
:class:`kivy.property.ListProperty`)::
grid = GridLayout(cols=len(self.data))
self.bind(data=grid.setter('cols'))
To have your display updated when your data change, you can now have just::
GridLayout:
cols: len(root.data)
Extend canvas
-------------
Kv lang can be used to define the canvas instructions of your widget too::
MyWidget:
canvas:
Color:
rgba: 1, .3, .8, .5
Line:
points: zip(self.data.x, self.data.y)
And yes, they get updated too if properties values change.
Of course you can use `canvas.before` and `canvas.after`.
Templating
----------

View File

@ -7,7 +7,7 @@ Introduction to Widget
----------------------
A :class:`kivy.uix.widget.Widget` is the basic component of the interface, it
can display things at places, and recieve touch (and other) events, and react to
can display things at places, and receive touch (and other) events, and react to
them. It's representation and behaviour.
Manipulating the Widget tree
@ -15,25 +15,35 @@ Manipulating the Widget tree
Widgets are organized in Trees, your application have a root widget, which
usually have children, which can have children on their own. Children of a
widget are represented as a :class:`kivy.properties.ListProperty`
:attr:`kivy.uix.widget.Widget.children`. The way to add a children to a widget
is to call :meth:`kivy.uix.widget.Widget.add_widget`, likely, to remove a
widget, you use :meth:`kivy.uix.widget.Widget.remove_widget`.
widget are represented as the :attr:`kivy.uix.widget.Widget.children`
:class:`kivy.properties.ListProperty`. The way to add a children to a widget is
to call :meth:`kivy.uix.widget.Widget.add_widget`, likely, to remove a widget,
you use :meth:`kivy.uix.widget.Widget.remove_widget`.
Organize with Layouts
---------------------
Layouts are a special kind of widget that allows automatic control of the size
and position of their children. There are different kind of layouts, allowing
for different automatic organisation. A common caracteristic of layouts is that
for different automatic organisation. A common characteristic of layouts is that
they use (even if differently) of the :attr:`kivy.uix.widget.Widget.size_hint`
and :attr:`kivy.uix.widget.Widget.pos_hint` properties. Those properties allow
to define size and pos of the widget relatively to the parent layout.
to define size and position of the widget relatively to the parent layout.
Seperate with Screen Manager
Look at the documentation of the various Layouts to see to which situation each
one applies:
- :mod:`kivy.uix.floatlayout`
- :mod:`kivy.uix.boxlayout`
- :mod:`kivy.uix.gridlayout`
- :mod:`kivy.uix.stacklayout`
- :mod:`kivy.uix.relativelayout`
- :mod:`kivy.uix.anchorlayout`
Separate with Screen Manager
----------------------------
If your application is composed of various screens, you likely want an easy way
to navigate from one to another, fortunately, there is the
:class:`kivy.uix.screenmanager.ScreenManager` class, that allows you to define
screens separatly, and to set the transitions from one to another.
screens separately, and to set the transitions from one to another.

View File

@ -8,8 +8,9 @@ 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.
want your view to be fullscreen, either use lower than 1 size hints (for
instance size_hint=(.8, .8)) or deactivate the size_hint and use fixed size
attributes.
Examples
--------

View File

@ -12,8 +12,9 @@ will cover the whole "parent" window. When you are creating a popup, you must at
a minimum set a :data:`Popup.title` and a :data:`Popup.content` widget.
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.
want your popup to be fullscreen, either use lower than 1 size hints (for
instance size_hint=(.8, .8)) or deactivate the size_hint and use fixed size
attributes.
.. versionchanged:: 1.4.0
@ -115,7 +116,7 @@ class Popup(ModalView):
.. versionadded:: 1.1.0
:data:`background_color` is a :class:`~kivy.properties.ListProperty`,
:data:`separator_color` is a :class:`~kivy.properties.ListProperty`,
default to [47 / 255., 167 / 255., 212 / 255., 1.]
'''

View File

@ -126,7 +126,7 @@ tabbed panel's background_image and background_color.
'''
__all__ = ('TabbedPanel', 'TabbedPanelContent', 'TabbedPanelHeader',
'TabbedPanelItem', 'TabbedPanelStrip', 'TabbedPanelException')
'TabbedPanelItem', 'TabbedPanelStrip', 'TabbedPanelException')
from functools import partial
from kivy.clock import Clock
@ -139,7 +139,7 @@ from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.logger import Logger
from kivy.properties import ObjectProperty, StringProperty, OptionProperty, \
ListProperty, NumericProperty, AliasProperty, BooleanProperty
ListProperty, NumericProperty, AliasProperty, BooleanProperty
class TabbedPanelException(Exception):
@ -260,10 +260,12 @@ class TabbedPanel(GridLayout):
default to 'atlas://data/images/defaulttheme/tab'.
'''
_current_tab = ObjectProperty(None)
def get_current_tab(self):
return self._current_tab
current_tab = AliasProperty(get_current_tab, None)
current_tab = AliasProperty(get_current_tab, None, bind=('_current_tab', ))
'''Links to the currently select or active tab.
.. versionadded:: 1.4.0
@ -271,10 +273,11 @@ class TabbedPanel(GridLayout):
:data:`current_tab` is a :class:`~kivy.AliasProperty`, read-only.
'''
tab_pos = OptionProperty('top_left',
options=('left_top', 'left_mid', 'left_bottom', 'top_left',
'top_mid', 'top_right', 'right_top', 'right_mid',
'right_bottom', 'bottom_left', 'bottom_mid', 'bottom_right'))
tab_pos = OptionProperty(
'top_left',
options=('left_top', 'left_mid', 'left_bottom', 'top_left',
'top_mid', 'top_right', 'right_top', 'right_mid',
'right_bottom', 'bottom_left', 'bottom_mid', 'bottom_right'))
'''Specifies the position of the tabs relative to the content.
Can be one of: `left_top`, `left_mid`, `left_bottom`, `top_left`,
`top_mid`, `top_right`, `right_top`, `right_mid`, `right_bottom`,
@ -365,8 +368,8 @@ class TabbedPanel(GridLayout):
default_tab = AliasProperty(get_def_tab, set_def_tab)
'''Holds the default tab.
.. Note:: For convenience, the automatically provided default tab is deleted
when you change default_tab to something else.
.. Note:: For convenience, the automatically provided default tab is
deleted when you change default_tab to something else.
:data:`default_tab` is a :class:`~kivy.properties.AliasProperty`
'''
@ -378,27 +381,30 @@ class TabbedPanel(GridLayout):
self.default_tab.content = l[0]
default_tab_content = AliasProperty(get_def_tab_content,
set_def_tab_content)
set_def_tab_content)
'''Holds the default tab content.
:data:`default_tab_content` is a :class:`~kivy.properties.AliasProperty`
'''
def __init__(self, **kwargs):
# these variables need to be initialised before the kv lang is processed
# setup the base layout for the tabbed panel
# these variables need to be initialised before the kv lang is
# processed setup the base layout for the tabbed panel
self._tab_layout = GridLayout(rows=1)
self.rows = 1
# bakground_image
self._bk_img = Image(
source=self.background_image, allow_stretch=True,
keep_ratio=False, color=self.background_color)
self._tab_strip = _tabs = TabbedPanelStrip(tabbed_panel=self,
self._tab_strip = TabbedPanelStrip(
tabbed_panel=self,
rows=1, cols=99, size_hint=(None, None),
height=self.tab_height, width=self.tab_width)
self._partial_update_scrollview = None
self.content = content = TabbedPanelContent()
self._current_tab = default_tab = self._original_tab \
self.content = TabbedPanelContent()
self._current_tab = self._original_tab \
= self._default_tab = TabbedPanelHeader()
super(TabbedPanel, self).__init__(**kwargs)
@ -470,7 +476,7 @@ class TabbedPanel(GridLayout):
self._reposition_tabs()
else:
Logger.info('TabbedPanel: default tab! can\'t be removed.\n' +
'Change `default_tab` to a different tab.')
'Change `default_tab` to a different tab.')
else:
if widget in content.children:
content.remove_widget(widget)
@ -640,7 +646,8 @@ class TabbedPanel(GridLayout):
self.cols = 2
self.rows = 1
# tab_layout contains two blank dummy widgets for spacing
# "vertically" and the scatter containing scrollview containing tabs
# "vertically" and the scatter containing scrollview
# containing tabs
tab_layout.rows = 3
tab_layout.cols = 1
tab_layout.size_hint = (None, 1)
@ -651,12 +658,12 @@ class TabbedPanel(GridLayout):
# rotate the scatter for vertical positions
rotation = 90 if tab_pos[0] == 'l' else -90
sctr = Scatter(do_translation=False,
rotation=rotation,
do_rotation=False,
do_scale=False,
size_hint=(None, None),
auto_bring_to_front=False,
size=scrl_v.size)
rotation=rotation,
do_rotation=False,
do_scale=False,
size_hint=(None, None),
auto_bring_to_front=False,
size=scrl_v.size)
sctr.add_widget(scrl_v)
lentab_pos = len(tab_pos)
@ -676,7 +683,7 @@ class TabbedPanel(GridLayout):
elif tab_pos[lentab_pos - 4:] == '_mid':
#calculate top of scatter
sctr.bind(pos=partial(self._update_top, sctr, 'mid',
scrl_v.width))
scrl_v.width))
tab_list = (Widget(), sctr, Widget())
elif tab_pos[lentab_pos - 7:] == '_bottom':
tab_list = (Widget(), Widget(), sctr)
@ -719,7 +726,8 @@ class TabbedPanel(GridLayout):
def _update_top(self, *args):
sctr, top, scrl_v_width, x, y = args
Clock.unschedule(partial(self._updt_top, sctr, top, scrl_v_width))
Clock.schedule_once(partial(self._updt_top, sctr, top, scrl_v_width), 0)
Clock.schedule_once(
partial(self._updt_top, sctr, top, scrl_v_width), 0)
def _updt_top(self, sctr, top, scrl_v_width, *args):
if top[0] == 't':

View File

@ -280,7 +280,7 @@ class Widget(EventDispatcher):
children = self.children
if index >= len(children):
index = len(children)
next_index = 0
next_index = -1
else:
next_child = children[index]
next_index = canvas.indexof(next_child.canvas)