Kognitivo: challenge your brain!

Point

As I have heard about kivy first time I was completely excited about the possibility to create my own app in python. Also as a programmer I want to optimize everything and my work process is not the last thing in my optimization queue.

You've probably noticed, that everyone has his own biological watches inside, and some people achieve the best results on Monday afternoon, because they come back fresh from the weekend and some (like me) can work most effectively in evening or even at night. So what a deal: meet kognitivo - your personal brain gym trainer :)

TL;DR: I made a cool kivy app, download link at the end of the post. It's for free :)

History

First design sketches

The first idea was to make it in German, so I thought I can reach more people with that, because I'm living here. How naive...

And this is how have realized that (this was the intermediate state, I posted it also on reddit)

And the actual design:

Once I woke up and realized that the original design is just ugly. So I made the new one XD

I met huge difficulties and had to fight against many of platform limits, that was damn hard ;)

Development details

Here you can see the last post about pros and cons I've collected during the development. Here are much more.

Problems

Mipmapping and antialising

One of the most frustrating things - is antialising (actually absense of that). I asked on irc about it and everything I got is something like "your device's opengl is shit, it doesn't support it, fuck you bla bla". Wat? The community is normally friendly, but this issue seems to be just not that important for them.

BUT: without antialising all the graphics you try to create in your app will look like shitty 90's stuff, totally pixelized and useless. I've spent so much time to build the activity diagrams hoping it's going to be prettier on the mobile devices. Nope, it doesn't... It looks most horable while using images:

You can actually work it a bit around: set mipmap: True in your image class, and it looks much better. But here you start playing a lottery: mipmapping creates mip maps (your cap!) with 1x1,2x2, ..., 64x64,... sizes and is going to show it like in this resolution. So if the widget has 511 pixels of width, it uses 256x256 texture, you can imagine how it looks like.
As a bonus you can just forget about dpi aware media, it's not needed on most devices anyway, cause "their opengl is shit" (c).

Widget's parents

Kivy won't let you move a child widget from one parent widget to another. You'll get an error, that the widget already has a parent. Event if it's a big-big screen having the half of the app's functionality. Once a child, always a child.

Kivy language

As I just started learning kivy, I have numbed as I saw, that there is something like 'kivy language'. It sounds already dangerous.
And now a small fable:

As I was working for the company M., I got a colleague, let's say Martin. Martin is a really good programmer and also a good sociopath. Once he wanted to get into integrational testing for django apps and he found webtest - amazing library from pylons. But somehow he has decided, that lxml and beautiful soap are just to complicated and he wrote his own(TM), simple(TM) and fast(TM) parser for html responses. Good idea. I've pointed him, that he reinvents the wheel and he writes his own beautiful soap but much worse and he answered, that he has 10 years experience which I don't have, so I just had to shut up. And I did. His small personal frankenstein crashed each day and I believe he implements xml grammars support right now.

So what is this about? There enough 'languages' for doing this. Why not yaml? Why not xml? Why not json? I understand you needed python injections, but who has decided that developing your own(TM), simple(TM) and fast(TM) language is the best decision? I hope some kivy developers gonna read my small novel, because I still don't have 10 years of the experience, and I can shut up, but I wish you guys to get succeeded, because kivy is awesome. And this decision is the bad one.

So maybe want some proofs. Here they are:

  • each kv file can be loaded by name or as an inline string in the source code. Imagine how will you organize that if you have 100 widgets, how will you connect kv representation with python logics for particular widget.

  • a kv file is not somehow isolated from another kv files, so if you accidentally named some two widgets with the same name - it's your problem.

  • kv files can't depend on or include each other. This one is especially comfortable when you try to inherit widgets. And you gonna do this, believe me. I know about new features coming in 1.8.1, but see my fable... Maybe you wouldn't even need it, if you took something functional already.

  • python injections are awesome, I like it. But what about multiline statements? Probably the idea was, that if you want to write a multiline, you have to do it in python. But what if I just need it? And also: event handlers defined in kivy seem to have the highest priority (possibly incorrect), and if so I just must write the handler in kivy, and it should be possible.

  • widgets inheritance showed up in 1.8.0 (called dynamic classes). Great! So I say <BigBoob@Big+Boob>. Hm. But there is -%class_name% instruction saying not to inherit graphic instructions of the parent. Hm. So what does that <-BigBoob@Big+Boob> mean? How the big boob look like? Is it big? Is it a boob still? So what if I want my new ModerateBoob act like a boob and generally be like big, but not graphically, how should I say it? How to cancel graphic instructions of one of the parents? It's all pretty raw...

  • one more sentence to event handlers: Imagine the situation, you have a widget class 'A' is inherited from 'B' and kv instructions for that. Let's say it has a property 'foo'. So I define the method on_foo in both A and B, bind some other methods over bind in constructor and define on_foo field in both kivy files. Like this python

class B(Widget):  
  def __init(self, *args, **kwargs):
      ...
      self.bind(on_foo=self.bla)

  def on_foo(*args,**kwargs):
    ...
class A(B):  
  def __init(self, *args, **kwargs):
      super(A,self).__init__(*args, **kwargs)
      ...
      self.bind(on_foo=self.bla)
  def on_foo(*args,**kwargs):
      ...

kv

<A>:  
  on_foo: bla

<B>:  
  on_foo: bar

What happens then? In which order will be the 'foos' executed? Some magic guaranteed. And one more: there is on_press handler, but there is no press property in Button widget. What happens if I define press property on Button object?

  • as expected, tracebacks from kv files are completely useless. For example:
...
     level + 1, lines[i:], spaces)
   File "/home/eins/.virtualenvs/kognitivo/local/lib/python2.7/site-packages/kivy/lang.py", line 1271, in parse_level
     if current_property[:3] == 'on_':
 TypeError: 'NoneType' object has no attribute '__getitem__'

Which kv file is that? Which line is that? Which statement is that? Nobody knows. But it's surely current property.

Properties

I think it's pretty smart idea to use observer pattern for properties. But what the hell the handler won't be called if assigned value is the same as the current one? Spent half a day to realize why the first rectangle in the diagram looks different as the other ones. Yeah, it has number 0, so if you say 'on every assignment of number do foo', foo wan't be called if number hasn't change. Weird...

Geometry API

This one hurts by butt massively. Try to put ellipse into grid layout, make it circle and place it in the center of the grid cell. Or just define a square widget. But be aware not to eliminate your computer!
Also Ellipse class doesn't have center property. Mathematically clearly correct, but absolutely no use.

Some cross platform philosophy

You can develop for multiple platforms with kivy, it's awesome. But the thing is, as much as you want to make something platform specific, for example use notifications or execute some code in a separate service, you need to know how the point platform works. So due to this fact, if you want to develop for Android and iOS, you need to know how their SDKs work and this is frustrating, because I was hoping to avoid it with kivy. That was my aim actually. I have figured out how to use notifications on Android, but the service still doesn't work, and this was one of the most important ideas for my app: the user should be notified, that kognitivo wants to measure his or her cognitive activity. I was just desperate and left it for better times...

Benefits

Python, Python, Python

Obviously the biggest benefit you get is the possibility to develop the mobile applications with python. I think the most beautiful thing in it is to use the almightiness of it's frameworks. I used sqlalchemy and sqlite as a backend, and it worked like a charm! Python is the most powerfull language because of it's frameworks, you can even start Django on your smartphone! It's amazing! Or twisted for asynchro communication with server. Or nltk for in-app natural language processing. Or maybe you want make a mobile equations solver with scipy and numpy. This makes all the dreams come true. You surely think that it's a bad idea to try to make a hard weight scientifical computing on the smartphone. But why shouldn't we at least try it? Maybe it's gonna work! Numpy's most hard computing parts are written in C and Fortran, kivy's - in Cython and the modern smartphones got quad core processors, so why not? 'I got a quad core, you have a scipy, so what's the problem, let's try slightly'

Emulation

I was also totally inspired by being able to start my app on a desktop. As I started to develop kognitivo, somewhere inside I didn't believe, that the same app will work on my smartphone. Just because I have never seen something like that. It also makes your development process more effective, no emulation - no pain.

Support

I think the main reason of Django's success story is community and documentation. Kivy tooks the same way and it's right. The guys from irc channel and mail list were always there, even if I have not solved my problems with that. Sometimes this shit is just not working. Everything breakes sometimes.

Improvement offers

During development of kognitivo I have missed some things, which would look pretty natural for me, but are still not there.

Settings vs configuration

Kivy uses Config object to store the application configuration, can serialize it in the application store and so on. Looks pretty nice, but I always missed Django's settings concept. Settings are something different: they are not dynamical parameters of your app and can distinguish on different platforms.

I have solved it like this in my app: I defined settings.py in my app's root and placed the settings there. This is the example:

# general
KIVY\_VERSION\_REQUIRE = '1.8.0'  
KIVY\_GRAPHICS\_HEIGHT = None  
KIVY\_GRAPHICS\_WIDTH = None  
KIVY\_LOG\_LEVEL = "info"

# database
DATABASE_DEBUG = False  
DATABASE_ADDRESS = 'sqlite:///db.sqlite3'

#screens
ACTIVITY\_SHOW\_STATS\_PERIOD = 30  # days

# test flight
TESTFLIGHT_TOKEN = None

# tasks
ACTIVE\_TASK\_CLASSES = []  
INACTIVE\_TASK\_CLASSES = []  
INTRO\_HINT\_LENGTH = 5  
from kivy.utils import platform, get_color_from_hex

try:  
    platform_settings = "from settings_" + platform() + " import *"
    exec platform_settings
except ImportError:  
    pass

I don't want my users to change kivy's required version, or log level. I also don't want them to change my testflight token, but I want to change it by myself quick. It's quite easy concept, which is somehow not there out-of-box. I have also stolen common settings_local mechanism of django and used it to store platform specific options in a separate file. Here is the example of settings_android.py

TESTFLIGHT_TOKEN = "xxx"  

Not that much I know XDD But I think I going to place more stuff there if I start to develop for two platforms simultaneously.

One more for linux - settings_linux.py

KIVY\_GRAPHICS\_HEIGHT = '800'  
KIVY\_GRAPHICS\_WIDTH = '480'  

Usage example in foo.py:

from settings import INTRO_HINT_LENGTH  
...

class IntroHint(FloatLayout, BackgroundMixin):  
    LENGTH = INTRO_HINT_LENGTH  # intro hint length in seconds

Themes

The default theme of the widgets in kivy is just too general. It looks very android-like, but not everyone wants his or her app look like this. For instance me. So I was wondered if I can define it somehow once, using the most general categories like brand colors, text color, text font, fill colors and so on. I have solved in my settings! I added style variables and used it everywhere to draw the elements like this (addition to settings.py):

from kivy.utils import platform, get_color_from_hex  
KIVY_FONTS = [  
    {
        "name": "RobotoCondensed",
        "fn_regular": "data/fonts/RobotoCondensed-Light.ttf",
        "fn_bold": "data/fonts/RobotoCondensed-Regular.ttf",
        "fn_italic": "data/fonts/RobotoCondensed-LightItalic.ttf",
        "fn_bolditalic": "data/fonts/RobotoCondensed-Italic.ttf"
    },
    {
        "name": "FreeSerif",
        "fn_regular": "data/fonts/FreeSerif.ttf"
    }
]
KIVY\_DEFAULT\_FONT = "RobotoCondensed"

TEXT_COLOR = get_color_from_hex("666666ff")  
TEXT\_COLOR\_SEMITRANSPARENT = get_color_from_hex("66666680")  
FILL_COLOR = get_color_from_hex("7e7edcff")  
FILL\_COLOR\_SEMITRANSPARENT = get_color_from_hex("7e7edc80")  
FILL\_COLOR\_QUASITRANSPARENT = get_color_from_hex("7e7edc33")  

And then in my kognitivo.kv (root kv file, loaded automatically, here more about connecting a font):

#:import KIVY_DEFAULT_FONT settings.KIVY_DEFAULT_FONT
...
<Label>:  
    font_name: KIVY_DEFAULT_FONT

or you're just saying

#:import settings settings
...
color_selected: settings.TEXT_COLOR  

It makes your app look the same way everywhere and be based on couple of graphical units like font and color. Kivy could support something like that out-of-box, so you could change these properties globally without any need to hack the framework.

Animations

Kivy gives a good possibility to animate the elements with Animation object. You can animate on properties with different time functions from predefined set (more than enough) or define your own. But actually what I was looking for is the way to bind animations to some events in declarative manner, to say widget animates property x, .3 seconds long with the time function bla on click, or swipe or so. Of course you can define it by yourself, but I wanted to do it in kivy language! It's surely the best place for that.

I have tried to build something like AnimationMixin, which is giving the widget animation field; the children of it OnPressAnimationMixin, which starts animation on press and OnPropertyAnimationMixin which animates to property's newly assigned value from the old one using animation. Of course you can write it by yourself once again, but this is one of the things I'd like to see in the frameworks API. Animation is closely connected with events, it feels natural. More about my animation mixins here

Here's what I mean by 'declarative manner':

<CircleDiagram>:  
    label: label
    color: (1,1,1,0.4)
    animate_properties: ['part', 'opacity']
    duration: 5
    transition: 'out_elastic'

Property decorator

I have never implemented this one, but I thought it could be quite useful to decorate properties to bind them with handlers like this:

class Foo(Widget):

  def handle_prop(self, instance, value):
     do_something()

  @handler(handle_prop)
  prop = NumericProperty()

Looks better as to bind it in constructor.

I got more ideas, but these are the best ones. You can find more information about my kivy adventures on this page also. And actually this post is just too long already XD

Support kivy!

One day I saw the small speech of Guido about kivy on PyCon, and I didn't like what he said at all. His point was, that the guys from Dropbox said, that kivy's perfomance was not good enough for them.

Perfomance is not the most important parameter, until it's unacceptably slow. For instance Django ORM is not the best one in this area, but it works and it's simple to use. A good dictator like Guido should take care about what he's saying, and in this case his words damaged the project without any reason for that. He didn't ever tried this! He never said which version of kivy was used, which kind of device was that and so on. It was just irresponsible.

So the first thing - work with kivy, write your own weekend project, it's not that complicated as it appears to be and share your impressions! Take part on kivy programming contest and popularize the project by yourself. Write your own success story XD

Secondly you can of course contribute, the code is placed on github. I have never done that, I think I'm just too stupid for that XD I'm only 24, but if you got a beard, the community will accept you :)

And thirdly

Do you like that? Support me!

Try my app, it's on google play here, rate it and write a comment:

Get it on Google Play

More features

Here is the plan:

  • notifications
  • calendar view
  • activity prediction
  • more task types
  • translation
  • more fancy animations

and more and more awesomeness. I also going to publish the trello board with my ideas.

Thank you!


comments powered by Disqus