Sahadev
Sahadev

Reputation: 1448

Tkinter pack_forget() and pack() issue on key-release event

My Requirement I am developing a program in which I handle space and KeyPress-space Events to handle time based event.

What I am doing is that on the start of program a window gets open to display a time interval, say "5" which depicts 5 Second. Once the time lapses(5Sec) the text "5" gets disappear.

After that the user is required to press space key, when user press space key the same number will display until user don't release space key.

I have given 2 second gap to show next interval(once user releases the space) and in that interval the number should not appear which was appearing in previous set time interval space Event.

Problem Statement: The issue that I am facing is that I have managed to display number(text) on key press but when I key-release, time interval is not disappearing for 2 second.

Here is my Code:

from tkinter import Label, Tk
import time

times = [3, 4, 5]
loop = 0

class Demo:
    def __init__(self, master):
        global times, loop
        self.parent = master
        self.lbl = Label(master, text=times[loop])
        master.after(times[loop]*1000, self.hideLabelAfterSomeTime)
        master.bind("<space>", self.hideLabel)
        master.bind("<KeyRelease-space>", self.showLabel)
        print('called', loop)
        self.lbl.pack()

    def hideLabel(self, event):
        global loop, times
        self.lbl.config(text=times[loop])
        self.lbl.pack()

    def showLabel(self, event):
        global loop
        self.lbl.pack_forget()
        time.sleep(2)
        loop += 1
        Demo(self.parent)

    def hideLabelAfterSomeTime(self):
        self.lbl.config(text= '')

master = Tk()
app = Demo(master)
master.geometry("400x300+400+200")
master.mainloop()

Upvotes: 0

Views: 467

Answers (2)

Brij
Brij

Reputation: 109

Here you go..Please let me know if you have any further query -

from tkinter import Label, Tk
import time

times = [3, 4, 5]
loop = 0

class Demo:
    def __init__(self, master):
        global times, loop
        self.parent = master
        self.lbl = Label(master, text=times[loop])
        master.after(times[loop]*1000, self.hideLabelAfterSomeTime)
        master.bind("<space>", self.hideLabel)

    master.bind("<KeyRelease-space>", self.showLabel)
    print('called', loop)
    self.lbl.pack()

def hideLabel(self, event):
    global loop, times
    self.lbl.config(text=times[loop])
    self.lbl.pack()

def showLabel(self, event):

    global loop
    self.lbl.pack_forget() 
    self.parent.update()
    time.sleep(2)
    loop += 1
    Demo(self.parent)

def hideLabelAfterSomeTime(self):
    self.lbl.config(text= '')

master = Tk()
app = Demo(master)
master.geometry("400x300+400+200")
master.mainloop()

Upvotes: 1

h fsii
h fsii

Reputation: 41

You appear to have found a "crevice" in the way Python interacts with the Tk main loop. At least on my system (Python 2.7, Fedora 22, x86_64) none of the operations in showLabel() have any effect on the screen until the function (including time.sleep(2)) completes.

If you modify the code as follows for troubleshooting, I think you can see what I mean.

def hideLabel(self, event):
    global loop, times
#       self.lbl.config(text=times[loop])
    self.lbl.config(bg = "red")
    self.lbl.pack()

def showLabel(self, event):
    global loop
#       self.lbl.pack_forget()
    self.lbl.config(fg = "blue")
    time.sleep(2)
    loop += 1
    Demo(self.parent)

def hideLabelAfterSomeTime(self):
#       self.lbl.config(text= '')
    self.lbl.config(bg = "green")

The second label now shows up below the first. The first label gets a blue foreground, but not on the keypress, as we would expect. It apparently only gets it when the function returns, after time.sleep().

I don't pretend to completely understand this - someone who has a deep understanding of Python and Tk might explain. One workaround for this particular program is to add update_idletasks(). This (warning: sloppy terminology ahead) forces the Tk main loop to immediately process pending events.

def showLabel(self, event):
    global loop
#   self.lbl.pack_forget()
    self.lbl.config(fg = "blue")
    master.update_idletasks()
    time.sleep(2)
    loop += 1
    Demo(self.parent)

On my system, that made the first label turn blue before time.sleep(). I didn't experiment beyond that. p.s showLabel() hides the label and hideLabel() shows it?

EDIT: The following slight mod of the original posted code works on my system almost as I would expect. I'm not 100% sure what it is intended to do. But the current label does disappear when I tap the space bar, and the next one shows up 2 seconds later, only to be blanked a variable time after that.

The only puzzle I ran into was that my system basically went crazy when I tried to hold down the space bar for more than 2 seconds. It turned out that the key auto-repeat was kicking in on my system, giving me dozens of keystrokes and causing dozens of subscript out-of-range errors. I gave up at that point so I still don't know how the program behaves if given a single space-down followed 3 seconds later by a single space-up.

from Tkinter import Label, Tk
import time

times = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
loop = 0

class Demo:
    def __init__(self, master):
        global times, loop
        self.parent = master
        self.lbl = Label(master, text=times[loop])
        master.after(times[loop]*1000, self.hideLabelAfterSomeTime)
        master.bind("<space>", self.hideLabel)
        master.bind("<KeyRelease-space>", self.showLabel)
        master.bind("<h>", self.showLabel)
        print('called', loop)
        self.lbl.pack()
        for child in master.children: print child

    def hideLabel(self, event):
        global loop, times
        self.lbl.config(text=times[loop])
        self.lbl.pack()

    def showLabel(self, event):
        global loop
        self.lbl.pack_forget()
        master.update_idletasks()
        time.sleep(2)
        loop += 1
        Demo(self.parent)

    def hideLabelAfterSomeTime(self):
        self.lbl.config(text= '')
        self.lbl.config(bg = "green")

master = Tk()
app = Demo(master)
master.geometry("400x300+400+200")
master.mainloop()

If the pack_forget has no effect on your system even with the update_idletasks(), I'd have to guess that the Tk main loop is more platform dependent than is widely known.

Upvotes: 0

Related Questions