How to make Kivy app run inside a thread?

Viewed 534

I have a very simple Kivy app that runs perfectly like this:

def run_form():
    rules_form().run()


if __name__ == '__main__':
    run_form()

But I want the program to continue while the rules_form window is open. I tried the following:

def run_form():
    rules_form().run()


if __name__ == '__main__':
    t = Thread(target=run_form)
    t.start()
    print("Hi")

When I do this, the program DOES print "hi", but the Kivy window that opens is white and is not responding.

How can I make it work?

1 Answers

I have recently been working with threads with Kivy and have done as Inclement has suggested. That is have Kivy run in the main thread then dispatch other tasks to another thread.

Here is an example of an app I created that uses threads:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.textinput import TextInput
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.label import Label
import webbrowser
from wiki_recommendations import WikiSearcher


class SearchBar(TextInput):
    articles = ListProperty()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bind(text=self.on_text)
        self.bind(articles=self.on_articles)

    def on_text(self, *args):
        WikiSearcher().get_search_results(self.text, self)

    def on_articles(self, *args):
        self.parent.ids['recommendations'].update_recommendations(self.articles)


class SearchItem(ButtonBehavior, Label):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.url = ''

    def on_release(self):
        webbrowser.open(self.url)


class Recommendations(BoxLayout):

    def update_recommendations(self, recommendations: list):
        for (search_item, recommendation) in zip(self.children, recommendations):
            search_item.text = recommendation
            search_item.url = 'https://en.wikipedia.org/wiki/' + recommendation


kv = """
<SearchItem>:
    canvas.before:
        Color: 
            rgba: [0.8, 0.8, 0.8, 1] if self.state == 'normal' else [30/255, 139/255, 195/255, 1]
        Rectangle:
            size: self.size
            pos: self.pos
        Color:
            rgba: 0, 0, 0, 1
        Line:
            rectangle: self.x, self.y, self.width, self.height
            
    color: 0, 0, 0, 1
    
BoxLayout:
    canvas.before:
        Color: 
            rgba: 1, 1, 1, 1
        Rectangle:
            size: self.size
            pos: self.pos
            
    orientation: 'vertical'
    padding: self.width * 0.1
    spacing: self.height * 0.1

    SearchBar:
        size_hint: 1, 0.2
        multiline: False
        font_size: self.height*0.8

    Recommendations:
        id: recommendations
        orientation: 'vertical'
        SearchItem
        SearchItem
        SearchItem
        SearchItem
        SearchItem
"""


class SearchApp(App):

    def build(self):
        return Builder.load_string(kv)


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

This app has users type in a search bar and automatically recommends Wikipedia pages. A screen shot can be seen below:

enter image description here

This code uses threads to get the recommendations (so as not to interrupt the users typing). A different file called wiki_recommendataions.py creates these threads then dispatches an API call.

import wikipedia
import threading


def thread(function):
    def wrap(*args, **kwargs):
        t = threading.Thread(target=function, args=args, kwargs=kwargs, daemon=True)
        t.start()

        return t
    return wrap


class WikiSearcher:

    @thread
    def get_search_results(self, text: str, root):
        """
        Gets the top Wikipedia articles
        :param text: The text to be searched.
        :param root: The object that calls this function - useful for returning a result.
        :return:
        """
        root.articles = wikipedia.search(text)

As the user types in the search bar the WikiSearcher object is instantiated and the get_search_results() is called. This updates the articles property of the SearchBar which in turn updates the children of Recommendations (a BoxLayout). These children are essentially just Buttons which direct the user to the recommended page when they are pressed.

Related