diff --git a/doc/sources/guide2/basic.rst b/doc/sources/guide2/basic.rst index 7c2ed50d5..c3ca88cb6 100644 --- a/doc/sources/guide2/basic.rst +++ b/doc/sources/guide2/basic.rst @@ -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:: diff --git a/doc/sources/guide2/events.rst b/doc/sources/guide2/events.rst index 5a5d24f46..44bdaceee 100644 --- a/doc/sources/guide2/events.rst +++ b/doc/sources/guide2/events.rst @@ -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): diff --git a/doc/sources/guide2/graphics.rst b/doc/sources/guide2/graphics.rst index 8167f592e..b23ea8cea 100644 --- a/doc/sources/guide2/graphics.rst +++ b/doc/sources/guide2/graphics.rst @@ -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) + diff --git a/doc/sources/guide2/lang.rst b/doc/sources/guide2/lang.rst index 23b778b9f..676d2c553 100644 --- a/doc/sources/guide2/lang.rst +++ b/doc/sources/guide2/lang.rst @@ -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 `:`:: + + : + +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 ---------- diff --git a/doc/sources/guide2/widgets.rst b/doc/sources/guide2/widgets.rst index c6d76530e..5dded1cd5 100644 --- a/doc/sources/guide2/widgets.rst +++ b/doc/sources/guide2/widgets.rst @@ -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. diff --git a/kivy/uix/modalview.py b/kivy/uix/modalview.py index 92ebe7a18..e9fc6f0a0 100644 --- a/kivy/uix/modalview.py +++ b/kivy/uix/modalview.py @@ -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 -------- diff --git a/kivy/uix/popup.py b/kivy/uix/popup.py index 61fbca346..1a2d7c971 100644 --- a/kivy/uix/popup.py +++ b/kivy/uix/popup.py @@ -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.] ''' diff --git a/kivy/uix/tabbedpanel.py b/kivy/uix/tabbedpanel.py index c4a4b5897..aff60e32e 100644 --- a/kivy/uix/tabbedpanel.py +++ b/kivy/uix/tabbedpanel.py @@ -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': diff --git a/kivy/uix/widget.py b/kivy/uix/widget.py index ae3153a4e..7e2628e53 100644 --- a/kivy/uix/widget.py +++ b/kivy/uix/widget.py @@ -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)