473,387 Members | 1,501 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Tkinter Frame won't destroy() correctly

9
Hi all,
For the life of me I cannot work out why this is occuring but whenever I try to .destroy() this widget (or its parent) my program hangs - it doesn't crash, it just stops responding so I have to force kill it.
Python 2.6.5, Windows 7

Expand|Select|Wrap|Line Numbers
  1.         def generate_list(self):
  2.             """
  3.             Clears off current list and runs through the entire contact list again,
  4.             makes a new 'user' object for each
  5.             """
  6.             for each in self.contacts_made:
  7.                 each.clear()
  8.                 #Can't get beyond here
  9.  
  10.             contacts_made = []
  11.             global user_count
  12.             user_count = 1
  13.  
  14.             for user in client_flags.contacts:
  15.                 process_user = self.create(user)
  16.                 process_user.produce()
  17.                 self.contacts_made.append(process_user)
  18.             self.update()
  19.  
  20.         def create(self, user_info):
  21.             return self.user(self.inner_frame, user_info)
  22.  
  23.         def update(self):
  24.             """
  25.             For ensuring the the scrollbar knows the correct/current dimensions of
  26.             the active user list - it will shrink and expand with more and less users
  27.             online
  28.             """
  29.             self.UListBox.configure(scrollregion=(0, 0, self.inner_frame.winfo_width(), self.inner_frame.winfo_height()))
  30.  
  31.         class user:
  32.             def __init__(self, parent, user_info):
  33.                 self.root = parent
  34.                 self.id = user_info[0]
  35.                 self.nick = user_info[1]
  36.                 self.status = user_info[2]
  37.  
  38.                 self.container = Frame(parent, bg='white', bd=1, relief=RAISED)
  39.                 global user_count
  40.                 self.container.grid(row=(user_count + 1), column=0, sticky=(E,W))
  41.                 user_count += 1 #need to go down too
  42.  
  43.             def clear(self):
  44.                 self.container.destroy() #<-Hangs here
  45.                 #Can't get to here
  46.  
  47.             def produce(self):
  48.                 global statusGreen, statusRed, verd11
  49.                 self.greenCircle = Label(self.container, image=statusGreen, bg="white")
  50.                 self.redCircle = Label(self.container, image=statusRed, bg="white")
  51.                 lNick = Label(self.container, text=self.nick, font=verd11, bg="white", anchor=W)#, font=fFont_Nick)
  52.                 bConnect = Button(self.container, text="C", font=verd11, command=self.user_connect)#having an image would be cooler than "C"
  53.  
  54.                 if self.status == "AVAILABLE":
  55.                     self.greenCircle.grid(row=0, column=0, pady=5)#, sticky=(N,S,W))
  56.                 else:
  57.                     self.redCircle.grid(row=0, column=0, pady=5)#, sticky=(N,S,W))
  58.  
  59.                 lNick.grid(row=0, column=1, sticky=(N,S,E,W), padx=(5,0), pady=5)
  60.                 bConnect.grid(row=0, column=2, sticky=(N,S,E), pady=3)
  61.  
  62.                 self.container.columnconfigure(0, minsize=18, weight=0)
  63.                 self.container.columnconfigure(1, minsize=150, weight=0)
  64.                 self.container.columnconfigure(2, minsize=18, weight=0)
  65.                 self.container.rowconfigure(0, minsize=34, weight=0)
  66.  
These 'user' classes are being grided under each other as they're created (tracked by user_count). Then I want to 'refresh' the list of users; which involves destroying the old objects and making new ones.

There is something going wrong on what must be a basic level of what I've created or my understanding of widget destruction.

Does anything jump out here?
Cheers
Jul 13 '10 #1

✓ answered by bvdet

Try calling widget.after() from your main thread to check the value of some variable that you set in the child thread. See my Clock example.

13 14949
bvdet
2,851 Expert Mod 2GB
What does method self.create() return? self.contacts_made should be a list of user instances.

destroy() is a universal widget method. It should work. Without having all the code for testing, it's sometimes hard to see a problem.
Jul 13 '10 #2
jinsue
9
Ah, my bad. Would make sense to include the create function... :)

I also added in the produce function in case that is making some odd settings.

Thanks
Jul 14 '10 #3
bvdet
2,851 Expert Mod 2GB
jinsue,

I don't see anything that sticks out, but I have no way to test the code.
You should capitalize the name of the class User. This is recommended for naming classes.

Try calling destroy() directly on the widget instead of in an instance method. This can be accomplished by inheriting from Tkinter.Frame. Example:
Expand|Select|Wrap|Line Numbers
  1. class User(Frame):
  2.     def __init__(self, parent, user_info):
  3.         Frame.__init__(self, parent)
Then you should be able to call destroy() directly on the instance as in:
Expand|Select|Wrap|Line Numbers
  1. for each in self.contacts_made:
  2.     each.destroy()
In my experience with Tkinter, things that you might think would work just don't work.
Jul 14 '10 #4
jinsue
9
I'm not big on how inheritance works (never needed it), but I understand what you mean.

In adding in that line to have:
Expand|Select|Wrap|Line Numbers
  1. def __init__(self, parent, user_info):
  2.                 Frame.__init__(parent)
  3.                 self.root = parent
  4.                 self.id = user_info[0]
  5.                 self.nick = user_info[1]
  6.                 self.status = user_info[2]
  7.  
  8.                 self.container = Frame(parent, bg='white', bd=1, relief=RAISED)
  9.                 global user_count
  10.                 self.container.grid(row=(user_count + 1), column=0, sticky=(E,W))
  11.                 user_count += 1 #need to go down too
  12.  
When the call goes through to:
Expand|Select|Wrap|Line Numbers
  1.                 process_user = self.create(user)
  2.                 process_user.produce()
  3.  
Nothing, visually occurs, so I'm not sure what its altering to cause incorrect grid'ing or some such.
On your train of thought though I tried changing self.clear() to self.container.destroy() but with no success.

In my experience with Tkinter, things that you might think would work just don't work.
I agree entirely with that >.>
Jul 14 '10 #5
bvdet
2,851 Expert Mod 2GB
When done this way:
Expand|Select|Wrap|Line Numbers
  1. class User(Frame):
  2.     def __init__(self, parent, user_info):
  3.         Frame.__init__(self, parent)
the instance itself is the Frame object. There is no need in creating another Frame object assigned to self.container.

Here's a simple example of creating a Frame in this way inside a Tk window:
Expand|Select|Wrap|Line Numbers
  1. import Tkinter
  2. from Tkconstants import *
  3. import time
  4.  
  5. textFont1 = ("Arial", 16, "bold")
  6.  
  7. class Clock(Tkinter.Frame):
  8.     def __init__(self, master):
  9.         Tkinter.Frame.__init__(self, master)
  10.         self.pack(fill=BOTH, expand=1)
  11.         menubar = Tkinter.Menu()
  12.         master.config(menu=menubar)
  13.         optionsMenu = Tkinter.Menu(tearoff=0)
  14.         menubar.add_cascade(label="Options", menu=optionsMenu)
  15.         optionsMenu.add_command(label='Print', command=self.print_value)
  16.         optionsMenu.add_command(label='Quit', command=master.destroy)
  17.  
  18.         self.clockVar = Tkinter.StringVar()
  19.         self.clockLabel = Tkinter.Label(self, textvariable=self.clockVar,
  20.                                         relief="raised", font=textFont1,
  21.                                         bd=3,
  22.                                         bg='#ffffff000',
  23.                                         fg="#000000fff",
  24.                                         activebackground = "#000000fff",
  25.                                         activeforeground = "#ffffff000",
  26.                                         takefocus=1,
  27.                                         padx=3,
  28.                                         pady=3)
  29.         self.clockLabel.pack(fill=BOTH, expand=1)
  30.         self.run()
  31.  
  32.     def run(self):
  33.         timeStr = time.strftime("%A, %b %d, %Y\n%I:%M:%S %p\n%Z")
  34.         # 24 hour clock
  35.         #timeStr = time.strftime("%A, %b %d, %Y\n%X\n%Z")
  36.         if timeStr != self.clockVar.get():
  37.             self.clockVar.set(timeStr)
  38.         self.after(200, self.run)
  39.  
  40.     def print_value(self):
  41.         print self.clockVar.get()
  42.  
  43. if __name__ == "__main__":
  44.     root = Tkinter.Tk()
  45.     app = Clock(root)
  46.     root.mainloop()
Change the command master.destroy to self.destroy and the Tk window will remain when Quit is selected but the clock frame will vanish. The Tk window is still active. There must be a way to make yours work.
Jul 14 '10 #6
jinsue
9
Ok, I modified the appropriate sections and everything came out like normal which is good, but upon calling each.destroy() the same problem occurs - the process hangs.

At this point I think more information would be good about the specifics of what is making it choke up.

Speculation on my part is now that Tkinter's grid manager might be unhappy with destroying a frame and then the other frames could be trying to all take its place or something odd. I know when you pack and grid something in Tkinter you often get the 'impossible balance' problem where it will stall forever trying to make both managers happy. It could be something odd like that.

However; how would I go about running a more detailed trace into the .destroy() method as that may render some clues as to why it becomes unhappy.

Thanks for your input thus far bvdet.
Jul 15 '10 #7
jinsue
9
Upon further testing I have encountered something interesting.
If in the first run of generate_list(), you build the User objects and then destroy all of anyone of them straight after, it works perfectly. The other frames push together nicely and execution continues happily. When you run it the second time (refreshing the list) and it goes through self.contacts_made to destroy anything in it, it hangs.

Expand|Select|Wrap|Line Numbers
  1.         def generate_list(self):
  2.             if self.contacts_made <> []: #Only destroy if it actually has things in it
  3.                 for each in self.contacts_made:
  4.                     each.destroy() #<-Fails here second time through
  5.  
  6.             contacts_made = []
  7.             global user_count
  8.             user_count = 1
  9.  
  10.             self.process_user1 = self.create([1, "Jane", "AVAILABLE"])
  11.             self.process_user1.produce()
  12.             self.process_user2 = self.create([2, "Bob", "AVAILABLE"])
  13.             self.process_user2.produce()
  14.             self.process_user3 = self.create([3, "Alex", "UNAVAILABLE"])
  15.             self.process_user3.produce()
  16.             self.process_user4 = self.create([4, "Tim", "AVAILABLE"])
  17.             self.process_user4.produce()
  18.  
  19.             self.contacts_made.append(self.process_user1)
  20.  
I think the solution is close now, but I am unsure as to the implications of this data. Please advise.
Jul 15 '10 #8
bvdet
2,851 Expert Mod 2GB
Unrelated to your problem, but this line:
Expand|Select|Wrap|Line Numbers
  1. if self.contacts_made <> []:
is unnecessary. When the for loop sees an empty list, it never does anything.

In addition, if you are going to compile a list of contacts, don't create all the extra variables self.process_user1, self.process_user2, and so on. Example:
Expand|Select|Wrap|Line Numbers
  1.             userList = [[1, "Jane", "AVAILABLE"],
  2.                         [2, "Bob", "AVAILABLE"],
  3.                         [3, "Alex", "UNAVAILABLE"],
  4.                         [4, "Tim", "AVAILABLE"]]
  5.             for user in userList:
  6.                 new = self.create(user)
  7.                 new.produce()
  8.                 self.contacts_made.append(new)
Try this suggestion. Create your frame object holding the contacts and destroy the frame only. You may need another frame to hold the contacts frame to save it's place if you have other widgets on the master. Using the Clock example again:
Expand|Select|Wrap|Line Numbers
  1. import Tkinter
  2. from Tkconstants import *
  3. import time
  4.  
  5. textFont1 = ("Arial", 16, "bold")
  6.  
  7. class Clock(Tkinter.Frame):
  8.     def __init__(self, master):
  9.         self.master = master
  10.         menubar = Tkinter.Menu()
  11.         master.config(menu=menubar)
  12.         optionsMenu = Tkinter.Menu(tearoff=0)
  13.         menubar.add_cascade(label="Options", menu=optionsMenu)
  14.         optionsMenu.add_command(label='Print', command=self.print_value)
  15.         optionsMenu.add_command(label='Stop', command=self.destroy)
  16.         optionsMenu.add_command(label='Restart', command=self.start)
  17.         optionsMenu.add_command(label='Quit', command=master.destroy)
  18.         self.start()
  19.  
  20.     def start(self):
  21.         Tkinter.Frame.__init__(self, self.master)
  22.         self.pack(fill=BOTH, expand=1)
  23.         self.clockVar = Tkinter.StringVar(self.master)
  24.         self.clockLabel = Tkinter.Label(self, textvariable=self.clockVar,
  25.                                         relief="raised", font=textFont1,
  26.                                         bd=3,
  27.                                         bg='#ffffff000',
  28.                                         fg="#000000fff",
  29.                                         activebackground = "#000000fff",
  30.                                         activeforeground = "#ffffff000",
  31.                                         takefocus=1,
  32.                                         padx=3,
  33.                                         pady=3)
  34.         self.clockLabel.pack(fill=BOTH, expand=1)
  35.         self.run()
  36.  
  37.     def run(self):
  38.         timeStr = time.strftime("%A, %b %d, %Y\n%I:%M:%S %p\n%Z")
  39.         # 24 hour clock
  40.         #timeStr = time.strftime("%A, %b %d, %Y\n%X\n%Z")
  41.         if timeStr != self.clockVar.get():
  42.             self.clockVar.set(timeStr)
  43.         self.after(200, self.run)
  44.  
  45.     def print_value(self):
  46.         print self.clockVar.get()
  47.  
  48. if __name__ == "__main__":
  49.     root = Tkinter.Tk()
  50.     app = Clock(root)
  51.     root.mainloop()
  52.  
Notice the frame is destroyed when "Stop" is selected. Any widgets in that frame will also be destroyed. When "Restart" is selected, the frame and its contents are recreated.
Jul 15 '10 #9
bvdet
2,851 Expert Mod 2GB
As an afterthought, if you can't get destroy() to work, you could always try grid_forget().
Jul 15 '10 #10
jinsue
9
Ahah! So close now. Ok I didn't mention in the OP but the secondary (and thus the call that hangs) call is run from a thread which from a quick google search shows that this is a huge no-no /facepalm. At least now I know.

So I'm pretty certain that what I have will work as intended, provided I call it from the mainloop()'s thread.
That makes the million dollar question: how do I get the main (GUI) thread to make the call? It seems a common enough occurrence that this can be done but obviously I'm not in the know.

Similar problem:
http://mail.python.org/pipermail/tki...er/002078.html

Third dot point from the bottom, up from 'Resources':
http://docs.huihoo.com/tkinter/Tkint...ary.html#Hints

The key point from the last link; "use 'after' from the main loop to poll a 'threading Queue' that your thread writes"

That makes sense but I do not understand where in the main loop I would put this command - if its a specific location I will probably be better off simply doing a condition check for this specific case and not complicating my project with the use of Queues (although they do look quite nice).

Please tell me you know where I should be putting this bvdet or that I'm being an idiot for realising it already :D.
Jul 15 '10 #11
bvdet
2,851 Expert Mod 2GB
Try calling widget.after() from your main thread to check the value of some variable that you set in the child thread. See my Clock example.
Jul 15 '10 #12
jinsue
9
It works perfectly now! Thanks so much bvdet for helping with this. I now understand the true significance of your clock example.
I could die happily right now that I have that solved...

To who may care, I ended up making a flag that gets checked like this:
Expand|Select|Wrap|Line Numbers
  1.         def --the setup section--
  2.             self.UListBox.after(5000, self.check_to_update_list)            
  3.  
  4.         def check_to_update_list(self):
  5.             """Allows process to be called based on variables from non-Mainloop threads safely"""
  6.             if client_flags.contacts_update == True:
  7.                 self.generate_list()
  8.                 client_flags.contacts_update = False
  9.  
  10.             self.UListBox.after(2000, self.check_to_update_list)
  11.  
So it makes the call to self.generate_list() based on the variable set in the child thread and Tkinter is all happy.
Jul 15 '10 #13
bvdet
2,851 Expert Mod 2GB
@jinsue
You have made my day. It is gratifying to solve problems such as this. I have learned from your problem also.
Jul 15 '10 #14

Sign in to post your reply or Sign up for a free account.

Similar topics

6
by: max(01)* | last post by:
hi people. when i create a widget, such as a toplevel window, and then i destroy it, how can i test that it has been destroyed? the problem is that even after it has been destroyed, the instance...
5
by: phil_nospam_schmidt | last post by:
I am trying to prevent a user from resizing a frame beyond its "natural" size as given by winfo_reqwidth and winfo_reqheight, without any success. Can anyone make any suggestions, based on my code...
3
by: Tuvas | last post by:
I'm tyring to set the size of the window that is opened when you open a Tkinter window, without much sucess. I have tried changing the heigth and width atributes, but it doesn't do anything. I...
6
by: Eric_Dexter | last post by:
Instead of creating my buttons and waiting for me to press them to execute they are executing when I create them and won't do my callback when I press them.. thanks for any help in advance ...
1
by: idsh | last post by:
I cant make this code work. I am new at Python, and in this program I have made a simple mastermind program. After creating the widgets, I try to insert text in the textbox in a function but all I...
2
by: Rajendran Appavu | last post by:
When I am done with a widget that is packed in a Frame, is it safe to call destroy() method on the widget after calling its pack_forget() or grid_forget() method? -- Thanks, Rajendran.
3
by: joshdw4 | last post by:
I hate to do this, but I've thoroughly exhausted google search. Yes, it's that pesky root window and I have tried withdraw to no avail. I'm assuming this is because of the methods I'm using. I...
3
by: J-Burns | last post by:
Hello. Im a bit new to using Tkinter and im not a real pro in programming itself... :P. Need some help here. Problem 1: How do I make something appear on 2 separate windows using Tkinter? By...
2
by: Mariostg | last post by:
I am trying to have a form talk to another one as per exemple below. Basically, a child form alter a widget on a parent form. I cannot find a way to make this to work. What am I doing wrong or...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.