Python tkinter GUI冻结/崩溃

2024-06-06 15:43:50 发布

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

from Tkinter import *
import tkFileDialog
import tkMessageBox
import os
import ttk

import serial
import timeit
import time

######################################################################################
class MyApp:
    def __init__(self, parent):
########################################################
#Setup Frames

        self.MiddleFrame = Frame(parent) #Middle Frame
        self.MiddleFrame.pack()
        #GLOBAL VARIABLES
        self.chip_number = 0 #number of chip testing
###########################################
        #Middle Frame setup  
        Label(self.MiddleFrame, text='Done').grid(row=8, column=1, sticky = E)
        self.Done = Canvas(self.MiddleFrame, bg="yellow", width=10, height=10)
        self.Done.grid(row=8, column=2)         
        Label(self.MiddleFrame, text='Chip Number:').grid(row=9, column=1, sticky = E)
        #start button
        self.button1 = Button(self.MiddleFrame,state=NORMAL, command= self.start_pre)
        self.button1["text"]= "START"
        self.button1.grid(row=1, column=2, sticky = E)
########################################### 
#Action of Start Button
    def start_pre(self):

        x = 0
        while x<10000:         
            self.start_button()
            x=x+1

#Talking to Board
    def start_button(self):
        #increase chip count number and update
        self.chip_number += 1
        Label(self.MiddleFrame, text=str(self.chip_number)).grid(row=9, column=2, sticky = E)
        #reset-yellow
        self.reset_color()          
        print "Still Working", self.chip_number
        self.Done.configure(background="green")
        self.Done.update_idletasks()                 

###############################################################
#Color Boxes
#Reset
    def reset_color(self):
        self.Done.configure(background="yellow")
        self.Done.update_idletasks() 
###############################################################################################################
#Start Programs
root = Tk() #makes window
root.title("Interface")
myapp = MyApp(root) #this really runs program
root.mainloop() #keep window open                                                                           

在我的程序中,我首先按下开始按钮。 我将打印“仍在工作”和图形用户界面将更新芯片号和闪烁完成灯一遍又一遍。“开始”按钮转到将执行10000次的函数。然而经过3000次迭代,gui冻结,但程序仍然打印“仍在工作”。如何防止gui崩溃?


Tags: textimportselfnumberdefcolumnrootframe
2条回答

我建议你从下面的代码开始?如果需要,您可以移植回Python 2,但是您的程序已经被重写为使用Python 3,并且已经被设计为使用tkinter的能力来使用after方法调度将来的事件。希望您会发现代码更容易遵循。

import collections
import timeit
import tkinter

def main():
    root = Application()
    root.setup()
    root.mainloop()

class Application(tkinter.Tk):

    def setup(self):
        mf = self.__middle_frame = tkinter.Frame(self)
        self.__middle_frame.grid()
        bf = self.__bot_frame = tkinter.Frame(self)
        self.__bot_frame.grid()

        self.__port_set = False
        self.__chip_number = 0
        self.__chip_pass_num = 0
        self.__chip_fail_num = 0
        self.__chip_yield_num = 0
        self.__stop = True

        self.__widgets = collections.OrderedDict((
            ('COT', 'Continuity Test'), ('CHE', 'Chip Erase'),
            ('ERT', 'Erase Test'), ('WRT', 'Write Test'),
            ('WIRT', 'Wire Reading Test'), ('WIT', 'Wire Reading Test'),
            ('WRAT', 'Write All Test'), ('DO', 'Done')))

        for row, (key, value) in enumerate(self.__widgets.items()):
            label = tkinter.Label(mf, text=value+':')
            label.grid(row=row, column=0, sticky=tkinter.E)
            canvas = tkinter.Canvas(mf, bg='yellow', width=10, height=10)
            canvas.grid(row=row, column=1)
            self.__widgets[key] = label, canvas

        self.__cn = tkinter.Label(mf, text='Chip Number:')
        self.__cn.grid(row=8, column=0, sticky=tkinter.E)
        self.__display = tkinter.Label(mf)
        self.__display.grid(row=8, column=1, sticky=tkinter.E)

        self.__button = tkinter.Button(bf, text='START',
                                       command=self.__start_pre)
        self.__button.grid(sticky=tkinter.E)

    def __start_pre(self):
        self.__button['state'] = tkinter.DISABLED
        self.__start_button(0)

    def __start_button(self, count):
        if count < 100:
            self.__chip_number += 1
            self.__display['text'] = str(self.__chip_number)
            self.__widgets['DO'][1]['bg'] = 'yellow'
            start_time = timeit.default_timer()
            print('Still Working:', self.__chip_number)
            self.after(500, self.__end_button, count)
        else:
            self.__button['state'] = tkinter.NORMAL

    def __end_button(self, count):
        self.__widgets['DO'][1]['bg'] = 'green'
        self.after(500, self.__start_button, count + 1)

if __name__ == '__main__':
    main()

你的代码有很多问题。首先,这是有根本缺陷的:

while self.stop == True:         
    self.start_button()
    time.sleep(0.5)

你不能指望一个GUI在处理这样的代码时能正常工作。作为一般经验法则,您永远不应该拥有GUI调用的主线程sleep。导致sleep阻止事件循环处理任何事件,包括刷新屏幕的请求等低级事件。

stackoverflow上多次询问并回答了sleep的用法。你可能会发现其中一些问题很有用。例如

还有一个问题属于内存泄漏的范畴。从while循环中,可以无限期地调用self.start_button()。这种情况大约每秒发生一次,因为在循环中调用sleep的时间是半秒,而在start_button中调用sleep的时间是半秒。

每次调用start_button时,都会创建另一个标签小部件,该小部件堆叠在第9行第2列的所有先前小部件之上。最终这会导致你的程序崩溃。我很惊讶它会导致你的程序很快失败,但这与重点无关。

我的建议是从一个简单的例子开始,它只会每秒更新一个标签。让它工作,这样你就能理解基本的机制。然后,一旦它开始工作,你就可以添加从串行端口读取的代码。

相关问题 更多 >