Tkinter(ttk模块)和后台运行函数导致程序崩溃

2024-09-30 18:27:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我在使用Tkinter(ttk模块)并在后台运行函数并将函数的printsys.stdout)消息发送到GUI文本小部件self.constext时遇到了一个严重的问题。函数本身包含的类执行空间查询大约需要15分钟,中间有print语句来检查计算状态。在这篇文章被编辑之前,我确实尝试过threadingQueue实现ThreadingClient或QueueClient,但是启动GUI总是导致程序崩溃

这是我目前为止的代码

    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):,并接收打印或错误控制台消息,将它们写入小部件。在

有什么想法吗??在


Tags: theimportselfstyledefsyscolumngrid
2条回答

Tkinter不是线程安全的。如果您试图在文本小部件中插入数据,您将得到不可预知的行为(或者经常崩溃)。为了让一个单独的线程向小部件发送数据,您需要将数据写入线程安全队列,然后让主线程轮询该队列(使用tkinter的after方法)。在

也不能从线程调用tkMessagebox(崩溃)

see here

我有一个类似的问题,我是这样解决的:

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())

在这个例子中,我有一个progressbar,但是你应该不会有任何问题来适应你的代码,主要是通过改变periodiccall。在

相关问题 更多 >