Frame resizing issue in Tk/ttk python - python

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")

Related

tkinter, pack inside grid and propagation not working

I am trying to create a button with a fixed size. So, I created a Frame (grid) with the size I want and then a child Button (pack) inside of the Frame that takes all the space.
Here is a minimal example:
window = tk.Tk()
frame1 = tk.Frame(window, bg="#ffffff", height=50, width=100)
frame1.grid(row=0, column=0)
frame1.grid_propagate(0)
#frame1.propagate(False) # <----- This line makes it work.
button1 = tk.Button(frame1, bg="#000000")
button1.pack(expand=True, fill=tk.BOTH)
Without the commented line above, I expected that it would work (having a rectangle of the specified size), but I get a small square. Why is it not working?
I was not able to find documentation on that .propagate() function. I only found about the grid_propagate() and pack_propagate() functions.
Right now you have 2 containers: window and frame1. When you use frame1.grid(...), you actually tell the master of frame1, which is window that it should use the grid manager. When you use button1.pack(...), you tell the button's master (frame1) that it should use the pack manager inside itself.
So when you use frame1.grid_propagate(...), you tell the grid manager (that doesn't manage the widgets inside frame1), that it should do something, so it ignores you.

Python - is there any way to expand the resolution ratio for existing Tkinter Tk() and Toplevel() widget? (zooming-in)

I just made an app using python and tkinter widgets.
There are Labels, Frames, Buttons, etc in the Tk and Toplevel widgets.
However, it includes thousands of codes and its really annoying to resize every widgets when I support multiple resolutions.
Is there any way to expand the resolution ratio for existing Tkinter Tk() and Toplevel() widget and their child widgets? (zooming-in)
If not, what would be the best approach to support multiple resolutions of a python app with the same ratio?
Any help would be much appreciated, sorry for bad English.
Yes, this is possible however it depends on the geometry manager you have used in your program.
For the .pack() method (which is arguably the simplest geometry method for "intelligent" GUI designs) you can use a range of attributes on when you declare .pack() on the widget. These attributes include (but are not limited to) fill, expand, anchor, padx, pady, etc.
The below shows an example of a set of three buttons which will automatically expand to fit the window if it changes or is initialised to a different size than was used during development.
from tkinter import *
root = Tk()
btn1 = Button(root, text="btn1")
btn2 = Button(root, text="btn2")
btn3 = Button(root, text="btn3")
btn1.pack(fill="both", expand=True)
btn2.pack(fill="both", expand=True)
btn3.pack(fill="both", expand=True)
root.mainloop()
For the .grid() method you will need to make use of the functions Grid.columnconfigure() and Grid.rowconfigure. Both of these have the attribute weight which determines which rows and columns should be given priority for assignment of extra space if more becomes available in the window. Setting all rows and columns to have a weight of 1 means they will all be given space equally. You will also need to use the sticky attribute when declaring .grid() on the widgets.
The below shows an example of a set of three buttons which will automatically expand to fit the window if it changes or is initialised to a different size than was used during development.
from tkinter import *
root = Tk()
for column in range(3):
Grid.columnconfigure(root, column, weight=1)
for row in range(1):
Grid.rowconfigure(root, row, weight=1)
btn1 = Button(root, text="btn1")
btn2 = Button(root, text="btn2")
btn3 = Button(root, text="btn3")
btn1.grid(column=0, row=0, sticky=N+S+E+W)
btn2.grid(column=1, row=0, sticky=N+S+E+W)
btn3.grid(column=2, row=0, sticky=N+S+E+W)
root.mainloop()
Using .place() would be a lot more difficult, you would need to have a function setup which would trigger on every window resize event which would calculate the size that the buttons need to expand to.
This would look something like the below:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.button = Button(self.root, text="Button")
self.button.place(relx=0.5, rely=0.5, anchor="center")
self.root.bind("<Configure>", self.resize)
def resize(self, *args):
self.button.configure(width=self.root.winfo_width(), height=self.root.winfo_height())
root = Tk()
App(root)
root.mainloop()
Subjectively speaking, .pack() tends to be easier, however this all comes down to how much effort you're willing to put in to implement this with your current program.
Can't comment so I add a short tip to the detailed Ethan answer. You can design most of the GUIs in tkinter with either pack, grid or a combination of both (by placing frames on a window with one of them, and using either grid or pack inside of each frame, to "place" the widgets). You can tune the configurations for proper location and size when the window resizes. Keep placer use for special cases (like overlaying some widget on the top of others)

Setting Tkinter/ttk Frame background color

I'm trying to change the background color of a ttk frame and I've looked up other examples, but none have seemed to work. This is my code so far:
from Tkinter import *
import ttk
p = Tk()
p.geometry('600x350')
p.configure(bg='#334353')
gui_style = ttk.Style()
gui_style.configure('My.TButton', foreground='#334353')
gui_style.configure('My.TFrame', background='#334353')
frame = ttk.Frame(p, style='My.TFrame')
frame.grid(column=1, row=1)
ttk.Button(frame, text='test', style='My.TButton').grid(column=0, row=0)
ttk.Button(frame, text='Test 2', style='My.TButton').grid(column=3, row=3)
p.mainloop()
The window has the background color that I want, but the frame still has the default gray background. Is there something i need to add differently? I want the entire window except for the buttons to be the color #334353. How do I do this?
EDIT: I've attached what my window looks like. I don't want the gray. :/ (Note. I don't have enough rep to post images apparently, so here is a link to imgur with my current window: http://imgur.com/KyhbdMB
Your frame is only sized to the minimum size required to hold the two child windows (the buttons). It seems like you want the frame to fill the main window. When you grid the frame you should add the sticky option to have it expand to fill the available space (eg: frame.grid(column=1,row=1,sticky='news')). Then you need to have the parent allocate all the space space to this grid cell. For that you want to use the grid_rowconfigure and grid_columnconfigure methods for the parent window. In this case:
p.grid_columnconfigure(1, weight=1)
p.grid_rowconfigure(1, weight=1)
which tells the main frame grid geometry manager that spare space should be given to the cell and row 1 column 1. This will lead to your frame expanding to fill the window.
It works on my PC!
Try this:
Update your Python environment(Tested under Py 3.4 Windows 32bit)
Install the lastest TTK package

tkinter's .pack_propagate() method

I am experimenting with Tkinter, as I was trying to figure out is there a way to set the tkinter's window size without using canvas. I came upon this how to set frame size question on SO's Question & Answer. So I went ahead and test it by writing a very small program to display a text label. But I found out it is "missing", or disappear when I use frame.pack_propagate(0)
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root, width=400, height=400)
# Does not work at the moment, textBox is missing
# frame.pack_propagate(0)
frame.pack()
textBox = tk.Label(frame, text="(x,y): ")
textBox.pack()
root.mainloop()
So my question is, can you explain why my textBox (Label) is not appearing when I use the frame.pack_propagate(0) instead of frame.pack() method? And secondly, is there a way to set the window size without using a canvas? I want to know because I am writing a series of small programs to teach my friend about tkinter, before introducing canvas to him. It would be nice if the window size are all the same across my tkinter samples. And I am just wondering as well (curious). Thank you very much.
I am using python 3.2.2 on MAC OS 10.5.8.
pack_propagate only sets a flag, it doesn't cause the frame to be placed in the widget. It is not a substitute for calling pack.
In other words you must do this:
# put the frame in its parent
frame.pack()
# tell frame not to let its children control its size
frame.pack_propagate(0)
# put the textbox in the frame
textBox.pack()
To answer your second question: Yeah, there is a way.
tkinters Tk do have the Tk.geometry function. When you just call it without arguments, you will get the current geometry in form of 'widthxheight+x+y', so for example (on Windows 10) '200x200+26+26' when you create your first Tk window. Using that format you can resize the Tk by, e.g., writing: root.geometry('400x500+60+60') to set the width to 400, the height to 500 and place it at the coordinates (60|60).
This works for Tk alswell as for Toplevel. But Toplevel also takes the arguments height and width when initialized or configured. If you want them to keep their size when packing something inside just use root.pack_propagate(False) on them.
By the way there is something similar for the grid manager: root.grid_propagate(False)

Why does Tkinter frame resize when text box is added to it?

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.

Categories

Resources