【问题标题】:Exception With Tkinter Callback because loop continues in the backgroundTkinter 回调异常,因为循环在后台继续
【发布时间】:2015-05-23 14:12:02
【问题描述】:

各位论坛的朋友,

我目前是 python 和一般编程的初学者,所以请不要对我大喊大叫 :) 我知道我有很多全局变量并且犯了很多错误。

我正面临一个可怕的非结束循环问题,它并没有真正影响我的 tkinter 窗口本身,但是当关闭时,在 tkinter 回调问题中看到这个异常是很遗憾的。

其实我不知道为什么我在 Valider() 函数中的 batman 变量没有结束所有这些循环 有人可以帮我解决这个问题吗?

Ps:对不起,代码中的法语 谢谢

这里是源代码:

    from tkinter import*
from random import* 
import time 
from tkinter import messagebox
from tkinter import ttk

#Initialisation des variables
bonne_reponse=0
mauvaise_reponse=0
nbr_total=0
batman=False
nbr_q=10 # 10 question 

t=10 # t est definit le temps que l'utilisateur a pour repondre a la question



#_______________Fonction qui genere 2 nombres au hasard dans des intervalles differents selon la difficultee choisie__________________________#



def Division ():
    nbr1 = randint (1,9) 
    nbr2 = randint (1,9)
    div=nbr1*nbr2
    calcul_affiche  = ("CALCUL : " + str(div) + "/" + str(nbr2) + " =")
    label1.configure(text=calcul_affiche)
    calcul=nbr1
    return calcul





##### Fonction qui demarre le programme et donc qui lance la barre de progression lors du clic sur "Demarrer". 

def Démarrer():
    global reponse_final
    global batman
    batman=False

    reponse_final=Division()
    start_time=time.time()

    # defini un temps de demarrage car le module time compte le temps depuis 1974
    while batman==False:
        now =t-((time.time())-(start_time))
        root.update()
        #Redefini la valeur de la barre de progression
        wq=(now/t)*100
        progress["value"] = wq
        # Fausse le resultat apres le temps ecoule (avec un calcul improbable) 
        if wq<=0:
            batman=True
    if batman==True:
        Valider()



##### Fonction de validation

def Valider(): 
    global bonne_reponse
    global mauvaise_reponse
    global nbr_total
    global batman 
    utilisateur_reponse=entryWidget.get() #recupere la valeur de la boite d'entree
    entryWidget.delete(0, END) #supprime ce qu'il y a dans la barre d'entree
    # empeche l'utlisateur d'entrer des lettres dans la boite d'entrée 
    batman=True 
    try:  
        if reponse_final != int(utilisateur_reponse):
            titre="Réponse"
            bon_rep=str("Mauvaise réponse ! La bonne réponse était: "+str(reponse_final))
            messagebox.showinfo(titre, bon_rep)
            mauvaise_reponse+=1
            nbr_total+=1

        elif reponse_final== int(utilisateur_reponse):
            #messagebox.showinfo("Réponse", "Bonne réponse!")
            bonne_reponse+=1
            nbr_total+=1
    except:
        messagebox.showerror("Boite d'entrée", "Temps ecoulé ou Entrez uniquement des nombres")
        mauvaise_reponse+=1
        nbr_total+=1
    #on arrete le jeux lorsque le nombre de question souhaité est atteint   
    if nbr_total==nbr_q:
        exitnote()   
    Démarrer()




######################Création fenetre principale Tkinter "root" ###################

root = Tk()
root.title("Calcul Mental")
root.configure(bg="gainsboro")
root["padx"] = 60
root["pady"] = 40

# On definit un style et un theme pour les widgets
s = ttk.Style()
s.theme_use('clam')


# Creation du label correspondant a la consigne en debut de page
consigne= ("Cliquez sur démarrer pour commencer. Un nombre illimité de calculs va vous être proposé.")
instructions = ttk.Label(root, text=consigne)
instructions.pack()

label_trait4= ttk.Label(root, text="--------------------------------------------------------------------------------------------------")
label_trait4.pack()

# Création de la barre de progression
s.configure("blue.Horizontal.TProgressbar", foreground='aquamarine3', background='aquamarine3')
progress = ttk.Progressbar(root, style="blue.Horizontal.TProgressbar", orient="horizontal", length=500, mode="determinate")
progress["maximum"] = 100
progress.pack()


### Création d'une boite de texte pour un label texte et la boite d'entree 
text_boite= Frame(root)

# Création du label de calcul 
label1 = ttk.Label(text_boite, background="white", width=15)
label1["text"] = ""
label1.pack(side=LEFT)


# Création d'un Entry Widget dans text_boite
entryWidget = ttk.Entry(text_boite)
entryWidget['width'] = 50
entryWidget.pack(side=RIGHT)

text_boite.pack()


# Bouton valider
root.bind("<Return>", lambda event: Valider())

btn_valider = ttk.Button(root, text="Valider", command=Valider)
# fait en sorte que quand l'utilisateur appui sur la touche entrer ca lance la fonction valider. 


# On rend inutilisable le bouton demarrer apres le 1er clic
def btndemarrer():
    btn_démarrer.config(state=DISABLED)
    instructions.destroy()
    Démarrer()

btn_démarrer = ttk.Button(root, text="Démarrer", command = btndemarrer)
btn_démarrer.pack()

# Affiche la note dans une boite et detruit la fenetre
def exitnote():
    global batman
    batman=True
    try:
        note=(bonne_reponse*20/nbr_total)
        w=("Votre note est de "+str(note)+"/20.0 ")
        messagebox.showinfo("Voici votre note",str(w))
        root.destroy()

    # Si note n'est pas definit car l'utilisateur n'a pas encore appuye sur Entrer, on affiche le message suivant:
    except:  
        messagebox.showinfo("DEVNOTE","Je respecte votre choix mais vous n'avez meme pas essayé.")
        root.destroy()

btn_valider.pack()
btn_arret = ttk.Button(root, text="Arrêt", command=exitnote)
btn_arret.pack()

# On lance la boucle Tkinter qui s'interompt lors de la fermeture de la fenetre 
root.mainloop()

这是一个例外:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python34\lib\tkinter\__init__.py", line 1487, in __call__
    return self.func(*args)
  File "C:\Users\Thierry\Google Drive\Calcul_Mentalsimple.py", line 145, in <lambda>
    root.bind("<Return>", lambda event: Valider())
  File "C:\Users\Thierry\Google Drive\Calcul_Mentalsimple.py", line 94, in Valider
    Démarrer()
  File "C:\Users\Thierry\Google Drive\Calcul_Mentalsimple.py", line 55, in Démarrer
    progress["value"] = wq
  File "C:\Python34\lib\tkinter\__init__.py", line 1275, in __setitem__
    self.configure({key: value})
  File "C:\Python34\lib\tkinter\__init__.py", line 1268, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Python34\lib\tkinter\__init__.py", line 1259, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".59022192"

【问题讨论】:

  • 考虑将代码添加到问题本身,而不是添加链接!
  • 当我点击代码链接时,我看到一个页面显示“没有可用的预览”,并询问我是要下载它还是选择一个应用程序来查看它。很多人(包括我自己)会认为您可能隐藏了某种恶意代码,试图诱使我们运行。我怀疑这是你的意图,但这就是它看起来的样子。
  • 该脚本中有很多代码,这可能是 OP 决定不将其包含在问题中的原因...
  • 好的,我会尝试直接在这里发布,是的,有很多代码,因为我还没有将它拆分成不同的文件
  • 我已经通过删除很多函数来简化代码,所以这里是:) 那么有人可以帮助我吗?

标签: python tkinter python-3.4


【解决方案1】:

好的,这里有两件事:

1) 根据上面的代码,您的 Valider() 函数不需要任何参数,因此您可以简单地绑定而不使用 lambda

root.bind('<Return>', Valider)

2) 当按钮/返回被触发并且您的 Valider() 被调用时,据我所知,对触发事件的引用被传递(至少在 wxPython 中它是这样做的,而简短的谷歌搜索给了我同样的 tkinter )。

这意味着您的触发函数(在本例中为 Valider())也必须处理该引用。据此,您的 Valider() 定义应如下所示:

def Valider(event=None):

以这种方式定义您的 Valider() 可以处理对触发该函数的事件的传入引用,但您可以使用直接调用该函数

Valider()

因为事件参数有一个默认值设置为

希望对您有所帮助。 问候:-)

【讨论】:

  • 是的,它确实有效,谢谢! :) 但没有解决主要问题 :( 实际上 progress["value"] = wq 错误发生在我按下 Valider 的次数,而 wq 不等于 0 意味着循环仍在后台运行,我不明白为什么 Valider 中的蝙蝠侠不杀它?
  • 你还有和以前一样的异常吗?
  • 另外根据上面的代码,您的 Demarrer() 函数中缺少 global progress
  • 是的,同样的异常不在 lambda Valider() 中,当然现在只在 Valider() 中。但是 progress["value"] 是你如何设置进度条的值我看不出它怎么可能是全局的?但是我仍然尝试过,仍然是同样的错误,这里仍然有一些无限循环问题:(
  • 如果循环正在进行后台工作,您应该将其移至单独的线程(请参阅我今天早些时候回答的您的另一个问题)现在我不完全理解您的代码/UI 的作用,也许它如果您使用当前拥有的代码更新问题并将描述性 cmets 翻译成英文,这样我就有机会了解您正在尝试做什么,这可能会有所帮助。问候
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
  • 2018-03-20
  • 2015-08-19
  • 2021-03-25
  • 1970-01-01
  • 2021-12-27
  • 2011-02-07
相关资源
最近更新 更多