Kivy 多线程和更新屏幕

Kivy multi-threading and update screen

我写了一段 python 处理多个进程的代码,它工作得很好,但我正在尝试向这段代码添加一个基于 Kivy 的显示。我已经更新了代码,以便能够使用线程而不是进程,这很有效。我遇到的问题是我似乎无法让线程写回屏幕。我正在使用 @mainthread。我找到了示例代码 here,并且能够让线程更新屏幕,但出于某种原因,我的代码似乎无法正常工作。
->编辑,我删除了我认为与我的问题无关的额外代码。
->Edit #2:,我仍然将代码简化为我认为是裸露的。仍然显示我遇到的问题。根据我的阅读,可能是 x.join 代码导致了我的问题,并锁定了主循环。我需要 运行 while 循环 5 次,但一次只能 运行 2 个线程。需要在线程完成之前将 while 循环保持在非阻塞状态。

这里是menu.kv

<ScreenManagement>:
    MenuScreen:
    ProgramScreen:


<MenuScreen>:
    name: 'menu'
    AnchorLayout:
        GridLayout:
            cols: 2
            Button
                text: "Start Application"
                on_release: root.routine()

<ProgramScreen>:
    filler1: filler1
    filler2: filler2
    filler3: filler3
    filler4: filler4
    name: 'program'
    GridLayout:
        cols: 4
        Button:
            id: filler1
            text: 'Filler 1'
            halign: 'center'
            padding_y: '300'
            bcolor: 1,0,1,1
        Button:
            id: filler2
            text: 'Filler 2'
            halign: 'center'
            padding_y: '300'
            bcolor: 1,0,0,1
        Button:
            id: filler3
            text: 'Filler 3'
            halign: 'center'
            padding_y: '300'
            bcolor: 1,0,1,0
        Button:
            id: filler4
            text: 'Filler 4'
            halign: 'center'
            padding_y: '40 '
            bcolor: 0,0,1,1

这是我的 main.py

from kivy.app import App
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.lang import Builder
from kivy.properties import *
from kivy.core.window import Window
from kivy.clock import Clock, mainthread
import threading
import time


########################################################################
class ScreenManagement(ScreenManager):
    pass

class MenuScreen(Screen):
    def routine(self):
        self.parent.current = 'program' 
        threading.Thread(target=ProgramScreen().build).start()

class ProgramScreen(Screen):

    @mainthread
    def update_label(self, int_counter, new_text):
        if int_counter == 0 :
            print "here ",  int_counter, new_text
            self.filler1.text = new_text
        elif int_counter == 1 :
            self.filler2.text = new_text
        elif int_counter == 2 :
            self.filler3.text = new_text
        elif int_counter == 3 :
            self.filler4.text = new_text
        else:
            self.filler1.text = "fault"

#dummy function to be replaced with a function that will call GPIO for input and feedback.
    def func (self,value):
        print 'func', value, 'starting'
        for i in xrange(10*(value+1)): 
            if ((i%3) == 0):
                self.update_label(int(value),str(i))
                print value, i 
                time.sleep(1)
        print 'func', value, 'finishing'

    def build(self):
        NumberofFiller = 2
        NumberofCells = 5
        CellCounter = 0
        while CellCounter < NumberofCells:
            try:
                threads = []
                print ('Counter:',CellCounter,'Fillers:',NumberofFiller,'Cells:',NumberofCells)     
                for i in range (NumberofFiller):
                    t = threading.Thread(target=self.func, args=((CellCounter%NumberofFiller),))
                    t.start()
                    threads.append(t)
                    CellCounter = CellCounter +1

                for x in threads:
                    #Problem I believe is here.
                    x.join()
                    #Need a way to pause the While loop for the first set of threads to finish before starting the next threads.
#                print (threads)
            except (KeyboardInterrupt, SystemExit):
                functions.cleanAndExit() 


########################################################################
#Builder.load_file("Menu_red.kv") #Not needed
class Menu_red2App(App):
    def build(self):
        return ScreenManagement()

#----------------------------------------------------------------------
if __name__ == "__main__":
    Menu_red2App().run()

在执行 self.parent.current = 'program' 之后,我能找到的更新屏幕的唯一方法是 运行 将其余代码作为一个线程。但是我现在似乎无法让线程写回主函数来更新屏幕。最后,一旦更新了文本,我将需要更改方框的颜色,但会在适当的时候出现。

问题在于,当您在 routine 方法中启动外线程时,您将目标设置为 ProgramScreen().build,这实际上会创建一个新屏幕。所以它不会影响你在屏幕管理器中的屏幕。
要将目标设置为您在屏幕管理器中获得的 ProgramScreenbuild 方法,您可以通过其名称获取该屏幕。
你已经给它起了名字 program.
所以你的 MenuScreen class 应该是这样的:

class MenuScreen(Screen):
    def routine(self):
        self.parent.current = 'program' 
        threading.Thread(target=self.manager.get_screen('program').build).start()