Tkinter frame to occupy full bottom row of window - python

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.

Related

How to make a floating Frame in tkinter

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

tkinter useing grid on a frame doesn't seem to work

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

tkinter Frame not filling parent Canvas [duplicate]

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.

set Tkinter label widget below entry widget

I am relatively new to Tkinter and I am trying to create a GUI with three horizontally aligned entry widgets and three corresponding label widgets. I am struggling with how to put the label widgets below the entry widgets without disturbing the alignment of the entry boxes (i.e., pack side = LEFT). Here is an example snapshot of the GUI without the labels below the entry boxes:
Currently my script looks like this, with the label widgets commented out:
#!/usr/bin/python
from sys import exit
from Tkinter import *
from subprocess import Popen, PIPE
from time import sleep
from os.path import exists
import os
import time
import sys
import ttk
#Initialize Tkinter by creating Tk root widget, which is a window with a
title bar. The root widget has to be created before any other widgets.
Root = Tk()
Root.title("GUI")
class Command:
def __init__(self, func, *args, **kw):
self.func = func
self.args = args
self.kw = kw
def __call__(self, *args, **kw):
args = self.args+args
kw.update(self.kw)
self.func(*args, **kw)
#Script definitions#
def quitter():
exit(0)
#Button organization and layout#
Sub0 = Frame(Root)
Sub = Frame(Sub0)
#X mesh node
Label(Sub, text = "Create x mesh nodes").pack(side = TOP, pady = 5)
#x min text box area
v1=StringVar()
#Create entry widge to get user input, in this case a text string for the x
min mesh
Entry(Sub, textvariable=v1).pack(side = LEFT, pady = 5, padx = 5)
#Label(Sub, text = "min").pack(side = BOTTOM, pady = 5, padx = 5)
#x max text box area
v2=StringVar()
Entry(Sub, textvariable=v2).pack(side = LEFT, pady = 5, padx = 5)
#Label(Sub, text = "max").pack(side = BOTTOM, pady = 1)
#x step text box area
v3=StringVar()
Entry(Sub, textvariable=v3).pack(side = LEFT, pady = 5, padx = 5)
#Label(Sub, text = "step increment").pack(side = LEFT, pady = 1)
Sub.pack(side = TOP)
Frame(Sub0,height=1, relief = GROOVE, bg="black").pack(side = TOP, fill =
BOTH, pady= 5)
#GUI Organization#
Sub0.pack(side = TOP, padx = 10)
Button(Root, text = "Quit", command = quitter).pack(side = TOP, pady = 5)
#The window will not appear until we enter the Tkinter event loop. Script
will remain in the event loop until we close the window.
Root.mainloop()
When I uncomment the label widgets I get the following:
Can someone guide me in the right direction on how to make this happen? Or suggest a better way to implement it? Also, is there a way to control the size of the entry widgets? I'd like them to be smaller. Thank you in advance!
You can use the grid geometry manager for this:
from sys import exit
from Tkinter import *
from subprocess import Popen, PIPE
from time import sleep
from os.path import exists
import os, time, sys, ttk
#Initialize Tkinter by creating Tk root widget, which is a window with a
Root = Tk()
Root.title("VIZ TOOL")
class Command:
def __init__(self, func, *args, **kw):
self.func = func
self.args = args
self.kw = kw
def __call__(self, *args, **kw):
args = self.args+args
kw.update(self.kw)
self.func(*args, **kw)
#Script definitions#
def quitter():
exit(0)
#Button organization and layout#
Sub0 = Frame(Root)
Sub = Frame(Sub0)
#X mesh node
Label(Sub, text = "Create x mesh nodes").grid(columnspan=3)
#x min text box area
v1=StringVar()
#Create entry widge to get user input, in this case a text string for the x
Entry(Sub, textvariable=v1).grid(row=1, column=0)
Label(Sub, text = "min").grid(row=2, column=0)
#x max text box area
v2=StringVar()
Entry(Sub, textvariable=v2).grid(row=1, column=1)
Label(Sub, text = "max").grid(row=2, column=1)
#x step text box area
v3=StringVar()
Entry(Sub, textvariable=v3).grid(row=1, column=2)
Label(Sub, text = "step increment").grid(row=2, column=2)
Sub.pack(side = TOP)
Frame(Sub0,height=1, relief = GROOVE, bg="black").pack(side = TOP, fill =
BOTH, pady= 5)
#GUI Organization#
Sub0.pack(side = TOP, padx = 10)
Button(Root, text = "Quit", command = quitter).pack(side = TOP, pady = 5)
#The window will not appear until we enter the Tkinter event loop. Script
Root.mainloop()
For the size of the entries you can do something like this:
Root.option_add("*Entry.Width", 5) #10 is default
Using option add like this will override the size of all entry widgets, you can specify individual size with the width keyword on Entry(....., width = *, ...)

tkinter pack doesn't expand through whole area

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

Categories