2017-04-16 307 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