Having issues packing two frames to get an expected outcome as defined below:
Frame 1 to have a width of 150 and scale on Y value, color of blue.
Frame 2 to scale on both X and Y, color of red.
So that when the window is resized the frame 1 keeps its x position and the frame 2 will scale.
As seen in picture below:
Expected Outcome
So this was the code used:
import tkinter as tk
frame1 = tk.Frame(bg = 'blue', width= 150, height = 150)
frame2 = tk.Frame(bg = 'red')
frame1.pack(fill = tk.Y, expand = 1, anchor = tk.W, side = tk.LEFT)
frame2.pack_propagate(True)
frame2.pack(fill = tk.BOTH, expand = 1, anchor = tk.E, side = tk.RIGHT)
Though it produces this: Actual Outcome
A requirement for this is I still need to use pack, but stuck on how to get the outcome required.
Your only problem is that frame1 needs expand to be set to False or 0.
By setting it to 1, you're asking Tkinter to expand the area given to the left frame. Since you only have it fill in the "y" direction, the frame on the left doesn't fill up that extra area even though it has been allocated, which is why you see the blank space between the left and the right side.
fairly simple, if you want them to stack side by side you need to keep stacking them from the same side, and the first frame doesn't need expand=1
try the following:
import tkinter as tk
root = tk.Tk()
frame1 = tk.Frame(bg = 'blue', width= 150, height = 150)
frame2 = tk.Frame(bg = 'red')
frame1.pack(fill = tk.Y, anchor = tk.W, side = tk.LEFT)
frame2.pack_propagate(True)
frame2.pack(fill = tk.BOTH, expand = 1, anchor = tk.E, side = tk.LEFT)
root.mainloop()
Related
For my tkinter app I want to make a frame that would on top of other widgets, not taking any space (like html position fixed).
The Frame will have to contain widgets, if Frame is not possible labels or buttons will do.
I have no idea how to do it so haven't tried anything yet. Please help!
Here is a demonstration of place manager.
Remarks in the code explain behaviour.
import tkinter as tk
root = tk.Tk()
root.geometry("868x131")
button = tk.Button(root, text = "A Button")
button.place(x = 2, y = 2)
Frame = tk.LabelFrame(root, text = "A LabelFrame using place", bg="cyan")
# Frame using place with x, y, width, height in absolute coordinates
Frame.place(x = 250, y = 20, width = 600, height = 70)
ButtonInFrame = tk.Button(Frame, text = "A Button IN LabelFrame", bg="white")
# Note: place x,y is referenced to the container (Frame)
# Note: place uses just x,y and allows Button to determine width and height
ButtonInFrame.place(x = 2, y = 2)
Label = tk.Label(root, text = "A Label on LabelFrame\nWith multiple lines\nOf Text.", bg="light green")
# Label sits on top of buttonFrame and Frame
# Note place uses just x,y and allows Label to determine width and height
Label.place(x = 330, y = 60)
root.mainloop()
Im trying to get a tkinter gui that lets the user sign in im really new to tkinter. I made a frame and when i use grid to put another frame widget inside of the frame it does it based off of the root and not the inner_frame thats what I think is happening. In the code I made a grey box to demonstrate and I dont understand why it is below the blue frame and not inside the yellow frame under the "sign in" text. Thanks for the help.
from tkinter import *
root = Tk()
root.title("sighn in test")
#colors
background = "#273E47"
accent = "#d8973c"
red = "#bb4430"
white = "#edf2f4"
#this creates and places the background frame
main_buttons_frame = Frame(root, height = 500, width = 400, bg = background).grid(row = 0, column = 0)
#this creates and places the inner frame
inner_frame = Frame(main_buttons_frame, height = 450, width = 300, bg = accent).grid(row = 0, column = 0)
#this creates and places the "sighn in text"
top_text = Label(inner_frame, text = "sign in", font = ("helvitica", 30, "bold"), bg = accent, fg =
background).grid(row = 0, column = 0)
#this is a test to demonstrate
test_frame = Frame(inner_frame, bg = "grey", height = 100, width = 100).grid(row = 1, column = 0)
root.mainloop()
You have very common mistake of beginners
inner_frame = Frame(...).grid...()
It assigns None to inner_frame because grid()/pack()/place() gives None.
So later Frame(inner_frame, ..) means Frame(None, ..) and it adds to root
You have to do it in two steps
inner_frame = Frame(...)
inner_frame.grid(...)
And now you have Frame assigned to inner_frame
EDIT:
With correctly assigned widgets I get
and now gray box is inside yellow frame but image shows different problem - grid()/pack() automatically calculate position and size and external frame automatically change size to fit to child's size.
Using .grid_propagate(False) you can stop it
But it shows other problem - cells don't use full size of parent so yellow frame is moved to left top corner, not centered :) Empty cells have width = 0 and heigh = 0 so moving all to next row and next column will not change it - it will be still in left top corner :)
You need to assign weight to column and/or row which decide how to use free space. All columns/rows has weight=0 and when you set weight=1 then this row and/or column will use all free space - (this would need better explanation) - and then element inside cell will be centered.
main_buttons_frame.grid_columnconfigure(0, weight=1)
main_buttons_frame.grid_rowconfigure(0, weight=1)
import tkinter as tk # PEP8: `import *` is not preferred
# --- colors ---
background = "#273E47"
accent = "#d8973c"
red = "#bb4430"
white = "#edf2f4"
# --- main ---
root = tk.Tk()
root.title("sighn in test")
main_buttons_frame = tk.Frame(root, height=500, width=400, bg=background) # PEP8: without spaces around `=` inside `( )`
main_buttons_frame.grid(row=0, column=0)
#main_buttons_frame = None
main_buttons_frame.grid_propagate(False)
main_buttons_frame.grid_columnconfigure(0, weight=1)
main_buttons_frame.grid_rowconfigure(0, weight=1)
inner_frame = tk.Frame(main_buttons_frame, height=450, width=300, bg=accent)
inner_frame.grid(row=0, column=0)
#inner_frame = None
inner_frame.grid_propagate(False)
inner_frame.grid_columnconfigure(0, weight=1)
inner_frame.grid_rowconfigure(0, weight=1)
top_text = tk.Label(inner_frame, text="sign in", font=("helvitica", 30, "bold"), bg=accent, fg=background)
top_text.grid(row=0, column=0,)
#top_text = None
#top_text.grid_propagate(False)
#top_text.grid_columnconfigure(0, weight=1)
#top_text.grid_rowconfigure(0, weight=1)
test_frame = tk.Frame(inner_frame, bg="grey", height=100, width=100)
test_frame.grid(row=1, column=0)
#test_frame = None
#test_frame.grid_propagate(False)
#test_frame.grid_columnconfigure(1, weight=1)
#test_frame.grid_rowconfigure(0, weight=1)
root.mainloop()
BTW: PEP 8 -- Style Guide for Python Code
So I've been using the canvas widget in tkinter to create a frame full of labels which has a scrollbar. All is working good except that the frame only expands to the size of the labels placed in it - I want the frame to expand to the size of the parent canvas.
This can easily be done if I use pack(expand = True) (which I have commented out in the code below) for the frame in the canvas but then then the scrollbar doesn't work.
Here's the appropriate bit of code:
...
self.canvas = Canvas(frame, bg = 'pink')
self.canvas.pack(side = RIGHT, fill = BOTH, expand = True)
self.mailbox_frame = Frame(self.canvas, bg = 'purple')
self.canvas.create_window((0,0),window=self.mailbox_frame, anchor = NW)
#self.mailbox_frame.pack(side = LEFT, fill = BOTH, expand = True)
mail_scroll = Scrollbar(self.canvas, orient = "vertical",
command = self.canvas.yview)
mail_scroll.pack(side = RIGHT, fill = Y)
self.canvas.config(yscrollcommand = mail_scroll.set)
self.mailbox_frame.bind("<Configure>", self.OnFrameConfigure)
def OnFrameConfigure(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
I've also provided an image with colored frames so you can see what I'm getting at. The pink area is the canvas that needs filling by the mailbox_frame (You can see the scrollbar on the right):
Just for future reference in case anyone else needs to know:
frame = Frame(self.bottom_frame)
frame.pack(side = LEFT, fill = BOTH, expand = True, padx = 10, pady = 10)
self.canvas = Canvas(frame, bg = 'pink')
self.canvas.pack(side = RIGHT, fill = BOTH, expand = True)
self.mailbox_frame = Frame(self.canvas, bg = 'purple')
self.canvas_frame = self.canvas.create_window((0,0),
window=self.mailbox_frame, anchor = NW)
#self.mailbox_frame.pack(side = LEFT, fill = BOTH, expand = True)
mail_scroll = Scrollbar(self.canvas, orient = "vertical",
command = self.canvas.yview)
mail_scroll.pack(side = RIGHT, fill = Y)
self.canvas.config(yscrollcommand = mail_scroll.set)
self.mailbox_frame.bind("<Configure>", self.OnFrameConfigure)
self.canvas.bind('<Configure>', self.FrameWidth)
def FrameWidth(self, event):
canvas_width = event.width
self.canvas.itemconfig(self.canvas_frame, width = canvas_width)
def OnFrameConfigure(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
Set a binding on the canvas <Configure> event, which fires whenever the canvas changes size. From the event object you can get the canvas width and height, and use that to resize the frame.
Just an updated answer which covers both horizontal and vertical scrollbars without breaking them.
def FrameWidth(self, event):
if event.width > self.mailbox_frame.winfo_width():
self.canvas.itemconfig(self.canvas_frame, width=event.width-4)
if event.height > self.mailbox_frame.winfo_height():
self.canvas.itemconfig(self.canvas_frame, height=event.height-4)
Only sets the frame height and width if they are less than the canvas width. Respects both Horizontal and Vertical scrollbars.
self.root = tkinter.Tk()
self.frame = Frame(self.root, width = 1300, height = 1300, bg = "yellow")
self.frame.grid(row = 0 , column = 0, sticky = 'nw')
self.frame.grid_rowconfigure(0, weight=1)
self.frame.grid_columnconfigure(0, weight=1)
self.frame.grid_propagate(True)
self.canvas_main = Canvas(self.frame,
height = 300,
width = 300,
scrollregion=(500,500,0,500)
)
self.canvas_main.grid(row = 0, column = 0, padx= 10, pady = 10, sticky = "news")
self.scrollbar = Scrollbar(self.root, orient = VERTICAL, command = self.canvas_main.yview)
self.scrollbar.grid(row = 0, column = 1, sticky = 'ns')
self.canvas_main.configure(yscrollcommand=self.scrollbar.set)
In above code block, I have entered Scrollbar element for enabling vertical scrollbar however, it does not reflect. Can anyone find out what I am doing wrong in this block?
You've set the scrollable region (scrollregion) to be zero pixels tall, so tkinter thinks there's nothing to be scrolled in the vertical direction.
Often you will do your drawing first and then set the scrollable region to encompass everything you've drawn. You do this by getting the bounding box -- the smallest rectangle that includes all of the canvas objects -- and then using that as the value for scrollregion.
Example:
canvas = tk.Canvas(...)
<draw a bunch of objects>
canvas.configure(scrollregion=canvas.bbox("all"))
The other method is to define ahead of time how big the virtual drawing area is. For example, you've set your canvas to have a width and height of 300. If you want the actual drawing area to be twice the visible size then you could set the scrollregion to an area twice that big.
This example sets the scrollable region to begin at 0,0 (the upper left corner), and extend to 600,600 (the bottom right corner):
canvas = tk.Canvas(..., width=300, height=300)
canvas.configure(scrollregion=(0,0,600,600)
I'm trying to display a window with tkinter that has a frame which occupies the entire bottom row.
Here is the code as I have it now:
import openpyxl, PIL.Image, sys
from tkinter import *
class App(object):
def __init__(self, root):
self.root = root
w, h = root.winfo_screenwidth() * .9, root.winfo_screenheight() * .9
root.geometry("%dx%d+0+0" % (w, h))
root.title('Squirrel Project')
root.wm_iconbitmap('Squirrel.ico')
buttonFrame = Frame(root, width = w, height = 25, padx = 15, pady = 15, bg = 'blue')
buttonFrame.pack(fill = X, expand = True, side = BOTTOM)
saveButton = Button(buttonFrame, text = 'Save', command = self.save).pack(side = LEFT)
With the above code, the frame occupies the entire width of the window, but in the middle row. If I remove fill = X, expand = True from buttonFrame.pack then the frame will occupy the bottom row, but only a portion of it. How can I have the frame occupy the entire bottom row?
You've given the option expand=True, which is telling the frame to take up extra space in the window. Thus, since it's the only thing in the window it uses all of the space in the window.
If you don't want it to take up extra space, omit that option or set it to False.