2017-04-16 309 views
0

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

这里是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'后,运行该休息的代码作为一个线程。但我现在似乎无法让线程回写主功能来更新屏幕。最后,一旦文字被更新,我将需要改变这些框的颜色,但那会在适当的时候出现。

+0

您的问题将帮助小例子。 https://stackoverflow.com/help/mcve – EL3PHANTEN

+0

你好,我已经删除了“我不认为是问题的一部分”的“额外”代码。 – user1086924

回答

0

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

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

这就是我以为我所做的。主程序创建一个线程'threading.Thread(target = ProgramScreen()。build,args =(NumberofFiller,NumberofCells,))。start()',它应该与主程序无关。然后这将调用单独的线程。我相信问题是我“加入”了线程(我不想继续下去,直到所有子线程完成)。但是,我看不出这会如何阻止主代码。目前没有任何东西与Raspberry Pi连接。 – user1086924

+0

@ user1086924如果您可以将代码缩减为与我的“复制粘贴/可运行”类似的最小示例,并且仍然可以重现问题,则可以很容易地提供帮助。也许你会在那个时候看到自己的问题。只要继续删除东西,直到您只剩下线程问题。 – EL3PHANTEN

+0

@ user1086924 okey更新 – EL3PHANTEN