/dev/blah

things i want to remember, things i want to share

Développeur Python et adepte Linux depuis 2005, passionné par beaucoup trop de choses. Profil Github

Entries tagged “kivy”

Prepare for the Kivy contest #2

written by tshirtman, on 3/16/14 2:50 AM.

Developers, developers… rejoice!

kivy contest

The kivy project is organizing a second contest!

From the 15th of April, when the theme will be revealed, to the 15th of May, the final deadline, all the interested candidates will compete to produce the nicest application possible, once month to make the difference, and collect prices :).

Prizes will be announced before the beginning of the contest, the python software foundation kindly offered to sponsor the contest, if you are or know somebody interested in sponsoring the contest, please contact us at contest@kivy.org.

Tip me if you like this :)

Kpritz

written by tshirtman, on 3/3/14 2:23 AM.

Just because it seemed not to hard, i spent the evening (actually about 2 hours) making a Spritz clone using kivy.

Code is a bit crude and all, but here it is for your enjoyment:

https://gist.github.com/9316552

Here is an apk if you want to actually try it.

Only flat text files are supported, opening the option window is a bit slow (because of ColorPickers), but it’s functionnal, progression is saved, all the options i though were needed are there, and it seems usable.

You can find a lot of free book in the .txt format over at project gutenberg.

Tip me if you like this :)

Using tex_coords in kivy for fun and profit

written by tshirtman, on 2/19/14 1:45 AM.

Your gpu is an awesome thing, it can do all kind of calculations, very fast. For example, instead of telling it exactly how you want each pixels to be, you can throw textures and vertices at it, and have it correctly deduce how this things will have to look.

To do this, though, you must tell it what part of the texture will be stuck to each vertice. This is usually denoted using texture coordinates.

Texture coordinates, like usual coordinates, indicate the place of something. Instead of noting them x and y, they are often called u and v, which allows to clearly note what parts of an equation relate to each.

While kivy offers you high level canvas instructions, it gives you a pretty good access to lower level features, and you can actually manipulate the texture coordinates of your Rectangle instructions. This allow for cheap zooming, repeat-scrolling, and other image manipulations, since your gpu is doing all the actual work.

tex_coords = u, v, u + w, v, u + w, v + h, u, v + h

which is better understood as:

tex_coords = [
    u,     v,
    u + w, v,
    v + w, v + h,
    u,     v + h
]

considering a default tex_coords value, where u and v = 0, and w and h = 1 (all values are relatives, so usually between 0 and 1).

u, v + h-------u + w, v + h
|              |
u, v-----------u + w, v

which means the 4 angles of your texture, will be on the 4 angles of your rectangle, how dull!

One way to play with different values, is to create a little app showing you the effect of deformations.

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, ListProperty
from kivy.core.image import Image as CoreImage

kv = '''
#:import chain itertools.chain
RootWidget:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: root.pos
            size: root.size
            texture: app.texture
            # here is our usage of the calculated texture coordinates
            # we devide by 100 because we took the input with a 100x100
            # rectangle as default value
            tex_coords: [x / 100. for x in chain(*root.points)]

        PushMatrix
        # translate the rectangle to make it easier to play with
        Translate:
            xy: root.width / 2, root.height / 2

        Color:
            rgba: 1, 0, 0, .5
        Line:
            points: chain(*root.points + root.points[:1])
            width: 2
        PopMatrix
'''


def dist(point, pos):
    return ((point[0] - pos[0]) ** 2 + (point[1] - pos[1]) ** 2)
    # ** .5 # who cares about square root here? it doesn't change the order


class RootWidget(Widget):
    # the default values, a 100x100 square, displayed around the middle of the screen
    points = ListProperty([[0, 0], [100, 0], [100, 100], [0, 100]])

    def on_touch_down(self, touch, *args):
        # compensate the translation done in canvas
        pos = touch.pos[0] - self.width / 2, touch.pos[1] - self.height / 2

        touch.ud['point'] = min(
            range(4), key=lambda x: dist(self.points[x], pos))

    def on_touch_move(self, touch, *args):
        # compensate the translation done in canvas
        pos = touch.pos[0] - self.width / 2, touch.pos[1] - self.height / 2
        # update the point
        self.points[touch.ud['point']] = pos


class TexCoordsApp(App):
    texture = ObjectProperty(None)

    def build(self):
        self.root = Builder.load_string(kv)
        self.texture = CoreImage.load(
            'GrassGreenTexture0002.jpg').texture
        self.texture.wrap = 'repeat'
        return self.root


if __name__ == '__main__':
    TexCoordsApp().run()

The texture have been scrapped on the web and is not very interesting, but you can find it here.

Of course, this is much more a learning device (helful because these transformation are not quite straighforward for our brains) than a practical application, but a lot more can be done.

Here is, for example, a little infinite scroll app uting this concept.

edit: Ben Rousch kindly created apks out of the two examples, if you want to try them on android: TexCoordsExample ScrollExample

Tip me if you like this :)

Magnet: a widget to easily make your interface more lively

written by tshirtman, on 2/6/14 12:38 AM.

Kivy has a very nice Animation class, that allows you to move move widgets around (or other things), by updating any numeric property to a target value, just by setting a time and a transition function. Still, when you are building a complex interface, it can still be a bit cumbersome to manually trigger animations for all the elements, and to keep track of them.

After being frustrated about this issue for some time, i tried my luck some time ago, at doing a nicer, “90% use cases” interface, and the Magnet garden widget was born.

Its usage is simple, you simply use it to wrap your target widget, and give it rules about how to transition when the magnet is moved. As the magnet is moved or resized by the usual kivy logic, instead of making the wrapped widget immediately follow such constraints, it’ll create and keep track of animations to achieve a smooth transition to the new values for you.

As any garden “flower”, to install it, you need to install and use the garden project.

python setup.py install

(either in a virtualenv or system-wide)

then do:

garden install magnet

you can now import it a kivy application:

from kivy.garden.magnet import Magnet

Garden Magnet video

Tip me if you like this :)

Kivy image manipulations with Mesh and Textures

written by tshirtman, on 1/29/14 1:05 AM.

If you want to give a little life to interactive (or not) elements, it’s always nice to have more tricks to manipulate images for nifty effects.

One of such ways is mapping a Texture on a special canvas instruction, that will distort your texture based on the position of its points.

[kivy.graphics.Mesh](http://kivy.org/docs/api-kivy.graphics.html#kivy.graphics.Mesh) is such an instruction, and it has a simple interface, if not 100% straightforward, you basically call it this way.

Mesh:
vertices: (x1, y1, s1, v1, x2, y2, s2, v2, x3, y3, s3, v3...)
indices: (1, 2, 3...)
texture: some_texture
mode: some_mode

where all: x mean the horizontal coordinate of a point y mean the vertical coordinate of a point s mean the horizontal position of the texture attached to this point (between 0 and 1) v mean the vertical position of the texture attached to this point (between 0 and 1)

indices is useful if, for some (very good) reason, you don’t want to give the points in the order you’ll use them, or if you want to refer to a point multiple time (this won’t be demonstrated in this blog post, but try to think about a grid mapping, for example), now we will use it in the most simple way, just give the range of your number of points.

the texture needs to be a texture object, you can get such an object by getting the texture property of a [CoreImage](http://kivy.org/docs/api-kivy.core.image.html#kivy.core.image.Image) or an Image (or even a Label, if you want to).

Let’s say we have an image, for example, kivy logo.

from kivy.core.image import Image as CoreImage

texture = CoreImage('data/logo/kivy-icon-512.png').texture

(this will work wherever you are, because kivy look at his images anyway :))

This image is a circle, lets use that to cut it into a virtual pie and assign a part of the texture to each part, we’ll be using the triangle_fan mode of Mesh, that is a perfect fit for such an operation.

# the central point
points = [Window.width / 2, Window.height / 2, .5, .5]

# now go around
    i = 0
    while i < 2 * pi:
        i += 0.01 * pi
        points.extend([
    Window.width / 2 + cos(i) * 100,
    Window.height / 2 + sin(i) * 100,
    .5 + sin(i),
    .5 + cos(i)])

put that in a more complete program, and you get this:

from kivy.app import App
from kivy.lang import Builder
from kivy.core.image import Image as CoreImage
from kivy.properties import ListProperty, ObjectProperty
from kivy.clock import Clock
from kivy.core.window import Window
from math import sin, cos, pi


kv = '''
Widget:
    canvas:
        Color:
            rgba: 1, 1, 1, 1
        Mesh:
            vertices: app.mesh_points
            indices: range(len(app.mesh_points) / 4)
            texture: app.mesh_texture
            mode: 'triangle_fan'
'''


class MeshBallApp(App):
    mesh_points = ListProperty([])
    mesh_texture = ObjectProperty(None)

    def build(self):
        self.mesh_texture = CoreImage('data/logo/kivy-icon-512.png').texture
        Clock.schedule_interval(self.update_points, 0)
        return Builder.load_string(kv)

    def update_points(self, *args):
        points = [Window.width / 2, Window.height / 2, .5, .5]
        i = 0
        while i < 2 * pi:
            i += 0.01 * pi
            points.extend([
                Window.width / 2 + cos(i) * 100,
                Window.height / 2 + sin(i) * 100,
                .5 + cos(i),
                .5 + sin(i)])

        self.mesh_points = points

if __name__ == '__main__':
    MeshBallApp().run()

Now, this is not very impressive, we just have an image, there were much simpler ways to do that!

But now, we can tweek the rendering a lot, lets add some offseting of the texture, some radius change, and some wobbling, with sliders to control all this.

from kivy.app import App
from kivy.lang import Builder
from kivy.core.image import Image as CoreImage
from kivy.properties import ListProperty, ObjectProperty, NumericProperty
from kivy.clock import Clock
from kivy.core.window import Window
from math import sin, cos, pi


kv = '''
BoxLayout:
    Widget:
        canvas:
            Color:
                rgba: 1, 1, 1, 1
            Mesh:
                vertices: app.mesh_points
                indices: range(len(app.mesh_points) / 4)
                texture: app.mesh_texture
                mode: 'triangle_fan'
    BoxLayout:
        orientation: 'vertical'
        size_hint_x: None
        width: 100
        Slider:
            value: app.offset_x
            on_value: app.offset_x = args[1]
            min: -1
            max: 1
        Slider:
            value: app.offset_y
            on_value: app.offset_y = args[1]
            min: -1
            max: 1
        Slider:
            value: app.radius
            on_value: app.radius = args[1]
            min: 10
            max: 1000
        Slider:
            value: app.sin_wobble
            on_value: app.sin_wobble = args[1]
            min: -50
            max: 50
        Slider:
            value: app.sin_wobble_speed
            on_value: app.sin_wobble_speed = args[1]
            min: 0
            max: 50
            step: 1
'''


class MeshBallApp(App):
    mesh_points = ListProperty([])
    mesh_texture = ObjectProperty(None)
    radius = NumericProperty(500)
    offset_x = NumericProperty(.5)
    offset_y = NumericProperty(.5)
    sin_wobble = NumericProperty(0)
    sin_wobble_speed = NumericProperty(0)

    def build(self):
        self.mesh_texture = CoreImage('data/logo/kivy-icon-512.png').texture
        Clock.schedule_interval(self.update_points, 0)
        return Builder.load_string(kv)

    def update_points(self, *args):
        points = [Window.width / 2, Window.height / 2, .5, .5]
        i = 0
        while i < 2 * pi:
            i += 0.01 * pi
            points.extend([
                Window.width / 2 + cos(i) * (self.radius + self.sin_wobble * sin(i * self.sin_wobble_speed)),
                Window.height / 2 + sin(i) * (self.radius + self.sin_wobble * sin(i * self.sin_wobble_speed)),
                self.offset_x + sin(i),
                self.offset_y + cos(i)])

        self.mesh_points = points

if __name__ == '__main__':
    MeshBallApp().run()

which gives us:

Kivy image manipulations with Mesh and Text

Now we are talking!

Tip me if you like this :)

Kivy - Mouse cursor on Rapsberry Pi

written by tshirtman, on 1/23/14 12:40 AM.

It’s no news that kivy have been ported to Rapsberry Pi, it happened nearly a year ago, and works quite well, especially if you start the app without an X server, since kivy draw directly to EGL (don’t ask me :)), it doesn’t need it.

However, there is a little issue about it if you don’t have a touch screen, you can use your mouse, but you don’t see any cursor, whever you are in X or not, because in X, kivy display is actually on top of the X cursor, and without X, nothing even try to draw a mouse cursor.

A few months ago, i wrote a quick hack, adding a few graphic instructions to the egl window provider, and made the probsys driver used on rpi directly update the position of this instruction. It worked, but it wasn’t really a general solution.

So after procrastinating a proper solution for a long time, i finally got my act together and added the cursor display as an option to the touchring module of kivy, along with options of what image to use, offsets and so on, and added the code that made the mouse driver dispatch pos to window’s mouse_pos property.

So here it is, you can find it here until the PR is merged.

Tip me if you like this :)

Kivy team expanding

written by tshirtman, on 1/22/14 1:32 AM.

Yesterday, i had the pleasure to expand the Kivy core-dev team.

Regarding their great contribution to the framework development and their community involvement:

brousch Kovak inclement matham Zen-CODE

have been added in the team on github, allowing them to be even more helpful ;).

Congratulation and welcome to them!

Tip me if you like this :)

Stress-testing kivy with the recorder module.

written by tshirtman, on 1/21/14 12:48 AM.

In a framework like kivy, or any app using it it can happen that there is a bug that precise interaction is needed to reproduce, and it can be frustrating to manually test every time, sometime a complex or repetitive manipulation.

Kivy has a not-so-well known module, called recorder that allows to record and replay user inputs, both touch and keyboard events.

python main.py -m recorder

f8 to record, f7 to replay, ‘simple as that!

It’s helpful, but in some cases, something is missing, for example, an user reported a memory leak in 1.7.2, apparently windows specific, that happened to crash the app (the touchracer demo) after a lot of touches, over a very long time (hours) of usage. You don’t want to record hours of touches just to replay them, at least, i didn’t want to.

So i did a little update to this module, to add the feature of playing the same replay times and time over, i recorded a sample of one touch (the touch duration didn’t matter), as i wasn’t very fast (i’m only human), this recording lasted nearly a second (starting to record, touch down, touch up, stoping record), so i fired my editor of choice, and edited the recorder.kvi to:

#RECORDER1.0
(.0118309783935547, 'begin', 1, {'is_touch': True, 'profile': ['pos'], 'sy': 0.9688715953307393, 'sx': 0.08692628650904033})
(.0642069625854492, 'end', 1, {'is_touch': True, 'profile': ['pos'], 'sy': 0.9688715953307393, 'sx': 0.08692628650904033})

Note that the times are now quite low, so this recording will be played dozens of time a second, and i removed over events, such as the keyboard ones.

With this, it was a lot easier to observe the leak on 1.7.2, memory was raising fast enough to observe.

Now, i knew we had a few memory leak fixes in master, so to check we had gotten ride of this one, i just had to replay this very same test on the last version.
And if needed, git-bisect will tell me which one did fix it, without too much effort on my side.

And it was indeed fixed!

Tip me if you like this :)

Position/Size of widgets in kivy.

written by tshirtman, on 1/16/14 12:55 AM.

I see it’s a common struggle for people to understand how to manage size and positions of the widgets in kivy.

There is not much to it, really, just a few principles to understand, but they are better explained by example, i believe.

dumb positionning

Let’s create a root widget, and put two buttons in it.

root = Widget()
b1 = Button()
b2 = Button()
root.add_widget(b1)
root.add_widget(b2)

What will happen when i return root to be used as my root widget? As a root widget, it will have all the space for itself, the two buttons, however, will only use their default size, which is (100, 100), and will use their default position, which is (0, 0), the bottom-left corner of the screen.

We can change b1 and b2 positions to absolute values, let’s change line 2 and 3 to:

b1 = Button(pos=(100, 100))
b2 = Button(pos=(200, 200))

something a little smarter

Now, that doesn’t make for a very flexible UI, if you ask me, it would be better to use the parent size to decide where to place the buttons, no?

b1 = Button(pos=(root.x, root.height / 2))
b2 = Button(pos=(root.width - 100, root.height / 2))

No, that’s not much smarter, it’s still quite dumb actually, it doesn’t even work, why? because we just instanciated root, it’s not even the root widget yet, it doesn’t have its final size, so our widgets will use the default value of (100, 100), for their calculations, no - no.

Let’s fix this with bindings.

b1 = Button()
root.bind(on_size=reposition_b1, on_pos=reposition_b1)
b2 = Button()
root.bind(on_size=reposition_b2, on_pos=reposition_b2)

Where these two functions will be along the lines of:

def reposition_b1(root, *args):
    b1.pos = root.x, root.height / 2 - b1.height / 2

Also, widget have nice alias properties for things like height / 2, x + width, x + width / 2, here is a quick table:

right = x + width
top = y + height
center_x = x + width / 2
center_y = y + height / 2
center = (center_x, center_y)

and actually, pos is just an alias property for (x, y). Alias Properties works both ways, so we can use them to set our positions in a simpler way.

def reposition_b1(root, *args):
    b1.x = root.x
    b1.center_y = root.center_y

A lot of work for not so much, right? That’s because we are not using the right tools!

Let’s jump on a layout, specifically FloatLayout.

FloatLayout to the rescue

Layouts are here to make our life easier when constructing an UI.

FloatLayout is very flexible, it lets you set rules for positions, or do things in the absolute way.

root = FloatLayout()
b1 = Button(pos_hint={'x': 0, 'center_y': .5})
b2 = Button(pos_hint={'right': 1, 'center_y': .5})

pos_hint will make the values relative to the size/position of the parent. So here, for b1 x will be the x of the parent, and center_y will be at the middle between y and top.

Now, if you run this, you may get a surprise, because FloatLayout also made b1 and b2 sizes relative to root.size, and the default value being (1, 1), they both have the same size as the root widget, sometime you want to keep that relative, maybe with a different value, sometime you want to have them keep the value passed in size (or default size value), if so, you can pass (None, None) to size_hint.

b1 = Button(pos_hint={'x': 0, 'center_y': .5}, size_hint=(None, None))

Conclusion

I’ll probably do other posts on the subject, because there is much more to it, but the basic principles are here. I anyway really encourage you to look at this section of the documentation, which go further with the various kind of layouts you have access to.

Tip me if you like this :)

Publisher/Consumer model in Kivy

written by tshirtman, on 1/14/14 1:25 AM.

Few things are worse to an user than an unresponsive UI, well i can think of a crashing UI, of course, but not much more. So, in an event driven environment, it’s important to avoid blocking the UI for too long.

But sometime you have a task that will take an unacceptable time for such constraint, if the task can’t really be chunked, a Thread is likely to be the acceptable solution, but threads have constraints and in Kivy, you can’t update the UI from one, you have to schedule something to happen on the main thread, and update things from here. If the task is chunkable, it’s even easier, but the following idea can apply to both situation.

So, a solution that i find convenient, is to use a publisher/consumer model.

The idea is simple, have a scheduled action each frame do a small part of your task, until a timeout is triggered, and then wait for next frame to continue.
To trigger work, just put (publish) it in a list of tasks to be treated py the consumer.

So let’s start by setting a consumer for adding elements to a list, we don’t want to add 100 elements in the same frame, because that would take too much time.

from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.label import Label

kv = '''
BoxLayout:
    ScrollView:
        GridLayout:
            cols: 1
            id: target
            size_hint: 1, None
            height: self.minimum_height
     Button:
        text: 'add 100'
        on_press: app.consumables.extend(range(100))
'''

class PubConApp(App):
    consumables = ListProperty([])

    def build(self):
        Clock.schedule_interval(self.consume, 0)
        return Builder.load_string(kv)'

    def consume(self, *args):
        if self.consumables:
            item = self.consumables.pop(0)
            label = Label(text='%s' % item)
            self.root.ids.target.add_widget(label)

if __name__ == '__main__':
    PubConApp().run()

Now, i’m only taking one item each frame, it’s probably good enough, but if we have a lot of item, we still may want to take as much is possible, considering kivy loop is frame-limited to 60fps, 100 items will take more than a second, why wait if we have the power? Let’s use a slightly smarter version.

add this import near of the top

from kivy.clock import _default_time as time

then change consume definition to be:

def consume(self, *args):
    limit = Clock.get_time() + 1 / 60.
    while self.consumables and time() < limit:
        item = self.consumables.pop(0)
        label = Label(text='%s' % item)
        self.root.ids.target.add_widget(label)

Of course, this only work because we know creating and adding one widget takes considerably less time than one frame, so it’s not like one of such operation will make our loop hang too long.

Now, filling the consumable list here is done from main UI, but it could totally be done from a Thread, assuming locking is correctly handled (or that extend/pop are atomic, which seems to be the case), so if your filling of work to be displayed is slow, doing the exact same thing as a background task will allow you to do heavy lifting, while keeping your app snappy.

A slightly more demonstrative version of this example can be found here

Tip me if you like this :)

Tags

#FIXME 3G absurd ad_sense alterway aléatoire android animation anonymity atheism bach backlog bash bitcoins blog blogging boldness book books boulot bricolage bépo C canvas captcha captures carte SD censure christianity chroot CLI cli cloudwatt code colors comfort zone command line community company life conferences contest copwatch copwatchnord-idf core-devs cours ct705 culture deb debian debug deformation dehors dessin dev distribute distribution débutant déploiement développement ebooks eeepad eeepc effect ego empty en escher event firefly flappy bird flask fosdem foss fr fun game garden gdb geek culture git github goals graphics grrr gödel hack hackathon hacked hacking hooks i3 images IMAP inspirational isync java jeu jeu video jinja2 jni keyboard keynav kivy kv lame learning lib libre life linux lol macosx magnet mail mailing-list mails maths mbsync meetings memory leak mesh meta mint mirroir MIT module motivational mouse museomix mutt nexus7 no-mouse notmuch nottoomuch offlineimap onycroit opencourseware osc packaging paris passphrase password patch pentacatyl people perte de données ping pip planning plugin positioning pr procrastination programmation progress project projet property proudhon proxy psf publisher/consumer pull-down pygame pyjnius pypi python pythonar qtile rapsberry pi reading recorder references release religion responsive review reviews réseau réseaux sociaux résurection salon screenshots script self service shows shutil shyness sizing solib sortie sousous!!! spam spritz stash status systeme système templating terminal texture texture coordinates Thomas Paine thread thème tiling time time management. tip tips tools transformer tutorial tv twitter typematrix typing ubuntu ubuntu-fr ultimate-smash-friends unity upload images useless usf utils value VDM video vie/mort vim virtualenv visite widget windows wm wmii work workflow workflow. zine études