Is it possible to make tkinter window's contents adjust to screen size? Like if I had made a window according to a screen resolution of 1366x768, and then I run the same code on a system of 1024x768 resolution and make its contents resize dynamically? I know I can use grid structure as as described here and set the weight of cells accordingly. But is it possible with pack geometry structure and not grid? I am asking this since half of the project I am working on was made by my colleague and he didn't use grid at all. So instead of redoing all of the code in grid, I am looking for a way around it. Here is the sample code I need to resize -
def container(self):
frame_number_panel = tk.Frame(self.parent, width=round(Dimension.SCREEN_WIDTH*0.8), height=Dimension.SCREEN_HEIGHT, bg="#eeeeee")
frame_number_panel.pack(side="left", anchor=W)
frame_number_panel.pack_propagate(False)
self.main_container(frame_number_panel)
# right side pannel
frame_right_side_panel = tk.Frame(self.parent, bg="#101115", width=round(Dimension.SCREEN_WIDTH*0.2), height=Dimension.SCREEN_HEIGHT)
frame_right_side_panel.pack(side="right", anchor=NE)
frame_right_side_panel.pack_propagate(False)
frame_first_row = tk.Frame(frame_right_side_panel, bg="#101115")
frame_first_row.pack()
winning_claim_image = PhotoImage(file=ResourcePath.resource_path('images/icon_win_claim.png'))
label_winning_claim = tk.Label(frame_first_row, bg="#fcd116", text="WINNING\nCLAIM", image=winning_claim_image,
compound="left", fg="#231f20", font=("Roboto-Bold", 10, "bold"),
width=Dimension.label_winning_claim_width,
height=Dimension.label_winning_claim_height)
label_winning_claim.image = winning_claim_image
label_winning_claim.pack(side="left", padx=Dimension.label_winning_claim_padx)
reprint_image = PhotoImage(file=ResourcePath.resource_path('images/reprint_2.png'))
label_reprint = tk.Label(frame_first_row, bg="#8ac539", text="REPRINT", image=reprint_image,
compound="left", fg="#231f20", font=("Roboto-Bold", 10, "bold"),
width=Dimension.label_winning_claim_width, height=Dimension.label_winning_claim_height)
label_reprint.image = reprint_image
label_reprint.pack(side="left", padx=Dimension.label_winning_claim_padding)
as you can see all the widgets are placed using pack geometry manager.
You can achieve nearly the same with pack than with grid. Indeed, I usually prefer the packing, since it gives me more freedom.
For instance, take the following example:
from tkinter import Tk, Label, X, Frame, Y, LEFT, BOTH
root = Tk()
# Initialize frames
f1 = Frame(root, bg="grey")
f2 = Frame(root, bg="pink")
# Initialize labels
w1 = Label(f1, text="Red", bg="red", fg="white")
w2 = Label(f1, text="Green", bg="green", fg="white")
w3 = Label(f1, text="Blue", bg="blue", fg="white")
w1b = Label(f2, text="Red", bg="red", fg="white")
w2b = Label(f2, text="Green", bg="green", fg="white")
w3b = Label(f2, text="Blue", bg="blue", fg="white")
# Packing level 1
f1.pack(fill=X)
f2.pack(fill=BOTH, expand=True)
# Packing level 2
w1.pack(fill=X)
w2.pack(fill=X)
w3.pack(fill=X)
w1b.pack(side=LEFT, fill=BOTH, expand=True)
w2b.pack(side=LEFT, fill=BOTH, expand=True)
w3b.pack(side=LEFT, fill=BOTH, expand=True)
root.mainloop()
As you can see, by using the parameters fill and expand in the correct way, I have set the second frame to be expandable with the windows on both sides (X and Y), while the first frame is only expandable in X. All the labels inside the frames are also equally distributed.
See all the options of pack in the documentation: https://effbot.org/tkinterbook/pack.htm.
Related
Good day everyone!
I need to place four frames inside the window as shown in the picture, and using the .pack() method.
Now I have 3 frames placed in the window (picture before), and I want to add another one by moving frame f11 to the right. I also attach a picture with how it should look as a result (picture after).
Current part of code:
f11 = LabelFrame(current_tab, borderwidth=2, pady=5, relief=GROOVE, labelwidget=lbl_frm_wdgt_founder)
f11.pack(side=BOTTOM, fill=BOTH)
frame_left = Frame(current_tab, borderwidth=0, relief=GROOVE)
frame_left.pack(side=LEFT, fill=BOTH)
frame_right = LabelFrame(current_tab, labelwidget=lbl_frm_wdgt_arb, borderwidth=2, relief=GROOVE)
frame_right.pack(side=RIGHT, fill=BOTH)
frame_bottom_left = Frame(current_tab, borderwidth=2, relief=GROOVE)
# frame_bottom_left.pack(???)
If you don't want to / can't use grid(), you can use tk.Frame() widgets as generic containers
import tkinter as tk # don't use star imports!
from tkinter import ttk
top_container = tk.Frame(current_tab)
top_container.pack(expand=True, fill=tk.X, side=tk.TOP)
# top container children
frame_left = Frame(
top_container,
borderwidth=0,
relief=tk.GROOVE
)
frame_left.pack(side=tk.LEFT, fill=tk.BOTH)
frame_right = LabelFrame(
top_container,
labelwidget=lbl_frm_wdgt_arb,
borderwidth=2,
relief=tk.GROOVE
)
frame_right.pack(side=tk.RIGHT, fill=tk.BOTH)
bottom_container = tk.Frame(current_tab)
bottom_container.pack(expand=True, fill=tk.X, side=tk.BOTTOM)
# bottom container children
frame_bottom_left = tk.Frame(
bottom_container,
borderwidth=2,
relief=tk.GROOVE
)
frame_bottom_left.pack(side=tk.LEFT, fill=tk.BOTH)
f11 = ttk.LabelFrame(
bottom_container,
borderwidth=2,
pady=5,
relief=tk.GROOVE,
labelwidget=lbl_frm_wdgt_founder
)
f11.pack(side=tk.RIGHT, fill=tk.BOTH)
Since the container frames are packed separately, you can use one on top and another on the bottom
P.S.: Try to avoid * star imports - make sure to properly namespace all constants e.g. tk.LEFT and classes e.g. ttk.LabelFrame
I want to fit this frame with scrollbar(refer the provided image) in the black space present in the provided image. How do I do that. The frame should totally cover the black space.
The code for program.The image
from tkinter import *
from tkinter import ttk
C = Tk()
C.maxsize(1200, 750)
C.geometry("1200x750")
C.title("Mainscreen")
style = ttk.Style()
style.theme_use('clam')
BG = PhotoImage(file="Mainscreen bg.png")
ML = PhotoImage(file="Music_label.png")
BG_label = Label(C, image=BG, border=0)
BG_label.place(x=0, y=0)
style.configure("Vertical.TScrollbar", gripcount=0,
background="Cyan", darkcolor="gray6", lightcolor="LightGreen",
troughcolor="Turquoise4", bordercolor="gray6", arrowcolor="gray6",arrowsize=15)
wrapper1= LabelFrame(C, width="1600", height="100", background="gray6",bd=0)
mycanvas = Canvas(wrapper1,background="gray6",borderwidth=0, highlightthickness=0, width=700, height=600)
mycanvas.pack(side=LEFT, expand="false", padx=0)
yscrollbar = ttk.Scrollbar(wrapper1, orient="vertical",command=mycanvas.yview)
yscrollbar.pack(side=RIGHT, fill="y")
mycanvas.configure(yscrollcommand=yscrollbar.set)
mycanvas.bind('<Configure>',lambda e: mycanvas.configure(scrollregion=mycanvas.bbox("all")))
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window=myframe, anchor="n")
wrapper1.pack(side=RIGHT,expand="false", padx=0, pady=200)
for i in range(50):
Button(myframe, image=ML,bg="gray6",bd=0).pack()
mainloop()
EDIT:
Music_Label
Mainscreen bg
So after trying to understand the underlying problem for a while I have reached the conclusion, that the problem is with the fact that the button are being drawn in the myframe and the myframe is outside the mycanvas which contains the scrollbar. So by changing the master widget for the button from myframe to mycanvas, the problem gets fixed now the scrollbar is adjacent to the buttons. BUT, Also now the button r shifted the side since while packing the wrapper1, you did side = RIGHT, so I would also suggest that you use place here instead of pack, since pack depends on the space available and is not reliable if you are using a Background for your GUI and want the buttons within a portion of it.
I have changed the following lines -:
Button(mycanvas, image=ML,bg="gray6",bd=0).pack() # Changed to mycanvas from myframe
and
wrapper1.place(x = {YOUR X, WHERE THE BOX STARTS}, y = {YOUR Y, WHERE THE BOX STARTS}) # Use place instead..
You can use pack also, and change the padx and pady arguments but it will be tricky to get it to work always as expected.
The fixed code is this -:
from tkinter import *
from tkinter import ttk
C = Tk()
C.maxsize(1200, 750)
C.geometry("1200x750")
C.title("Mainscreen")
style = ttk.Style()
style.theme_use('clam')
BG = PhotoImage(file="Mainscreen bg.png")
ML = PhotoImage(file="Music_label.png")
BG_label = Label(C, image=BG, border=0)
BG_label.place(x=0, y=0)
style.configure("Vertical.TScrollbar", gripcount=0,
background="Cyan", darkcolor="gray6", lightcolor="LightGreen",
troughcolor="Turquoise4", bordercolor="gray6", arrowcolor="gray6",arrowsize=15)
wrapper1= LabelFrame(C, width="1600", height="100", background="gray6",bd=0)
mycanvas = Canvas(wrapper1,background="gray6",borderwidth=0, highlightthickness=0, width=700, height=600)
mycanvas.pack(side=LEFT, expand=False, padx=0)
yscrollbar = ttk.Scrollbar(wrapper1, orient="vertical",command=mycanvas.yview)
yscrollbar.pack(side=RIGHT, fill="y", expand = False)
mycanvas.configure(yscrollcommand=yscrollbar.set)
mycanvas.bind('<Configure>',lambda e: mycanvas.configure(scrollregion=mycanvas.bbox("all")))
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window=myframe, anchor="n")
wrapper1.pack(expand=True, padx=0, pady=200) # Use place instead
for i in range(50):
Button(mycanvas, image=ML,bg="gray6",bd=0).pack() # Change this to mycanvas from myframe
mainloop()
When I run the code below I do not get an error but the bottom frame doesn't appear in the window please can you tell me why and how I can make it appear (using the pack method NOT GRID please). I am using Python 3.5.0
import tkinter
from tkinter import *
root = tkinter.Tk()
root.geometry("1920x1080")
TopFrame = Frame(root, width=1920, height=200, bg= "green")
TopFrame.pack(side=TOP)
MiddleRightFrame = Frame(root, width=1120, height=730, bg="orange")
MiddleRightFrame.pack(side=RIGHT)
MiddleLeftFrame = Frame(root, width=800, height=730, bg="black")
MiddleLeftFrame.pack(side=LEFT)
BottomFrame = Frame(root, width=1920, height=150, bg="blue")
BottomFrame.pack(side=BOTTOM)
Your MiddleLeftFrame is 800 pixels wide. Your MiddleRightFrame is 1120 pixels. 1120 + 800 = 1920. You're forcing the window to be only 1920 pixels wide, so there's no room for the blue frame.
Remove this line and your frame will appear: root.geometry("1920x1080")
If your intent is for it to appear at the bottom of the window, spanning the entire width of the window, then call pack on it before you call pack on the left and right sides.
Also, I strongly recommend grouping your pack statements together. It makes the code easier to manage in my experience (and I have a lot of experience!).
import tkinter
from tkinter import *
root = tkinter.Tk()
root.geometry("1920x1080")
TopFrame = Frame(root, width=1920, height=200, bg= "green")
MiddleRightFrame = Frame(root, width=1120, height=730, bg="orange")
MiddleLeftFrame = Frame(root, width=800, height=730, bg="black")
BottomFrame = Frame(root, width=1920, height=150, bg="blue")
TopFrame.pack(side=TOP)
BottomFrame.pack(side=BOTTOM)
MiddleRightFrame.pack(side=RIGHT)
MiddleLeftFrame.pack(side=LEFT)
root.mainloop()
The reason this works is due to the packer algorithm. When you place something on the left or right, it will allocate all of the remaining vertical space on that side. Thus, after you pack something on the left and right and then later pack something on the bottom, the "bottom" is the bottom of the space between the left and right, not the bottom of the window as a whole.
Here is the canonical description of how pack works:
http://tcl.tk/man/tcl8.5/TkCmd/pack.htm#M26
I think the issue is that you are using pack sides so that in the middle there is a line of nothing. One way to get around this is to create a MiddleFrame where pack sides are used and then just pack the other frames.
import tkinter
from tkinter import *
root = tkinter.Tk()
root.geometry("1920x1080")
TopFrame = Frame(root, width=1920, height=200, bg= "green")
TopFrame.pack()
#the middle frame
MiddleFrame = Frame(root)
#pack the two middle frames into the frame created above
#the parent of the two middle frames change to become MiddleFrame instead of root
MiddleRightFrame = Frame(MiddleFrame, width=1120, height=730, bg="orange")
MiddleRightFrame.pack(side=RIGHT)
MiddleLeftFrame = Frame(MiddleFrame, width=800, height=730, bg="black")
MiddleLeftFrame.pack(side=RIGHT)
#pack the middle frame with both frames inside it
MiddleFrame.pack()
BottomFrame = Frame(root, width=1920, height=150, bg="blue")
BottomFrame.pack()
Add:
tkinter.mainloop()
so that the GUI starts waiting for events as opposed to skipping to close itself.
Additionally, pack uses a filling algorithm which calculates dynamically to fill the empty space. You shouldn't really be doing it like this but a simple call swap would suffice in this specific case. Call:
BottomFrame.pack(side=BOTTOM)
exactly after TopFrame's pack:
TopFrame.pack(side=TOP)
BottomFrame.pack(side=BOTTOM)
MiddleRightFrame.pack(side=RIGHT)
MiddleLeftFrame.pack(side=LEFT)
When I put in my code for this name picker, the gui opens and the two buttons are on top of each other. I understand why this is happening but I do not know how to move it.
This is what it looks like
This is what I want it to look like
Code:
from tkinter import *
import tkinter
import random
names =['Sledge','Thatcher','Ash','Thermite','Twitch','Montagne','Glaz','Fuze','Blitz','IQ','Buck','Blackbeard','Capitão','Hibana']
name = ["Smoke","Mute","Castle","Pulse","Doc","Rook","Kapkan","Tachanka","Jäger","Bandit","Frost","Valkyrie","Caveira","Echo"]
def pickName():
nameLabel.configure(text=random.choice(names))
def pickName1():
nameLabel.configure(text=random.choice(name))
root = tkinter.Tk()
root.title("Operator Picker")
root.geometry("400x100")
nameLabel = tkinter.Label(root, text="", font=('Helvetica', 32))
nameLabel.pack()
Grid()
pickButton1 = tkinter.Button(text="Pick Attack", command=pickName)
pickButton1.pack()
pickButton1.place(bordermode=OUTSIDE,height=100, width= 100)
pickButton2 = tkinter.Button(text="Pick Defend", command=pickName1)
pickButton2.pack()
pickButton2.place(bordermode=OUTSIDE,height=100, width= 100)
root.mainloop()
You don't need to pack and place your buttons, you can just pack them. If you want them on opposite sides, try this
pickButton1 = tkinter.Button(root, text="Pick Attack", command=pickName, height=100, width=100)
pickButton1.pack(side=tkinter.LEFT)
pickButton2 = tkinter.Button(root, text="Pick Defend", command=pickName1, height=100, width=100)
pickButton2.pack(side=tkinter.RIGHT)
Edit:
When you define the width and height of an object containing text, it is measured in lines and characters instead of pixels, if you want those exact sizes, you need to add frames
f1 = tkinter.Frame(root, height=100, width=100) #defines frame size in pixels
f1.pack(side=tkinter.LEFT) #packs on the left
f1.pack_propagate(0) #tells frame not to let children control size
pickButton1 = tkinter.Button(f1, command=pickName, text="Pick Attack")
pickButton1.pack(fill=tkinter.BOTH, expand=1) #takes up all available space
f2 = tkinter.Frame(root, height=100, width=100)
f2.pack(side=tkinter.RIGHT)
f2.pack_propagate(0)
pickButton2 = tkinter.Button(f2, command=pickName1, text="Pick Defend")
pickButton2.pack(fill=tkinter.BOTH, expand=1)
How do I align my Radiobuttons? I can add spaces to test4 but that solution doesn't seem proper. Here's what it looks like at the moment—as you can see text111111 has extra chars. I've tried using padx.
My code:
from tkinter import *
class GUI:
def __init__(self, master):
self.iconnum = IntVar()
self.master = master
self.master.title('test')
self.master.resizable(width=False, height=False)
self.master.maxsize(500, 250)
self.master.minsize(500, 250)
self.test1 = Radiobutton(master, text="test11111111", variable=self.iconnum,
value=1, )
self.test2 = Radiobutton(master, text="test2", variable=0, value=2, )
self.test3 = Radiobutton(master, text="test3", variable=0, value=3, )
self.test4 = Radiobutton(master, text="test4", variable=0, value=4)
self.test1.grid(row=2, columnspan=1)
self.test2.grid(row=2, columnspan=2)
self.test3.grid(row=2, column=1)
self.test4.grid(row=3, columnspan=1)
self.Checker = Radiobutton(master, text="test5", indicatoron=0, height=1, width=35,
value=0, command=self.icon_switcher) #var=Selection)
self.Turbo = Radiobutton(master, text="test6", indicatoron=0, height=1, width=35,
value=1, command=self.icon_switcher) #var#=Selection)
self.Checker.grid(row=1)
self.Turbo.grid(row=1, column=1, )
def icon_switcher(self):
print("Hello")
root = Tk()
gui = GUI(root)
root.mainloop()
You should use the sticky keyword argument in order to align your widgets better when using grid.
import Tkinter as tk
window = tk.Tk()
radio = tk.Radiobutton(window, text="I'm a radio")
radio.grid(column=0, row=0, sticky=tk.N+tk.S+tk.W+tk.E)
window.mainloop()
You can use any combination of the N, S, W and E members of the Tkinter module. This parameter will make your widgets stick to the sides of the cell you have specified, somewhat like justification in text. If your widget is resizable, such as with Button, the widget will also automatically resize to fit the cell if you use all of the N, S, W and E members.
Important to note is that this can only do so much as to make the widgets stick to the edges of the cell. Sometimes it is necessary to actually resize the cell or move your widget to another cell.
In your example image, you have Buttons with a set size that is larger than the default size (the example code you provide is incomplete). This causes the cell, and the whole columns that the cells of these Buttons are in, to become wider. In this case, you might want to use the columnspan keyword argument to divide your column into smaller, resizable, parts, so that your Radiobuttons can be aligned still better.
import Tkinter as tk
window = tk.Tk()
radio_one = tk.Radiobutton(window, text="I'm a radio")
radio_two = tk.Radiobutton(window, text="I'm another radio")
button = tk.Button(window, text="I am a very long button", width=50)
button.grid(row=0, column=0, columnspan=2, sticky=tk.N+tk.S+tk.W+tk.E)
radio_one.grid(column=0, row=1, sticky=tk.N+tk.W)
radio_two.grid(column=1, row=1, sticky=tk.N+tk.W)
window.mainloop()
If you would like more information on what parameters the grid geometry manager can use, I suggest you read this tutorial, I have found it to be very helpful in the past.
As a sidenote, please note that you use the variable keyword argument in the declaration of your Radiobuttons incorrectly. You must pass either a tk.StringVar, tk.IntVar or some other comparable object, as described here.
You can use sticky=W inside your .grid for all your buttons to make them align on the left side. And you can also include pady = 20 to make it not go all the way to the left.