mirror of https://github.com/kivy/kivy.git
Merge branch 'master' of ssh://github.com/kivy/kivy
This commit is contained in:
commit
fe9b3f9eff
|
@ -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::
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
----------
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
--------
|
||||
|
|
|
@ -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.]
|
||||
'''
|
||||
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue