I have a window that is resizeable, where i want to have two listboxes with scroll bars that expand to fill all space available.
when i have only one listbox packed as fill=both, expand=1, side=left and one scrollbar packed as fill=Y, expand=0, side=right then it will expand only horizontally, even though it is set to fill both directions. when i resize the window, the listbox only fill the sides. the bottom of the window remain empty.
then i moved on to add another listbox. Now instead of packing the scroll bar on right, i packed everything to left, so they are stacked. the listboxes continue to have fill=both, expand=1. Now when i resize the window both list boxes only fill vertically! the horizontal space remains empty.
what is going on? why does it ignore the vertical space with one element packed left and another right? and why it refuses to fill horizontally when everything is stacked left?
the fact that once it fill the vertical or the horizontal space leads me to believe the parent frame is expanding fine... or should i investigate that more as well?
Without seeing your actual code it's impossible to know what you're doing wrong. Here's an example to prove that pack works as documented:
import Tkinter as tk
root = tk.Tk()
lb1 = tk.Listbox(root)
lb2 = tk.Listbox(root)
vsb1 = tk.Scrollbar(root, orient="vertical", command=lb1.yview)
vsb2 = tk.Scrollbar(root, orient="vertical", command=lb2.yview)
lb1.configure(yscrollcommand=vsb1.set)
lb2.configure(yscrollcommand=vsb2.set)
lb1.pack(side="left", fill="both", expand=True)
vsb1.pack(side="left", fill="y", expand=False)
lb2.pack(side="left", fill="both", expand=True)
vsb2.pack(side="left", fill="y", expand=False)
root.mainloop()
workaround i'm using (downvote if not the right tk way)
i created two frames, both side=LEFT, expand=1, fill=BOTH and then put each pair of listbox+scrollbar there. now everything expands/fills just fine.
previously the listboxes were in the yellow frame. The ones i just created are the the blue and green.
still not sure with the pack manager would not expand the listboxes when they had scrollbars without expansion between them...
Related
I am creating a registration form, and I have coded labels to show next to the text box for someone's username and password. This is the code I am using to place the text boxes and labels:
usernamebx.place(relx=0.5, rely=0.5, width=225, height=25,
anchor= CENTER)
userbx_label.place(relx=0.1, rely=0.5, anchor=CENTER)
passwbx.place(relx=0.5, rely=0.6, width=225, height=25, anchor = CENTER)
passwbx_label.place(relx=0.1, rely=0.6, anchor=CENTER)
The code for usernamebx and passwbx means that the text boxes don't move when I resize the tkinter window. However, I have done the same with the labels for each but it doesn't work. Any help?
The code for usernamebx and passwbx means that the text boxes don't move when I resize the tkinter window.
Actually, they do move! If you put a widget at relx 0.5 in a window that is 200 pixels wide, that means the center of the widget will be 100 pixels from the left edge of the window. When you grow the window to 400 pixels wide, the center of the widget now will be 200 pixels from the left edge. It moved 100 pixels. You don't see it because it's symmetrical so it stays in the center.
The same happens with a widget that is at 0.1. on a 200 pixel wide window it's going to be 20 pixels from the left edge. When you make the window 400 pixels widget it's going to be 40 pixels from the edge.
This is the nature of relative coordinates -- they will always change when the window is resized.
It's hard to see what your actual requirement is, though I'm guessing you want the username label+entry and password label+entry to be co-aligned in the center of the window.
If that's the case, one simple solution is to put those widgets in a frame. Use grid internally since it appears that you are in fact creating a grid. Then, you can place the frame in the window as a separate step.
Here's an example of the technique. For illustrative purposes the frame has a visible border, but that's not strictly necessary. You can remove the border to make it blend in with the background.
This example uses place to put the frame in the center, though you can also use pack.
import tkinter as tk
root = tk.Tk()
root.geometry("400x400")
inner_frame = tk.Frame(root, bd=2, relief="groove")
usernamebx = tk.Entry(inner_frame)
userbx_label = tk.Label(inner_frame, text="Username:")
passwbx = tk.Entry(inner_frame)
passwbx_label = tk.Label(inner_frame, text="Password:")
inner_frame.grid_columnconfigure(0, weight=1)
userbx_label.grid(row=0, column=0, sticky="e")
usernamebx.grid(row=0,column=1, sticky="ew")
passwbx_label.grid(row=1, column=0, sticky="e")
passwbx.grid(row=1, column=1, sticky="ew")
inner_frame.place(relx=.5, rely=.5, anchor="center")
root.mainloop()
If you want to use pack rather than place, have the packer expand the allocated space to be the whole window, and the frame will automatically be centered. In this case the window will shrink to fit the frame plus the padding.
inner_frame.pack(side="top", expand=True, padx=10, pady=10)
I want to pack two buttons (left and right) and a label (in the middle) in a frame. I want the label to fill the remaining space on the frame to both sides, but the widgets get displaced vertically with this code. What's the best way to do this? The widgets don't necessarily have to be packed on a frame but I want them to align horizontally while the text size of the label can change, but the buttons need to stay in place on the far left and right side. enter image description here
import tkinter as tk
root = tk.Tk()
root.geometry('600x800')
root.configure(background='#141414')
frm = tk.Frame(root)
frm.place(x=0, y=0, width=300, height=30)
btn1 = tk.Button(frm, text='button1')
lbl = tk.Label(frm, text='Lalalalalala')
btn2 = tk.Button(frm, text='button2')
btn1.pack(side='left')
lbl.pack(fill='x')
btn2.pack(side='right')
tk.mainloop()
You can solve this problem a couple of ways. One solution is to pack the label to one side or the other rather than the top.
btn1.pack(side='left')
lbl.pack(side='left', fill='x', expand=True)
btn2.pack(side='right')
Another is to pack the buttons first, and then pack the label. With pack the order matters.
btn1.pack(side='left')
btn2.pack(side='right')
lbl.pack(fill='x', expand=True)
For an illustrated explanation of how pack works see this answer to the question Tkinter pack method confusion
I am faced with the problem to center side-stacked frames in a parent frame. I know how to center a single frame in a frame but I did not find a simple way to do this for several of them.
I get the following window
from the code below:
import Tkinter as tk
root = tk.Tk()
root.geometry("200x200")
# main frame
f = tk.Frame(root, background='black')
f.pack(expand=True, fill="both")
# two side-by-side frames inside, they fill up their space
f1 = tk.Frame(f, background='green')
f1.pack(side=tk.LEFT, expand=True, fill="both")
f2 = tk.Frame(f, background='red')
f2.pack(side=tk.LEFT, expand=True, fill="both")
# three fixed-size frames in the left frame above; I would like them to be centered in the frame
tk.Frame(f1, width=20, height=20, background="orange").pack(side=tk.LEFT, fill=None, expand=False)
tk.Frame(f1, width=20, height=20, background="white").pack(side=tk.LEFT, fill=None, expand=False)
tk.Frame(f1, width=20, height=20, background="gray50").pack(side=tk.LEFT, fill=None, expand=False)
root.mainloop()
I would like the three square frames to be centered in the green one. I had to use tk.LEFT to position them, otherwise they would have been stacked up by default.
In my complete program, the green frame is there to exclusively contain the three square frames.
What is the most standard way to center the three square frames in the green one?
While thinking about furas's comment I realized that I did not understand the true difference between expand and fill (it is still a bit vague). It is possible to center the three frames by changing the f1.pack() line to:
f1.pack(side=tk.LEFT, expand=True, fill=None)
The f1 frame is tight around the three square (fill=None) ones buts tries to take as much space as possible in all directions (expand=True), effectively being centered. Note that the green background is not visible, the frame being tight around its content.
I was able to get the Scrollbar to work with a Text widget, but for some reason it isn't stretching to fit the text box.
Does anyone know of any way to change the height of the scrollbar widget or something to that effect?
txt = Text(frame, height=15, width=55)
scr = Scrollbar(frame)
scr.config(command=txt.yview)
txt.config(yscrollcommand=scr.set)
txt.pack(side=LEFT)
In your question you're using pack. pack has options to tell it to grow or shrink in either or both the x and y axis. Vertical scrollbars should normally grow/shrink in the y axis, and horizontal ones in the x axis. Text widgets should usually fill in both directions.
For doing a text widget and scrollbar in a frame you would typically do something like this:
scr.pack(side="right", fill="y", expand=False)
text.pack(side="left", fill="both", expand=True)
The above says the following things:
scrollbar is on the right (side="right")
scrollbar should stretch to fill any extra space in the y axis (fill="y")
the text widget is on the left (side="left")
the text widget should stretch to fill any extra space in the x and y axis (fill="both")
the text widget will expand to take up all remaining space in the containing frame (expand=True)
For more information see http://effbot.org/tkinterbook/pack.htm
Here is an example:
from Tkinter import *
root = Tk()
text = Text(root)
text.grid()
scrl = Scrollbar(root, command=text.yview)
text.config(yscrollcommand=scrl.set)
scrl.grid(row=0, column=1, sticky='ns')
root.mainloop()
this makes a text box and the sticky='ns' makes the scrollbar go all the way up and down the window
Easy solution to use a textbox with an integrated scrollbar:
Python 3:
#Python 3
import tkinter
import tkinter.scrolledtext
tk = tkinter.Tk()
text = tkinter.scrolledtext.ScrolledText(tk)
text.pack()
tk.mainloop()
To read the textbox:
string = text.get("1.0","end") # reads from the beginning to the end
Of course you can shorten the imports if you want.
In Python 2 you import ScrolledText instead.
I have the following code. My problem is that I can't manage to resize properly the frames. When I run the program, everything is as expected. But when I resize it, I want to keep the original view.
from Tkinter import *
import os
import sys
ALL=N+S+E+W
class Application(Frame):
def __init__(self,master=None):
Frame.__init__(self,master)
self.master.rowconfigure(0,weight=1)
self.master.columnconfigure(0,weight=1)
self.grid(sticky=ALL)
self.rowconfigure(0,weight=1)
myframe1=Frame(self,bg='green')
myframe1.bind("<Button-1>",self.handler1)
myframe1.grid(row=0,column=0,rowspan=1,columnspan=2,sticky=ALL)
self.rowconfigure(1,weight=1)
myframe2=Frame(self,bg='blue')
myframe2.bind("<Button-1>",self.handler2)
myframe2.grid(row=1,column=0,rowspan=1,columnspan=2,sticky=ALL)
buttons=('Red','Blue','Green','Black')
button=[0]*4
for c in range(4):
self.rowconfigure(c+2,weight=1)
self.columnconfigure(c,weight=1)
button[c]=Button(self,text="{0}".format(buttons[c]),command=lambda x=buttons[c]:self.colors(x))
button[c].grid(row=2,column=c,sticky=E+W)
self.columnconfigure(4,weight=1)
self.rowconfigure(6,weight=1)
button1=Button(self,text='{0}'.format('Open'),command=self.content)
button1.grid(row=2,column=4,sticky=E+W)
f=Frame(self,bg='red')
self.myentry=Entry(f)
self.myentry.grid(row=0,column=4,sticky=ALL)
self.text=Text(f)
self.text.grid(row=1,column=4,sticky=ALL)
f.grid(row=0,column=2,rowspan=2,columnspan=3,sticky=ALL)
...
I tried many combinations of rowconfigure, columnconfigure, rowspan, columnspan, but I failed!
My original view is:
After resizing in one direction:
In another direction:
The white area is the Text widget which I want to be resizable (also the blue and green areas).
Your problem is that you seem to not quite understand how grid works. For example, you are putting only two widgets in the red frame (self.myentry and self.text) yet you are putting them in column 2 and 4. Are you aware that the columns are relative to their parent, not the GUI as a whole? You want them in column 0 of the red frame, then you want the red frame in the second column of it's parent.
The way to solve this is to divide and conquer. First, divide the main screen up into it's logical parts, and lay out those logical parts so they resize properly. Then, for anything inside each part, lather, rinse repeat. Using frames for organization is the way to go.
Here's how I would tackle your problem (though there's certainly more than one way to solve this problem). First, you have two major areas of the screen: the top portion which has the green, blue and red frames and their contents, and the bottom part which holds the buttons. The top area should grow and shrink in all directions, the bottom area only grows in the X direction. I would create two frames for this, one for each part, and use pack since pack is the simplest geometry manager. The top frame should be configured to fill both directions and expand. The bottom part (with the buttons) should only fill in the X direction.
You now have two areas that are independent of each other and have proper resize behavior: the "main" area and the "toolbar" area. You are free to arrange the inner contents of these frames however you wish without having to worry about how that affects the main layout.
In the bottom frame, if you want all the widgets to be the same size, use pack and have them all fill X and expand, and they will equally fill the area. If you want them to be different sizes, use grid so you can control each column separately.
For the top part, it has three sub-sections: the red, green and blue frames. Since they are not all arranged horizontally or vertically I would use grid. Place green in cell 0,0, blue in cell 0,1, and red in cell 1,1 spanning two rows. Give row 0 and column 1 a weight of 1 so it takes up all the slack.
As I wrote earlier, this isn't the only way to "divide and conquer" this specific problem. Instead of seeing the main app as two parts -- top and bottom, with the top part having three sub-parts, another choice is to see that your main window has four parts: green, blue, red and toolbar. The key isn't to pick the perfect definition, but to break the layout problem down into chunks working from the outside in.
Here is a working example:
from Tkinter import *
ALL=N+S+E+W
class Application(Frame):
def __init__(self,master=None):
Frame.__init__(self,master)
# the UI is made up of two major areas: a bottom row
# of buttons, and a top area that fills the result of
# UI
top_frame = Frame(self)
button_frame = Frame(self)
button_frame.pack(side="bottom", fill="x")
top_frame.pack(side="top", fill="both", expand=True)
# top frame is made up of three sections: two smaller
# regions on the left, and a larger region on the right
ul_frame = Frame(top_frame, background="green", width=200)
ll_frame = Frame(top_frame, background="blue", width=200)
right_frame = Frame(top_frame, background="red")
ul_frame.grid(row=0, column=0, sticky=ALL)
ll_frame.grid(row=1, column=0, sticky=ALL)
right_frame.grid(row=0, column=1, rowspan=2, sticky=ALL)
top_frame.columnconfigure(1, weight=1)
top_frame.rowconfigure(0, weight=1)
top_frame.rowconfigure(1, weight=1)
# the right frame is made up of two widgets, an entry
# on top and a text below
entry = Entry(right_frame)
text = Text(right_frame)
entry.pack(side="top", fill="x")
text.pack(side="top", fill="both", expand=True)
# the button frame has five equally spaced buttons
for color in ('Red', 'Blue', 'Green', 'Black'):
b = Button(button_frame, text=color)
b.pack(side="left", fill="x", expand=True)
quit_button = Button(button_frame, text="Quit")
quit_button.pack(side="left", fill="x", expand=True)
root = Tk()
app = Application(root)
app.pack(fill="both", expand=True)
root.mainloop()