Kivy多线程和更新屏幕

时间:2017-04-16 01:47:17

标签: python multithreading kivy raspberry-pi3

我写了一段处理多个进程的python代码并且它工作得很好,但我试图在这段代码中添加一个基于Kivy的显示。我已经更新了代码,以便能够使用线程而不是进程,这是有效的。我遇到的问题是我似乎无法让线程写回屏幕。我正在使用@mainthread。我找到了一个示例代码here,并且能够让线程更新屏幕,但由于某种原因,我的代码似乎不起作用。
- >编辑,我删除了我认为与我的问题无关的额外代码。
- >编辑#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'之后,我发现屏幕更新的唯一方法是将其余代码作为线程运行。但我现在似乎无法让线程写回主函数来更新屏幕。最后,一旦文本更新,我将需要更改框的颜色,但这将在适当的时候到来。

1 个答案:

答案 0 :(得分:0)

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

MenuScreen