2013-07-26 23 views
2

我有一個嚴重的問題,使用Tkinter(ttk模塊)並在後臺運行一個函數,並將該函數的printsys.stdout)消息接收到GUI文本窗口小部件self.constext。該函數本身包含正在執行空間查詢的類,時間約爲15分鐘,其間有打印語句來檢查計算狀態。直到這個職位是編輯我也嘗試線程隊列實施ThreadingClient或QueueClient但總是在啓動GUI導致程序崩潰Tkinter(ttk模塊)和後臺運行導致程序崩潰的功能

所以,這裏是我的代碼到目前爲止

import tkFileDialog 
    import tkMessageBox 
    import ttk 
    import Tkinter 
    import Queue 
    import os 
    import time 
    import sys 
    import threading 
    import multiprocessing 
    import gemeindesteckbrief__SupportTools__ 




    class SystemInfoSupport(): 

    #def __init__(self, master,factshHW, factshHwGeb,factshHwSch, factshGem, 
    factshGeol, factshWLV) : 
     def __init__(self,master): 
    #Actual Window 
    self.sysInf =ttk.Frame (master) 
    self.sysInf.grid() 
    self.sysInf.grab_set() 
    self.incrVal = 0 
    #self.__calcFacthw = factshHW 
    #self.__calcFacthwGeb = factshHwGeb 
    #self.__calcFactHwSch = factshHwSch 
    #self.__calcFactGeol = factshGeol 
    #self.__calcFactWLV = factshWLV 
    #self.__calcFactGem = factshGem 

    #print self.__calcFacthwGeb 



    self.style= ttk.Style() 
    self.style.configure("Head.TLabel",foreground="#20B2AA", background="#E6E6FA", font = "Verdana 12 bold") 
    self.headLabel= ttk.Label (self.sysInf,text = "Systeminformation- Kalkulation",style = "Head.TLabel") 
    self.headLabel.grid(row=0, column =0, sticky ="NW",pady = 15, padx =20) 


    #Process OVerview 
    self.mainFrame = ttk.LabelFrame (self.sysInf,width=200,height=100) 
    self.mainFrame.grid(row=2, column =0, sticky = "NW", padx = 15, pady = 5) 
    self.style.configure("Prog.TLabel", font = "Verdana 10 italic underline") 
    self.progLabel= ttk.Label (self.mainFrame,text = "Räumliche Analysen-Fortschritt:", style ="Prog.TLabel") 
    self.progLabel.grid (row =2,column =1, sticky = "NW", padx = 10, pady = 2) 
    self.progBar= ttk.Progressbar(self.mainFrame,mode='determinate',length = 370, name='progBar1') 
    self.progBar.grid(row=3, column=0, columnspan=4,sticky ="NW", pady=5, padx=10) 
    self.style.configure("Scale.TLabel", font = "Verdana 8 bold") 
    self.scaleBounds = ttk.Label (self.mainFrame,text = "0 %\t\t\t\t\t 100 %") 
    self.scaleBounds.grid (row =4,column =1, sticky = "NW", padx = 5, pady = 1) 

    self.textFrame = ttk.LabelFrame (self.mainFrame,width=200,height=100) 
    self.textFrame.grid(row=5, column =1, sticky = "NW", padx = 5, pady = 10) 
    self.style.configure("Consol.TLabel", font ="Verdana 8 bold") 
    self.consLable = ttk.Label (self.textFrame,text = "Log-Console:",style ="Consol.TLabel") 
    self.consLable.grid (row =6,column =1, sticky = "NW", padx = 5, pady = 1) 
    self.consText= ttk.Tkinter.Text(self.textFrame, wrap = "word") 
    self.consText.grid(row =7,column =1, rowspan =4) 
    self.consText.tag_configure("stderr", foreground="#b22222") 
    self.scrollText= ttk.Scrollbar(self.textFrame,command = self.consText.yview) 
    self.scrollText.grid(row =7,column =2,rowspan =4,sticky='NSEW') 
    self.consText.config(yscrollcommand = self.scrollText.set) 

    self.cancelButton = ttk.Button (self.mainFrame, text ="Abbrechen",command = self.testProgBar) 
    self.cancelButton.grid (row =12,column =1) 


    sys.stdout = TextRedirector(self.consText, "stdout") 
    sys.stderr= TextRedirector(self.consText, "stderr") 


    # Create new threads 
    # run function in background using a ThreadingClient 
    # self.thread1 = gemeindesteckbrief_SpatialThread.SpatialThread(self.__calcFacthw, self.__calcFacthwGeb) 

    #run function in a threading.Thread 
    #self.thread1 = threading.Thread(name ="MyThread", target = self.prozessCalculateFactsheets) 
    #run function in a threading.Timer 
    #self.thread1= threading.Timer(2,self.calculateFactsheets) 
    #Start the thread 
    #self.thread1.start() 

    #self.check_thread() 

#Check if thread is still executing or not 
def check_thread(self): 
# Still alive? Check again in half a second 
    if self.thread1.isAlive(): 
     self.sysInf.after(500,self.check_thread) 


# function to test the sys.stdout behaviour and writting to the Tkinter.text widget 
def testProgBar (self): 
    print "hello my friend" 
    sys.stderr.write("hello my error friend\n") 
    self.sysInf.grab_release() 


# the actual function needed to be executed in background 
def calculateFactsheets (self): 
    # Read the directory to the input data of the init_File and add to a new Factsheet spatial calculation 
    try: 
     print "''''Hello from the Calculation Function()''''" 
     requireData = gemeindesteckbrief__SupportTools__.ToolSet() 

     if self.__calcFacthw == 1: 
      factsheetHochw = gemeindesteckbrief_SpatialCalculFactshHochw.SpatialAnalysis_FactsheetHochwasser(requireData.readData(13),requireData.readData(10), 
          requireData.readData(20), requireData.readData(21), requireData.readData(22), requireData.readData(23), requireData.readData(24), 
          requireData.readData(25), requireData.readData(26)) 

      factsheetHochw.verkExpertAnalyseGZPBWV() 
      factsheetHochw.verkExpertAnalyseHSG() 
      factsheetHochw.verkExpertAnalyseTotal() 
      factsheetHochw.verkExpertAnalyseGZPOI() 
      factsheetHochw.verkExpertAnalyseGZLOI() 
      factsheetHochw.verkExpertAnalyseHSGPOI() 
      factsheetHochw.verkExpertAnalysePLOITot() 
      factsheetHochw.verkExpertAnalyseLandWald() 
      factsheetHochw.verkExpertAnalyseLandWaldTotal() 

     if self.__calcFacthwGeb == 1: 
      print "FACTSHEET HOCHWASSER Gebaeude startet" 
      factsheetHochwGeb = gemeindesteckbrief_SpatialCalculFactshHochwGebaeude.SpatialAnalysis_FactsheetHochwGebaeude(requireData.readData(13),requireData.readData(10), 
         requireData.readData(16),requireData.readData(15), requireData.readData(17),requireData.readData(18)) 
      print "Data correct initialized" 
      factsheetHochwGeb.gebaeudeExpAnalyseGZPBWV() 
      factsheetHochwGeb.gebaeudeExpAnalyseHSG() 
      factsheetHochwGeb.gebaeudeExpAnalyseTotal() 

     tkMessageBox.showinfo("Räumlicher-Analyse Erfolgreich","Die Berechnungen wurden erfolgreich abgeschlossen!") 

    except: 
     tkMessageBox.showerror ("FactsheetHochwasser_Gebaeude FEHLER","Bei der Berechnung ist ein Fehler aufgetreten!\n Für Details öffnen Sie das Error-File in der Programmumgebung") 


class TextRedirector(object): 
def __init__(self,widget, tag): 
    self.targetwidget = widget 
    self.targettag = tag 

#@Override the sys.stdout & sys.stderr methods to write to the text widget instead of the python console 
def write(self, str): 
    self.targetwidget.configure(state="normal") 
    self.targetwidget.insert("end", str, (self.targettag,)) 
    self.targetwidget.configure(state="disabled") 


    root = ttk.Tkinter.Tk() 
    root.title ("SystemINFO-Menü") 
    runGUI = SystemInfoSupport (root) 
    root.mainloop() 

基本上我需要在後臺執行功能def calculateFactsheets (self):並接收打印或錯誤控制檯消息以將它們寫入小部件。

任何想法??

+0

您的縮進被搞砸了,這使得很難閱讀代碼。 –

+0

感謝這個網頁的源代碼聲明......是的,但我希望它不會壞! –

+1

如果您希望人們花時間回答您的問題,那麼您應該花時間修復縮進。 –

回答

2

Tkinter不是線程安全的。如果您嘗試將數據插入到文本小部件中,您將獲得不可預知的行爲(或經常崩潰)。爲了讓一個單獨的線程發送數據到一個小部件,您需要將數據寫入一個線程安全隊列,然後讓主線程輪詢該隊列(使用tkinter的after方法)。

也tkMessagebox不能從一個線程調用(崩潰)

see here

+0

我知道Tkinter不是線程安全的!我剛剛在Tkinter和Threads上做了很多工作,但任何解決方案都解決了我的實際問題(隊列處理程序,線程處理程序等.....) –

0

我有解決這樣類似的問題:

class UpdateWindow(Toplevel): 
    def __init__(self): 
     Toplevel.__init__(self) 
     self.create_widgets() 
     self.grid() 
     self.focus_force() 

    def create_widgets(self): 
     self.queue = Queue.Queue() 
     #... 
     self.button= Button(self, text='Auto Update', command=lambda: self.spawnthread(function)) 
     self.button.grid(row=1, column=0, sticky=N) 
     #... 

    def spawnthread(self, fcn): 
     self.button.config(state="disabled") 
     self.thread = ThreadedClient(self.queue, fcn) 
     self.thread.start() 
     self.periodiccall() 

    def periodiccall(self): 
     if self.thread.is_alive(): 
      self.after(100, self.periodiccall) 
      self.progressbar.step(500) 
     else: 
      self.button.config(state="active") 
      self.progressbar.stop() 


class ThreadedClient(threading.Thread): 
    def __init__(self, queue, fcn): 
     threading.Thread.__init__(self) 
     self.queue = queue 
     self.fcn = fcn 

    def run(self): 
     time.sleep(1) 
     self.queue.put(self.fcn()) 

在這個例子中,我有一個進度條,但你應該沒有任何問題適應你的代碼,主要是通過改變periodiccall

+0

in lamda:在代碼中引用了'self.spawnthread'有功能的簽名......功能究竟是幹什麼的?是否可以在此位置添加我的函數def.calculateFactsheets()? –

+1

是的。在我的原始代碼中,我有一個實際的功能,我添加了'function'作爲佔位符。這是你將放置在另一個線程中的函數的地方。 –

相關問題