我如何从一个线程更新Kivy元素?

时间:2021-05-21 20:42:13

I have a socket client that calls a View() class every time it receives a message. I've split my code in such a way such that this class can simply use print() or any other display method, as I like. However, it seems that Kivy is not fond of this method. I've extended Kivy's BoxLayout class for my view and can call the message() function. The class looks something like this:

我有一个套接字客户端,每当它收到消息时就调用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))

The message function is indeed called and it prints but the interface does not update. When I press the button however, it does update the interface as well as print.

消息函数确实被调用并打印,但是接口没有更新。当我按下按钮时,它会更新界面和打印。

I've looked into using a StringProperty to modify the button text but have failed that too. Just as a note, in case what I'm doing is completely infeasible, I'm trying to later draw an entire interface consisting of width * height buttons, in the form of a board.

我曾考虑过使用StringProperty来修改按钮文本,但也失败了。就像一个注释,如果我做的是完全不可行的,我试着以后画一个完整的界面,包括宽度*高度的按钮,以一个板的形式。

Any input is very much appreciated, it's been driving me insane.

非常感谢你的建议,它让我发疯。


EDIT 1* I've followed on a few comments and tried a few things out. I've added a Clock class and have it schedule an update() method from View. The update method simply changes the text of a few elements. I've noticed it works when I schedule it to, as shown below:

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)

The thread now simply assigns the text property as seen in message(). The periodically triggered update() methods works too, assigning random text. Now the issue however that it cannot set the text. This does not work:

线程现在只需分配message()中的text属性。周期性触发的update()方法也可以工作,分配随机文本。然而,现在的问题是它不能设置文本。这并不工作:

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

I must be doing wrong elsewhere, any suggestions?

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


EDIT 2* The error I'm trying to debug is here.

1 个解决方案

#1


3  

Since you don't post a full working example, I can only guess at what your doing. It seems you have an event (incoming message) on a thread, and you want to display some text when this occurs. You need to 'push' UI updates to the main thread, but you don't need to do periodic updates with Clock, you can just schedule one-time calls with Clock.schedule_once.

既然你没有发布一个完整的工作示例,我只能猜测你在做什么。似乎您在线程上有一个事件(传入消息),并且您希望在发生该事件时显示一些文本。您需要“推送”UI更新到主线程,但不需要使用时钟进行周期性更新,只需使用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)

As inclement mentioned, you can do this 'push to main thread' automatically with the @mainthread decorator:

正如前面提到的,您可以使用@mainthread decorator自动地执行“推到主线程”:

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

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

This way, whenever you call update it will be executed on the main thread.

这样,无论何时调用update,它都将在主线程上执行。

#1


3  

Since you don't post a full working example, I can only guess at what your doing. It seems you have an event (incoming message) on a thread, and you want to display some text when this occurs. You need to 'push' UI updates to the main thread, but you don't need to do periodic updates with Clock, you can just schedule one-time calls with Clock.schedule_once.

既然你没有发布一个完整的工作示例,我只能猜测你在做什么。似乎您在线程上有一个事件(传入消息),并且您希望在发生该事件时显示一些文本。您需要“推送”UI更新到主线程,但不需要使用时钟进行周期性更新,只需使用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)

As inclement mentioned, you can do this 'push to main thread' automatically with the @mainthread decorator:

正如前面提到的,您可以使用@mainthread decorator自动地执行“推到主线程”:

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

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

This way, whenever you call update it will be executed on the main thread.

这样,无论何时调用update,它都将在主线程上执行。