Skip to content

Instantly share code, notes, and snippets.

@Guhan-SenSam
Last active April 20, 2024 08:15
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Guhan-SenSam/9dfb11b7bfd8fd24561f4fcd9ff0d5de to your computer and use it in GitHub Desktop.
Save Guhan-SenSam/9dfb11b7bfd8fd24561f4fcd9ff0d5de to your computer and use it in GitHub Desktop.
Methods to Optimizing Kivy Performance

Many people state that kivy is slow. While this may be true it is mostly due to that python is slow to run on android devices.Thus it is in the programmer's hands to properly optimize their code so as to create a performant application.

Most of the lag on android devices runing kivy apps arise due to widget creation. Widget creation remains the slowest step in a kivy app. Here are some of the methods that I follow to optimize my apps and ensure I can hit 60fps even on old devices

Optimization Methods:

1. Load all your widgets at start

One very simple method to improve performance of your app is to load all the heavy widgets or even all your apps widgets at the start of your application. However this does have the draw back of causing longer load times for your app.

The way to cover up these long load times would be too create an interesting app splash screen with some animations. Buildozer supports lottie animations(https://github.com/tshirtman/p4a_lottie_demo Courtesy of tshirtman).

Loading all your widgets at start means that you can simply add your widgets to screens through out your code and the performance impact would become negligable.

2. Use Animations

If you do need to create your widgets during program run time you can use animations to hide the lag. Properly placed animations that animate opacity can be used to hide the time required to load widgets. The user would perceive this just as a transition and not as lag.

  1. First create your widget and give it an opacity of zero
  2. Add the widget to the layout that it needs to be in
  3. Add an animation for that widget's opacity. Set the duration to something small like .2 seconds.

You can take this further if you have to load mutiple instances of the same widget. Rather than loading them all together. Load one widget and on its animation complete load the next one. Keep the animation duration's short so that they dont add up to become too long

"""
Loading multiple instances of a widget over a period of time to prevent lag.
Useful for more complex widgets than just a button.
"""
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.uix.button import Button
from kivy.animation import Animation
KV = '''
GridLayout:
id:container
cols:5
rows:20
'''
class MessengerApp(App):
counter = 0
def build(self):
self.kv = Builder.load_string(KV)
return self.kv
def on_start(self):
Clock.schedule_interval(self.btn_create,0.3)
def btn_create(self,time):
if self.counter<50:
btn = Button(text = str(self.counter))
btn.opacity = 0 # Set the opacity of the button to 0
self.kv.add_widget(btn) # Add the button
Animation(opacity = 1, duration = .25).start(btn) # Duration is < than clock duration
self.counter +=1
if __name__ == '__main__':
MessengerApp().run()

3. Using a Producer Consumer Model To Load Widgets

By default when you create a widget. Kivy will create the entire widget within a single frame. But if your widget is complex and has numerous widgets and layouts within it this can cause lag. This is because kivy will be holding back from displaying that frame until the widget is entirely created which will appear to your user as the program freezing or hanging.

For certain cases using a loading bar to depict that the app is in fact still running but just loading data is useful but not pleasing. It makes the user wait for the app to complete its operation which can be frustating. There has to be some sort of visual action.

Instead you could slowly load instances of your widgets over multiple frames. Basically the code works in a way where you would create a widget in a frame and then the next instance in the next frame and so on. Thus large quantities of data can be loaded quickly.

Check this link for more info (https://blog.kivy.org/2014/01/producerconsumer-model-in-kivy/)

Note: This method is only effective if you have to load a large amount of simple widgets. If you have to load complex widgets I would suggest using animations like in method 2. Or else you could break your complex widget into smaller simpler ones and load them over multiple frames.

4. Simplyify your widgets as much as possible

This is a really painful task to do but definitely worth it. If you can remove un-needed layouts and widgets you can significantly improve the performance of your apps.

Some tips:

  • Use Boxlayouts and GridLayouts as I found these layouts can help position your widget more efficiently than floatlayouts.
  • Join Labels together. Rather than having three label widgets. Join them together to form a single label and use markup to modify the font style/color and any other property.
text_to = '''[font=Roboto-Black.ttf][size=18sp][color=' +self.rgb2hex(self.text_color) + ']' + 
a[0] + 
'[/color][/size][/font]' + 
'\n' + 
'[font=Roboto-Regular.ttf][size=16sp][color=' +self.rgb2hex(self.secondary_text_color) +']' + 
a[1] + 
'[/color][/size][/font]'''

This is an example of combining two completely differnet labels into a single label object. I pass the text as this string and when displaying it would appear as they are separate labels but in fact they are a single label. This in some cases can cut the amount of widgets to create in half.

5. Use Threading Wherever Possible

There is no way to update the user interface or create widgets in a thread as kivy doesnt garuntee stable behaviour in this way. Instead run your ui on your main thread and run your other app functions on a secondary thread. This if done properly and for the correct use case can be very effective in improving performance.

Check here for info on python threading in a simple way: (https://www.geeksforgeeks.org/multithreading-python-set-1/)

6 Lazy Load Screens

This method can be used to signifcantly increase your app load times on android. It works by loadig only the mainscreen and then slowly loading the other screens of your app over time. The code is by @Kulothungan16 ... Check his repo for more info (https://github.com/Kulothungan16/kivy-lazy-loading-template)

7 Miscellaneous Stuff

  • Reduce your import statements(improves start time but not by a lot). Wrong:
import kivy

Correct:

from kivy import #Whatever you need to import
  • Be smarter with widget placement. Try to group widgets together into a single instance. Dont be afraid to play with kivy source code or create your more efficient custom widgets. Remember the kivy library must remain customizable so there is only so much that can be removed and optimized. But if you create your own custom widgets you can remove unnecessary code that could speed up widget creation.
  • Dont use too much animation instances as this is slow on android
  • Reduce animation step value to 1/30 so that the animation only updates every other frame.Useful especially for opacity animation as the difference is not visible at all. You can go as low as 1/10 for opacity based animations.
Animation(opacity = 1, d=.3, step = 1/30)
  • Try to do some preloading of widgets in the background, like when the user is not touching the screen for a certain period of time. You can then stop this loading on the next touch down event etc. But remember dont to much background loading as this can cause a large amount of memory usage and could result in an even slower user experience
  • Reuse widgets as much as you can. Rather than recreating a widget everytime some value chnages reuse already created ones and only change the necessary values.
@tinashemapurisa
Copy link

Thank you for thsi post ,really helpfull

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment