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.
Related
I am creating a Tkinter/Python3 application where the main window inherits from Notebook (i need tabs), and each tab should be a custom class inheriting from Frame (I would then dynamically use matplotlib to create custom graphs).
Unfortunately I don't seem to be able to have Notebook accept my custom Frames.
Following very reduced snippet of code:
#!/usr/bin/env python3
from tkinter import *
from tkinter.ttk import Notebook
class MyFrame1(Frame):
def __init__(self, master=None, mytext=""):
super().__init__(master)
self.create_widgets(mytext)
def create_widgets(self, mytext):
self.label = Label(self.master, text=mytext, anchor=W)
# this is not placed relative to the Frame, but to the
# master
# 1. How I get the relative coordinates inside the frame
# to be 10, 10 of the frame area?
self.label.place(x=10, y=10, width=128, height=24)
class MyNotebook(Notebook):
def __init__(self, master=None):
super().__init__(master)
self.create_widgets()
def create_widgets(self):
self.f1 = MyFrame1(self, "abc")
# once the UI is drawn, the label "def" seems to overlay
# "abc" even when "f1" is selected
# 2. Why is self.f2 always shown even when self.f1 is
# selected?
self.f2 = MyFrame1(self, "def")
self.add(self.f1, text="f1")
self.add(self.f2, text="f2")
# Without this command nothing gets drawn
# 3. Why is this? Is this equivalent of 'pack' but for
# pixel driven layout?
self.place(width=640, height=480)
def main():
root = Tk()
root.minsize(640, 480)
root.geometry("640x480")
app = MyNotebook(master=root)
# this works as intended the label is indeed placed
# in the frame at 10, 10
#app = MyFrame1(master=root, mytext="123abc")
app.mainloop()
return None
if __name__ == "__main__":
main()
As per comments I have the following main question: why aren't my custom instances of MyFrame1 properly displayed inside MyNotebook?
Sub questions:
How can I get relative coordinate areas of where the frame is located when place my elements (in this case a Label)?
Why even when self.f1 tab is selected in the UI, I can still see the content of self.f2 tab?
Is self.place required in order to show all sub-elements when not using pack?
If I dynamically create Tkinter elements after the MyNotebook is initialized, will those be bound to respective tabs?
Not sure what I'm doing wrong?
Thanks!
Not sure what I'm doing wrong?
Your create_widgets method needs to add widgets to self, not self.master.
How can I get relative coordinate areas of where the frame is located when place my elements (in this case a Label)?
I don't understand what you mean by this. When you use place, coordinates will be interpreted relative to the frame. However, I strongly advise against using place. Both pack and grid will trigger the frame to resize to fit its children which almost always results in a more responsive UI
Why even when self.f1 tab is selected in the UI, I can still see the content of self.f2 tab?
Because you added internal widgets to self.master instead of self.
Is self.place required in order to show all sub-elements when not using pack?
No. It is required to use a geometry manager but it doesn't have to be place. Usually, place is the least desirable geometry manager to use. pack and grid are almost always better choices except for some very specific situations.
If I dynamically create Tkinter elements after the MyNotebook is initialized, will those be bound to respective tabs?
They will be in whatever tab you put them in.
Finally, I would suggest that you remove self.place in create_widgets. Instead, call pack, place, or grid in the same block of code that creates an instance of that class.
It's a bad practice for a widget to add itself to another widget's layout. The code that creates the widget should be the code that adds the widget to the layout.
from tkinter import *
window=Tk()
Button(window,text='').pack(in=?)
How to set 'in' attribute in pack method? what does that mean?
I looked at the source code of the pack, but I still couldn't understand how to set the ’in' attribute about pack method.
How to set 'in' attribute in pack method?
You must use in_ rather than in. For example:
root = tk.Tk()
frame = tk.Frame(root)
button = tk.Button(root)
button.pack(in_=frame)
what does that mean?
When laying out a widget, every widget except the root window has a master -- some other widget which contains the widget and which controls how it is laid out. By default the master is the same as the parent. For example, if you do Button(window,text=''), the parent of the button is window, and thus its master will also default to window.
You can use in_ to change the master. For example, you could make a button be the child of the root window, but be laid out in some other frame. In the earlier example, the button is a child of the root window but will be a slave inside of the frame.
This is something you almost never need to do with tkinter, since it's usually preferable to have the parent and the master be the same widget.
Hi I didn't really understand how furas made the below code work. Why didn't he get an error message about grid and pack on the same root when he added a box? In the addbox function he sets a frame to the root which is pack already and even uses the pack inside the function and then uses the grid.
Can someone please explain to me how this "magic" works?
a link to the his answer:
Creating new entry boxes with button Tkinter
from Tkinter import *
#------------------------------------
def addBox():
print "ADD"
frame = Frame(root)
frame.pack()
Label(frame, text='From').grid(row=0, column=0)
ent1 = Entry(frame)
ent1.grid(row=1, column=0)
Label(frame, text='To').grid(row=0, column=1)
ent2 = Entry(frame)
ent2.grid(row=1, column=1)
all_entries.append( (ent1, ent2) )
#------------------------------------
def showEntries():
for number, (ent1, ent2) in enumerate(all_entries):
print number, ent1.get(), ent2.get()
#------------------------------------
all_entries = []
root = Tk()
showButton = Button(root, text='Show all text', command=showEntries)
showButton.pack()
Thanks
There's no magic, it's just working as designed. The code uses pack in the root window, and uses grid inside a frame. Each widget that acts as a container for other widgets can use either grid or pack. You just can't use both grid and pack together for widgets that have the same master.
not really an answer but I think you will be helped by the link.
tkinter and it's layout is indeed a bit hard to understand.
I never understood how to deal with it until I stumbled over this presentation which explained the layout particulars in a way where I finally could get the hang of it.
Just putting it out there for others to find as well.
tkinter tutorial by beazley
I think you miss out on what pack and grid actually are. Consider such code:
import tkinter as tk
root = tk.Tk()
myFrame = tk.Frame(root)
myFrame.pack()
myButton1 = tk.Button(myFrame, text='This is button 1')
myButton2 = tk.Button(myFrame, text='This is button 2')
myButton1.grid(row=0, column=0)
myButton2.grid(row=1, column=0)
root.mainloop()
By creating root we create a new window. In this window we will put everything else. Then we create myFrame. Note, that the actual "thing" (in more adequate terms - widget) is created in line myFrame = tk.Frame(root). Note, that we have to specify where we are going to put this widget in brackets and we've written that it is going to be root - our main window. Blank frame probably isn't the best example since you can not see it being placed (not unless you use some more specifications at least), but still. We have created it, but not placed it in our user interface. The we use .pack() to place it. Now you refer to widgets as being used as packs or grids. That is not true though. Pack and grid are just the set of rules, on which the widgets are being placed inside some kind of window. Because of that, if you want to add something more to the root in our case, you will have to use .pack() again. Why? If you will give two sets of rules on how to place things on the screen for your computer - they will most likely conflict with each other. However, if we go one more level down and now want to place something inside our myFrame, we can again choose which set of rules to use. It is because it does not matter, where our frame is going to end up inside root, we now just want to specify where our Buttons 1 and 2 are going to end up inside the frame. Therefore we can again use .pack() or switch to .grid().
To conclude: .pack(), .grid() and .place() are sets of rules on how place widgets inside other widgets. In more general terms though these are rules on how place boxes in other boxes. One boxes in which we arrange other boxes can only have one set of rules.
I hope this example helps.
I am working on a program where I create some widgets in a for loop. So I need to get the name of them dynamically. I have set up is when the mouse enters the frame. Which holds the two text label widgets. I causes a function to run. And I want to change the background color of a widget with the name of noteName. But I seem to have run into a stopping point and I can no figure it out. I have searched online but could not find much. SO does anyone here know how to get the name of a widget?
Code:
def get_children_hover(event):
for widgets in event.widget.winfo_children():
#This is here where I can not seem to figure out how to get the widgets name.
Can someone push me into the right direction.
winfo_children() is the right thing to use but you are using it wrong. It is a method for parent widgets. (i.e. root, frame, canvas etc..)
Also:
If the order doesn’t matter, you can get the same information from the
children widget attribute (it’s a dictionary mapping Tk widget names
to widget instances, so widget.children.values() gives you a list of
instances).
simple example:
import tkinter as tk
def foo():
print ("Frame:", frm.winfo_children())
print ("Root:", root.winfo_children())
print ("children_values:", root.children.values())
root = tk.Tk()
frm = tk.Frame(root)
tk.Label(root,text="foo").pack()
btn = tk.Button(frm,text="FOOO",command=foo)
frm.pack()
btn.pack()
root.mainloop()
about your code:
def get_children_hover(event):
for widgets in root.winfo_children(): #assuming your Tk() instance named root
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.