【问题标题】:Python tkinter GUI freezing/crashingPython tkinter GUI 冻结/崩溃
【发布时间】:2015-07-23 15:01:20
【问题描述】:
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                                                                           

在我的程序中,我首先按下开始按钮。 我将打印“仍在工作”,GUI 将更新芯片编号并一遍又一遍地闪烁完成指示灯。开始按钮转到将执行 10000 次的功能。然而,经过 3000 次迭代,gui 冻结,但程序仍然打印“仍在工作”。如何防止 gui 崩溃?

【问题讨论】:

  • 可能你应该把方法放在一个单独的线程中。见这里stackoverflow.com/questions/16745507/…
  • 尝试使用 Tkinter 的 after 而不是 sleep
  • FWIW,您应该使用time.time() 而不是timeit.default_timer()。这两个名称都指代同一个函数,但time.time() 对阅读您的代码的人来说更有意义。
  • 我不确定如何使用 after 方法
  • 我更新了我的代码并摆脱了睡眠。仍在尝试找出解决方案

标签: python tkinter


【解决方案1】:

您的代码有很多问题。一方面,这是有根本缺陷的:

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

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

sleep 的使用在 stackoverflow 上已经被问过很多次了。您可能会发现其中一些问题很有用。例如,

您还有另一个属于内存泄漏类别的问题。从该 while 循环中,您可以无限期地调用 self.start_button()。这种情况大约每秒发生一次,因为在循环中调用了半秒睡眠,而在 start_button 中又调用了半秒。

每次调用start_button 时,您都会创建另一个 标签小部件,该小部件堆叠在第9 行第2 列中的所有先前小部件之上。最终这将导致您的程序崩溃。我很惊讶它会导致您的程序如此迅速地失败,但这不是重点。

我的建议是从一个简单的示例重新开始,该示例仅每秒更新一个标签。让它发挥作用,以便您了解基本机制。然后,一旦它开始工作,您就可以添加从串行端口读取的代码。

【讨论】:

  • stackoverflow上是否有每秒更新一个标签的示例代码
  • @VemaReddy:我在回答中链接到的最后一个问题就是这样。
【解决方案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()

【讨论】:

  • 如何移植回 Python 2?
  • @VemaReddy 对于此代码,您可能只需将 import tkinter 替换为 import Tkinter as tkinter
猜你喜欢
  • 2015-04-08
  • 2018-05-03
  • 2015-11-11
  • 2018-10-11
  • 1970-01-01
  • 1970-01-01
  • 2015-03-05
  • 2013-11-02
  • 2021-05-03
相关资源
最近更新 更多