首页 > 解决方案 > 如何让 Kivy 应用程序在线程内运行?

问题描述

我有一个非常简单的 Kivy 应用程序,它可以像这样完美地运行:

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


if __name__ == '__main__':
    run_form()

但我希望程序在 rules_form 窗口打开时继续运行。我尝试了以下方法:

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


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

当我这样做时,程序会打印“hi”,但打开的 Kivy 窗口是白色的并且没有响应。

我怎样才能让它工作?

标签: pythonmultithreadinguser-interfacekivykivy-language

解决方案


我最近一直在用 Kivy 处理线程,并且按照 Inclement 的建议做了。也就是说,让 Kivy 在主线程中运行,然后将其他任务分派给另一个线程。

这是我创建的使用线程的应用程序示例:

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()

这个应用程序让用户在搜索栏中输入并自动推荐维基百科页面。屏幕截图如下所示:

在此处输入图像描述

此代码使用线程来获取建议(以免中断用户输入)。名为wiki_recommendataions.py的不同文件创建这些线程,然后调度 API 调用。

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)

当用户在搜索栏中键入时,WikiSearcher对象被实例化并被get_search_results()调用。这会更新 的articles属性,而SearchBar后者又会更新Recommendations(BoxLayout)的子项。这些孩子本质上只是Buttons在用户被按下时将用户引导到推荐页面。


推荐阅读