I am creating a program that used entry widgets like so:
nameField = Entry(root, width = 20)
nameField.place(x=100, y=190)...
I have created multiple entry widgets, is there a way I can destroy all of the entry widgets that I have created?
You can store references to each widget in a list and then iterate over the list calling the destroy() method on each. You could also put all of the entry widgets in a single frame. When you delete the frame, any widgets inside the frame will also be deleted.
Related
My code destroys some widgets then builds new widgets then in the end of the application a button asks if I want to get to the beginning. This button will call class constructor which re-initialize every variable and start re-drawing the same old widgets. The problem is that the widgets even if they are new keep their latest values before destruction.
def mapping(self):
sort_frame = Frame(self.top_frame)
sort_frame.grid(row=0,column=1)
sort = False
Checkbutton(sort_frame, text="Sort: ", variable=sort, onvalue=True, offvalue=False,command=lambda fr=sort_option_frame, nx = next_button : self.enable_sort(fr,nx)).pack(side=TOP)
next_button = Button(self.bottom_frame, text='Next',borderwidth=1, command=self.output_select)
next_button.pack( side = RIGHT)
def output_select(self):
for widget in self.top_frame.winfo_children():
widget.destroy()
for widget in self.bottom_frame.winfo_children():
widget.destroy()
#new widgets drawing
Button(self.bottom_frame, text='New file',borderwidth=1, command=self.restart).pack( side = TOP)
#This UI resets the application for a new cycle
def restart(self):
for widget in self.top_frame.winfo_children():
widget.destroy()
for widget in self.bottom_frame.winfo_children():
widget.destroy()
self.__init__(self.root)
In this code for example, the Checkbutton on mapping will keep its latest values when mapping is called back in the new cycle.
I want the checkbutton to be new as if it's the first time it was created.
Thanks for your help
You can't use normal variables for the variable attribute. They need to be an instance of StringVar, IntVar, DoubleVar, or BooleanVar.
I'm currently learning how to use the Tkinter library on python to create a GUI that takes in longitude and latitude points and outputing that into a file. Basically I'm trying to automate the process of having to copy the correct format of line of points to another file.
So I created a Entry and button field to see how many long/lat points are needed to generate a 'shape'. Using this integer input from user, I have a for loop to populate the GUI with multiple widgets asking for the long/lat points. I have that working properly, but now I am trying to have a clear button, which would allow the user to clear all these long/lat points and give them the ability to repopulate the field with the amount of points the other shape requires.
So far I have:
def clearGrid():
coordAmount = int(pointText.get())
latLabel.grid_forget()
longLabel.grid_forget()
.....(contains code that populates the GUI)
#creating clear site Button
clearButton = Button(main_gui, text="Clear Sites!",command=clearGrid)
clearButton.grid(row=lastRow+1, column=5, pady=10)
However, the problem that I am running into is that when the clear button is clicked, it only clears the latest instance of the widgets not all of them. So in a for loop that creates 5 instances/iteration of widgets, it will remove only the 5th instance/iteration of widgets.
I'm trying to have the clear button functionality be able to delete all 5 instances of these widgets.
So here is a shortened code of how I am populating the GUI with widgets
def generatePoints():
for x in range(0,3):
degLong_label = Label(main_gui, text="Degree:", height=2)
degLong_label.grid(row=y,column=6,sticky=E)
degLong = Entry(main_gui, width=4)
degLong.grid(row=y,column=7,sticky=W)
#minute
minLong_Label = Label(main_gui,text="Minutes:", height=2)
minLong_Label.grid(row=y,column=8,sticky=W)
minLong = Entry(main_gui,width=3)
minLong.grid(row=y,column=8,sticky=E)
#seconds
secLong_Label= Label(main_gui,text="Sec:",height=2)
secLong_Label.grid(row=y,column=9,sticky=W,padx=20)
secLong = Entry(main_gui,width=3)
secLong.grid(row=y,column=9,sticky=E,padx=20)
#direction
dirLong_Label = Label(main_gui,text="Direction:",padx=5,height=2)
dirLong_Label.grid(row=y,column=12,sticky=W)
dirLong = Entry(main_gui,width=3)
dirLong.grid(row=y,column=13)
You need to hold on to references to all those widgets, usually via a list. Try initializing a list (list_of_widgets) before your loop, then every time you create a widget, append it to that list. When you clear, you can iterate through that list of widgets and destroy each one. Once you're done clearing them, you can clear the list so you don't try to destroy a widget twice (Tkinter will error at that point).
def generatePoints():
list_of_widgets = [] # or take the list as a parameter
for x in range(3):
degLong_label = Label(...)
degLong_label.grid(...)
list_of_widgets.append(degLong_label)
degLong = Entry(...)
degLong.grid(...)
list_of_widgets.append(degLong)
# et al.
def clearGrid(list_of_widgets):
for widget in list_of_widgets:
widget.destroy()
Note that you probably want to actually destroy the widgets if you aren't planning on showing that specific widget again (initializing a new one doesn't count).
My problem is this. I create a tkinter widget and later down the road I create a new frame that I want to add this widget to. When I call .grid() on the widget the widget is placed on the first frame, not the newer one that I want it to be on.
By default a widget is managed by its parent. If you don't want that, use the parameter in_ when calling pack, place or grid.
For example:
self.f1 = tk.Frame(...)
self.label = tk.Label(self.f1, ...)
self.label.pack(...)
...
self.f2 = tk.Frame(...)
self.label.pack(in_=self.f2, ...)
However, if you find yourself doing this a lot, you're probably doing something wrong. This is almost never necessary in most tkinter applications.
Is there a command/function in Python to erase the contents of a Tk() (window) and re-use the frame within which the window is inside?
I want to ask a user for input, then erase the labels/buttons in the window, and set up new labels/buttons inside the same window.
Also how do I create a list of labels with which to loop through and add to a Tk() window?
Keep a reference to all the widgets, and call the destroy() method on each one. Or, put all of the widgets inside another frame and destroy the frame -- destroying a frame will automatically destroy all children widgets.
I don't understand the question about creating labels in a loop. You do it like you create anything else in a loop. You can save the references in a list, though a dictionary is also convenient:
labels = {}
for name in ("one", "two", "three"):
labels[name] = tk.Label(..., text=name)
labels[name].pack(...)
I'm using Python and Tkinter to create a GUI for a program I'm writing, and I'm having a couple of problems.
I have three objects descended from LabelFrame in an object descended from Frame. One of the LabelFrame descendants is two columns of corresponding Label and Entry objects.
The problem is that there are a varying number of Label and Entry pairs, and there can be more than fit on the screen. I need a way to make a scrollbar for this LabelFrame so that everything fits on the screen. I've tried various ways of making a Scrollbar object, but nothing seems to work. How can I bind a scrollbar to this frame?
Also, I need to be able to refresh or reload this LabelFrame when the load_message() method is called, but it just redisplays the new pairs on top of the old ones (so when there are less pairs in the new set, the old set is still visible at the bottom). I've tried using grid_forget() but either nothing changes or the whole frame doesn't display. How can I forget this display and then redisplay it?
Here is the code for this class:
class freq_frame(LabelFrame):
def __init__(self, master = None, text = 'Substitutions'):
LabelFrame.__init__(self, master, text = text)
self.grid()
def load_message(self):
self.frequency = get_freq(message)
self.create_widgets()
def create_widgets(self):
self.label_list = [Label(self, text = get_label(char, self.frequency[char]), justify = LEFT) for char in self.frequency.keys()]
self.entry_list = [Entry(self, width = 1) for char in self.frequency.keys()]
for n in range(len(self.label_list)):
self.label_list[n].grid(column = 0, row = n)
for n in range(len(self.entry_list)):
self.entry_list[n].grid(column = 1, row = n)
If anyone can help with either of these problems, I'd appreciate it.
Also, this question seems like it might be a little thin, but I don't know what to add. Don't hesitate to ask for more information (but be specific).
Thanks!
Labelframes don't support scrolling. So the short answer to your question is "you can't". It sounds obvious, but if the documentation for a widget doesn't say it supports scrolling, it doesn't support scrolling.
However, there is a simple solution. First, add a canvas as a child to the labelframe and pack it so that it fills the labelframe. Attach scrollbars to the canvas and add them to the labelframe too. Then embed a frame within the canvas, add your widgets to that inner frame, and then adjust the scrollregion of the canvas to match the size of the frame after you've added all the inner labels and entries.
It sounds complicated, but it's really very straight-forward.
As for re-creating the widgets when you call load_message, calling grid_forget only removes them from view, it doesn't actually destroy the widgets. Over time you could potentially end up with hundreds of non-visible widgets which is almost certainly not what you want.
Instead, you want to first destroy all the existing widgets. That's pretty easy if they all are in the same parent, since you can ask the parent for a list of all its children. Just iterate over that list to delete each child, then add any new children. An even easier solution is to destroy and recreate that inner frame that contains the labels and entries. When you delete a widget, all child widgets get automatically destroyed. So, delete that inner frame, create a new one, and add your labels and entries again.