如何从线程中更新Kivy元素?

14

我有一个socket客户端,每次收到消息时都会调用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))

消息函数确实被调用并打印了,但界面没有更新。但我按下按钮后,它会更新界面并打印。我尝试使用 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)

现在该线程只需像中所示一样分配文本属性。定期触发的update()方法也可以工作,分配随机文本。然而问题是它无法设置文本。下面是无效的:

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

我肯定是在其他地方做错了,有什么建议吗?


EDIT 2* 我要调试的错误在这里


你不能从除主线程以外的线程修改UI元素或属性。请查看Kivy的Clock类,以在主线程上调度函数运行。 - bj0
除了@bj0的答案之外,您还可以使用@mainthread装饰器自动将此方法应用于函数。 - inclement
嘿,我已经编辑了帖子并找到了更多的发现。非常感谢你的帮助和使用Clock()的建议,但正如上面提到的,它仍然没有完全功能。 - kiyui
1个回答

14

由于你没有提供一个完整的工作示例,我只能猜测你在做什么。看起来你有一个事件(传入的消息)在一个线程上,当发生这种情况时你想显示一些文本。你需要将 UI 更新“推送”到主线程,但是你不需要使用 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,它都将在主线程上执行。


我的初衷是不发布完整的工作示例,因为这是作业的一部分,我不想让我的工作被抄袭。我现在已经将GitHub上的repo公开了,可以在这里找到。我已经删除了时钟方法并回到了使用@mainthread。您要查找的文件是client.py - kiyui
通常情况下,如果您不想(或无法)发布您正在处理的代码,请创建一个最小化的裸骨应用程序,其中包含您正在尝试解决的问题。这有助于其他人能够重现您的问题。 - bj0
3
看了一下你的代码,似乎不是Kivy的问题,而是一个multiprocessing问题。独立进程不共享内存,因此很多对象之间的引用将不能正常工作。使用线程代替进程,应该能按你想的方式工作。 - bj0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接