I have a frame and I want it to be a certain size (relative to the root window). When I pack in a label with text big enough to make the label bigger than the frame that contains it, the frame expands. How do I prevent the frame from expanding and make the words that don't fit into one line just go into the next line. I need a solution different than simply adding \n because in my actual project the text is dependant on the users input.
from tkinter import *
root = Tk()
root.geometry("300x300")
f = Frame(root,width=100, height=50, bg="yellow")
f.place(relx=0.5, rely=0.5, anchor=CENTER)
Label(f, text="this is a veeeeeery looooong text").pack(side=LEFT)
root.mainloop()
If you want the label to be a fixed size you can specify a size for the label. Tkinter will do its best to honor the requested size. Of course, that means that long text will be chopped off in the label.
Label(f, width=4, text="this is a veeeeeery looooong text").pack(side=LEFT)
With that, the frame will shrink if it's larger than the space required by the label, but it won't grow since the label itself won't grow.
If you want the label to have no effect on the frame at all, you can turn geometry propagation off for the frame. By turning it off, you are telling tkinter to honor the requested size of the frame and not let the size of the children override that.
f.pack_propagate(False)
This is rarely a good idea since tkinter is really good at computing the optimal size, but there are some rare occassions where it is exactly the right thing to do.
Related
I have been attempting to create a application that contains two Text() widgets, both of which can dynamically resize when the window size is changed. Before I have always used the root.pack() manager, with fill='both' and expand=True.
While this works for LabelFrames and most other widgets, it does not work when a Text widget is resized smaller then its original dimensions. Is there a way to have dynamically resizing Text widgets?
Ex.
import tkinter as tk
window = tk.Tk()
editor = tk.Text(bg='red')
editor.pack(side='top',fill='both',expand=True)
output = tk.Text(bg='green')
output.pack(side='top',fill='both',expand=True)
window.mainloop()
Tkinter will try to honor the requested size of a text widget. Since you didn't specify a size, the text widget will request a size of 80x24. When you resize the window smaller, pack tries to make room for everything at its requested size, and it does so in stacking order.
As the window shrinks, there's room for all of the first text widget but not enough for both. Because there's not enough room, it has to subtract space from the remaining widgets. Thus, it starts chopping off the last text widget.
To combat this, you can set the requested size of the text widgets to a small value that will fit in almost any window size, and then force them to grow by setting the size of the window as a whole. This way, pack will first allocate enough space for each small window, and then expand them equally when there's extra space.
For example:
import tkinter as tk
window = tk.Tk()
window.geometry("400x400")
editor = tk.Text(bg='red', width=1, height=1)
output = tk.Text(bg='green', width=1, height=1)
editor.pack(side='top',fill='both',expand=True)
output.pack(side='top',fill='both',expand=True)
window.mainloop()
The other solution is to use grid which lets you specify that rows and columns should be of uniform size.
import tkinter as tk
window = tk.Tk()
editor = tk.Text(bg='red')
output = tk.Text(bg='green')
editor.grid(row=0, column=0, sticky="nsew")
output.grid(row=1, column=0, sticky="nsew")
window.grid_columnconfigure(0, weight=1)
window.grid_rowconfigure((0,1), weight=1, uniform=1)
window.mainloop()
I'm building a calculator in python, using the tkinter toolkit. The problem is that when the text is typed in the calculator (and added to the label that shoes the calculation) the whole width of the window changes and becomes longer. What can I do in order to solve it?
Here is some important parts of the code: (the main class inherits from tk.Frame)
labelStyle = {"padx":10, "pady":10, "justify":"left"}
calculationsLabel = tk.Label(self, text="", **labelStyle)
calculationsLabel.grid(row=0, column=1, **gridStyle)
self.master.resizable(0,0)
self.pack(padx=5, pady=5)
self.calculationsLabel=calculationsLabel
for number in range(9,0,-1):
...
tk.Button(self, ...).grid(...)
The images that illustrates the problem:
You need to do a couple of things to prevent the label from changing the window size:
Give the label a width of 1 (one). By not giving the label a width you are implicitly saying "grow bigger when the text is bigger". By giving it an explicit width you are saying "no matter how big the text is, don't grow if the text is bigger".
use the columnspan option to grid, to get the cell that the label is in to span the width of your grid of buttons. Use the sticky option, so that even though the label requests only one character of width, grid will force it to grow wide enough to fit the space.
You can use the columnspan argument to let the label stretch over top all of your columns, instead of stretching out the first one. Add to your grid method:
calculationsLabel.grid(row=0, column=1, columnspan=7 **gridStyle)
(replacing 7 with however many columns you actually have)
You could use the columnspan option of grid to make the label span the complete width of the calculator.
Then, if you want the number to be on the left use anchor=W in the Label.
This way the window only expands when the Label is longer than the complete window. And you can even prevent that by using the Label's width option.
I'm trying to get stretching to work using Python 2.6.7 with Tkinter. I'd expect the below code to stretch the first button to the width of the second, but both buttons are only as wide as they need to be to fit their text.
#!/usr/bin/python
from Tkinter import *
win = Frame()
win.grid(sticky=N+S+E+W)
inner_a = Frame(win)
inner_a.grid(row=0, column=0, sticky=N+E+W)
inner_b = Frame(win)
inner_b.grid(row=1, column=0, sticky=S+E+W)
Button(inner_a, text='1').grid(row=0, column=0, sticky=E+W)
Button(inner_b, text='Some long text').grid(row=0, column=0, sticky=E+W)
win.mainloop()
By my understanding, the single column in win will expand to the width of the largest thing it contains, ie the width of inner_b, and then the width of inner_a, and thence of the first button, will be that of the second button.
Actually, what happens is the below; the first button is only wide enough to contain "1", not as wide as the second button.
What do I need to do to get the first button to expand the size of the second?
If you want widgets to line up in a grid, the first thing to do is make sure they have the same parent. This isn't strictly necessary if all the widgets are the same size or you are only using one column.
Another thing you need to do is give your column a weight. What is happening in your code is that the widget is expanding to fill the column, but the column isn't expanding to fill the master. If you give it a weight of 1 it will. You ned to do something like inner_a.columnconfigure(1, weight=1), and then do likewise for inner_b.
I'm creating a GUI using Tkinter/ttk in Python 2.7, and I'm having an issue where a Frame will resize itself once a widget is placed inside of it. I am new to Python and haven't used Tkinter before.
example:
ROOT = Tk()
FRAME = ttk.Frame(ROOT, width=300, height=300, relief='groove')
FRAME.grid()
ROOT.mainloop()
will produce a grooved frame 300x300, if i place a widget inside it like so:
ROOT = Tk()
FRAME = ttk.Frame(ROOT, width=300, height=300, relief='groove')
BUTTON = ttk.Button(FRAME, text="DON'T READ THIS TEXT")
FRAME.grid()
BUTTON.grid()
ROOT.mainloop()
the frame will shrink down to fit the button. Any way to force the frame not to resize?
To force the frame to keep its original dimensions turn "geometry propagation" off. In your case you would call FRAME.grid_propagate(False).
Speaking as someone with over 15 years of Tk experience, might I suggest that you almost certainly don't need this feature. Tk's ability to "shrink to fit" is really great, and makes it really easy to create GUIs with proper resize behavior. Once you start turning geometry propagation off you'll find you'll either have GUIs with bad resize behavior, or you'll spend a lot of time tweaking sizes and widget placement.
if you want to add padding, then use widg.grid(ipadx=..., ipady=..., padx=..., pady=...)
otherwise, you will need to add more context on what layout you're trying to achieve
This works for me:
app = Application()
app.master.title('Example')
app.master.geometry('640x480')
app.mainloop()
Apllication is Frame in my case with grid layout. It resizes to size of master window, so we need to change size of master window.
This should do the job: ROOT.geometry("640x480")
With this code, the window is 500 by 500, which is what I'm going for:
from tkinter import *
root = Tk()
frame = Frame(root, width=500, height=500)
frame.pack()
root.mainloop()
When I add a text box to the frame, though, it shrinks to just the size of the text box:
from tkinter import *
root = Tk()
frame = Frame(root, width=500, height=500)
text = Text(frame, width=10, height=2) # THESE TWO
text.pack() # LINES HERE
frame.pack()
root.mainloop()
Why does this happen, and how can I prevent it from happening?
The frame by default has "pack propagation" turned on. That means the packer "computes how large a master must be to just exactly meet the needs of its slaves, and it sets the requested width and height of the master to these dimensions" (quoting from the official tcl/tk man pages [1]).
For the vast majority of cases this is the exact right behavior. For the times that you don't want this you can call pack_propagate on the master and set the value to false. I think in close to 20 years of tk programing I've only needed to do this a half dozen times or so, if that.
Another choice you have is to use wm_geometry to set the size of the toplevel after you've created all the widgets. This does effectively the same thing as if the user had manually resized the window. This only works for toplevel windows though, you can't use wm_geometry on a frame.