如何从线程更新Kivy元素?

14 投票
1 回答
5951 浏览
提问于 2025-05-10 15:48

我有一个套接字客户端,每次收到消息时都会调用一个 View() 类。我把代码分开写,这样这个类可以简单地使用 print() 或其他任何显示方法,随我喜欢。不过,Kivy 似乎不太喜欢这种方法。我扩展了 Kivy 的 BoxLayout 类来做我的视图,并且可以调用 message() 函数。这个类大概长这样:

class View(BoxLayout):
    def __init__(self, **kwargs):
        super(View, self).__init__(**kwargs)
        self.btn = Button(text='Default')
        # Bind button press method
        self.btn.bind(on_press=self.message)
        self.add_widget(self.btn)
    def message(self, message):
        self.btn.text = 'Meow'
        self.add_widget(Button(text='Meow'))
        print(str(message))

确实调用了 message 函数,并且它能打印出来,但界面并没有更新。不过,当我按下按钮时,界面会更新,并且也会打印。

我尝试使用 StringProperty 来修改按钮的文本,但也没有成功。顺便提一下,如果我做的事情完全不可行,我想之后绘制一个由 width * height 个按钮组成的整个界面,像一个棋盘一样。

任何建议都非常感谢,这让我快疯了。


编辑 1* 我根据一些评论进行了尝试,试了几种方法。我添加了一个 Clock 类,并让它安排从 View 调用的 update() 方法。这个更新方法简单地改变了一些元素的文本。我注意到当我安排它时,它确实有效,如下所示:

def update(self, *args, **kwargs):
    self.btn.text = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase) for i in range(32))
def message(self, message):
    try:
        print(message)
        self.text = 'sending'
    except Exception as e:
        print(e)

现在这个线程简单地将文本属性分配为在 message() 中看到的内容。定期触发的 update() 方法也有效,能够分配随机文本。不过,现在的问题是它无法设置文本。这段代码不行:

def update(self, *args, **kwargs):
    self.btn.text = self.text

我一定是在别的地方做错了,有什么建议吗?


编辑 2* 我正在调试的错误可以在 这里 找到。

相关文章:

  • 暂无相关问题
暂无标签

1 个回答

14

因为你没有提供完整的示例代码,我只能猜测你在做什么。看起来你有一个事件(比如收到消息)在一个线程上运行,而你想在这个事件发生时显示一些文本。你需要把用户界面的更新“推送”到主线程,但你不需要用Clock进行定期更新,你只需要用Clock.schedule_once安排一次性的调用。

from functools import partial

def update(self, text, *a):
    self.btn.text = text

def message(self, message):
    Clock.schedule_once(partial(self.update, message), 0)

正如inclement提到的,你可以通过使用@mainthread这个装饰器,自动将更新“推送到主线程”。

@mainthread
def update(self, text):
    self.btn.text = text

def message(self, message):
    update(message)

这样,每当你调用update时,它都会在主线程上执行。

撰写回答