I wrote a short piece of code in tkinter in python to see if I could make a frame appear in my window. Here is the code below:
from tkinter import *
root = Tk()
root.title("Window")
root.state("zoomed")
root.config(bg="white")
winHeight = int(root.winfo_height())
winWidth = int(root.winfo_width())
controlFrame = Frame(root, bg="red")
controlFrame.pack()
root.mainloop()
I created one full-sized window with a background colour of white. The frame inside it is supposed to be red. However, when I run this code, I do not see any red. I am sure I packed it and everything.
I'd love to help you out on this one...
There's just a slight detail that you might not notice right now but the frame, in fact, is present in the window, but it's too small to see. By this I mean you haven't specified the height and width of the frame that you have placed in the window. Here's the fixed version:
from tkinter import *
root = Tk()
root.title("Window")
root.state("zoomed")
root.config(bg="white")
winHeight = int(root.winfo_height())
winWidth = int(root.winfo_width())
controlFrame = Frame(root, bg="red", height = 700, width = 700)
controlFrame.pack()
root.mainloop()
What this will do is just set the height and width of the frame to 700px, so you will get a square frame of red colour.
I hope this answer was satisfactory.
The answer is pretty simple, you don't have any other widget in your frame, it's empty for now, so its size is 0 pixel (or 1, I don't remember). That's why you don't see it in your window.
Related
I'm trying to set a Tkinter canvas to red/green for one second, then back to white afterward. However, despite the fact that the code setting the canvas to red/green precedes the code reverting back to white, the window doesn't reflect the initial color change. I understand that by calling .after, the program freezes until the specified duration is over, but I don't understand why it doesn't change to red or green before freezing.
if is_correct:
self.canvas.config(bg="green")
else:
self.canvas.config(bg="red")
self.window.after(1000, self.canvas.config(bg="white"))
Refer to this simple program.
from tkinter import *
root=Tk()
def change_bg():
canvas.config(bg="red")
root.after(1000,lambda: canvas.config(bg="white"))
canvas=Canvas(root,bg="white")
canvas.pack()
root.after(1000,change_bg)
root.mainloop()
from tkinter import *
import time
def change_color():
can.config(bg="red")
can.update()
change_color2()
def change_color2():
time.sleep(1)
can.config(bg="white")
root = Tk()
root.geometry("500x500")
can = Canvas(root, bg="white", height=450, width=500)
can.pack()
Button(root, text="Change color for 1 sec", command=change_color).pack()
root.mainloop()
You can refer to this code
I want to center a tkinter window on the screen, which can be done with:
root.geometry(f"+{(root.winfo_screenwidth()-root.winfo_width())//2}+"
f"{(root.winfo_screenheight()-root.winfo_height())//2}")
This is using the screen width and the width of the window to calculate the upper left corner. However, in order to find out the window width, I have to run root.update() as shown in the following example, which leads to the window showing up at a wrong position for a tiny moment.
import tkinter as tk
root = tk.Tk()
for i in range(20):
tk.Label(root, text='Hello World! '*5).pack()
# without the following line, the window dimensions are not being calculated
root.update()
root.geometry(f"+{(root.winfo_screenwidth()-root.winfo_width())//2}"
f"+{(root.winfo_screenheight()-root.winfo_height())//2}")
root.mainloop()
To avoid this, I can think of two solutions:
defining the window size in pixels, which means that the window size does not adjust automatically anymore, and
doing something like root.update() without the window being visible.
I don't know how to avoid the call to update(), but you could initially make the window completely transparent, which would prevent it from even momentarily showing up in the wrong position — thereby granting you the opportunity to position it properly and manually making it visible.
import tkinter as tk
root = tk.Tk()
# This changes the alpha value (how transparent the window should be).
# It ranges from 0.0 (completely transparent) to 1.0 (completely opaque).
root.attributes("-alpha", 0.0)
for i in range(20):
tk.Label(root, text='Hello World! '*5).pack()
tk.Button(root, text='Quit', command=root.quit).pack()
root.update() # Allow the window's size to be calculated,
# Move it so it's centered on the screen.
root.geometry(f"+{(root.winfo_screenwidth()-root.winfo_width())//2}"
f"+{(root.winfo_screenheight()-root.winfo_height())//2}")
# Now make it visible.
root.attributes("-alpha", 1.0)
root.mainloop()
The simplest way to achieve a clean window display is shown in the following code.
withdraw the master immediately, create widgets, update master before centering window and finally deiconify.
Works every time.
import tkinter as tk
def screencenter(o):
w, h = o.winfo_width(), o.winfo_height()
x = int((o.winfo_screenwidth() - w) / 2)
y = int((o.winfo_screenheight() - h) / 2)
o.geometry(f'{w}x{h}+{x}+{y}')
master = tk.Tk()
master.withdraw()
# create whatever widgets you need
master.update()
screencenter(master)
master.deiconify()
master.mainloop()
What you're looking for is root.withdraw() and root.deiconify().
The former will hide your window from view and the latter will show it.
I've included a full example below.
from tkinter import Tk
def show_it():
height = root.winfo_height()
width = root.winfo_width()
root.geometry(f"+{(s_width - width)//2}+"
f"{(s_height - height)//2}")
# show it again
root.deiconify()
root = Tk()
s_height = root.winfo_screenheight()
s_width = root.winfo_screenwidth()
root.withdraw()
# hide the window
root.after(200, show_it)
root.mainloop()
From this I found how to resize a button, so I tried to execute this code.
from tkinter import *
selection_window = Tk()
selection_window.wm_title("")
selection_window.geometry('{}x{}'.format(200, 150))
frame_1 = Frame(selection_window, width=200, height=30)
Button(frame_1, text="Single",height = 100).pack(side=LEFT,anchor=S)
Button(frame_1,text="Range",command=Toplevel,height = 20).pack(side=RIGHT,anchor=S)
frame_1.pack()
selection_window.mainloop()
But the size of button is not changed, rather, the buttons went to the center of the window. Can someone please tell me why is the problem?
Button Height:
If you notice, the height of frame_1 is 30 and the height of the buttons are 100 and 20. One button height is significantly taller than frame_1. So if you maximise your tk window, you will see the height difference of the buttons. Alternatively, try setting one button height to 10 and the other to 2, and rerun your script, to see the height difference. Conclusion, the button heights can be changed.
Button Lateral Placement:
The lateral placement of the buttons can be controlled by using the padx=[x_left, x_right] option of the pack system. x_left and x_right denotes the horizontal external padding to be left on each side of the button in relations to it's parent. Your can read Tk documentation for a clearer explanation on the Packer's algorithm.
from tkinter import *
selection_window = Tk()
selection_window.wm_title("")
selection_window.geometry('{}x{}'.format(200, 150))
frame_1 = Frame(selection_window, width=200, height=30)
frame_1.pack()
Button(frame_1, text="Single",height = 10).pack(side=LEFT, anchor=S, padx=[0,40])
Button(frame_1,text="Range",command=Toplevel,height = 2).pack(side=RIGHT, anchor=S, padx=[20,0])
selection_window.mainloop()
Height: Placement:
Part 2:
Per comments below, please run below script to see if changing a ttk.Button height is even possible for OSX using 'non-default' style themes and post your finding in the comment section. It worked on my Ubuntu.
from tkinter import *
import tkinter.ttk as ttk
s=ttk.Style()
print('Style themes on my system are ', s.theme_names())
s.theme_use('clam')
s.configure('bb.TButton', background='white', padding=50)
b1=ttk.Button(text='Default')
b1.pack(side=LEFT, anchor=S, padx=[0,40])
b2=ttk.Button(text='Custom', style='bb.TButton')
b2.pack(side=RIGHT, anchor=S, padx=[20,0])
padding=1
padding=40
I'm trying to set the frame size via theme_settings and it just doesn't work.
from Tkinter import *
from ttk import *
root = Tk()
style = Style()
style.theme_settings('default',{'TFrame':{'configure':{'width':100, 'height':100}}})
frame = Frame(root)
frame.pack()
root.mainloop()
But if I set it explicitly, then it works:
frame.configure(width=100, height=100)
Why?
ttk.version = "0.3.1"
Tkinter.version = "$Revision: 81008 $"
P.S. I need to set this size via .theme_settings() method, the question exactly about it.
UPD: I've checked the same behavior with Button element and it works. Something is wrong with frames...
style = Style()
style.theme_settings('default',{'TButton':{'configure':{'width':100}}})
button = Button(root)
button.pack()
UPD2: The same story with 'padding'. It works with buttons, but not with frames. While frames 'background' for example can be set via theme_settings
The Answer should be like that:
root = tk.Tk()
width x height + x_offset + y_offset
root.geometry("500x300+250+100")
Use the Place management as following codes:
root = tk.Tk()
root.geometry("500x300+250+100") # width x height + x_offset + y_offset
root = Tk()
descriptionFrame = Frame(root)
definitionFrame = LabelFrame(descriptionFrame, text="Definition")
definitionScroll = Scrollbar(definitionFrame)
definitionCanvas = Canvas(definitionFrame, width=30, height=4, yscrollcommand=definitionScroll.set)
definitionScroll.config(command=definitionCanvas.yview)
definitionLabel = Label(definitionCanvas, text="n/a")
descriptionFrame.pack()
definitionFrame.pack()
definitionScroll.pack(side=RIGHT, fill=Y)
definitionCanvas.pack(side=LEFT, fill=BOTH, expand=True)
definitionLabel.pack(fill=BOTH, expand=True)
root.mainloop()
I have this code. The Canvas is set to have a width of 30 and height of 4, but when I run this, it ignores the width and height of the Canvas and the resulting window is sized around the Label instead. I've tried using pack_propagate(False) on every single Frame in the code, but it doesn't affect anything for the definitionFrame, but when I use it on descriptionFrame it results in an empty window. How would I create a GUI where all the frames and the window are sized to the Canvas size of width 30 and height 4?
Thanks.
To answer your specific question of how to stop a frame (or any container widget) from resizing to fit its contents, you call either pack_propagate(False) or grid_propagate(False) depending on which geometry manager you are using. If you've tried that and it wasn't working, you did it wrong. Since you didn't post that code we can't diagnose what went wrong.
When you call pack_propagate(False) you have to make sure that widget has an appropriate size. Labels and buttons will have a default size to fit their text, but a frame will have a default size of 1x1, making the contents nearly invisible. If using this on a frame, then, make sure to give it an explicit width and height.
only Listbox, Text, Canvas, and Entry are scrollable by default; Canvas could work, but is a bit overkill IMO, so here's something that seems like what you want using Text
#!/usr/bin/python
from Tkinter import *
root = Tk()
descriptionFrame = Frame(root)
definitionFrame = LabelFrame(descriptionFrame, text="Definition")
definitionScroll = Scrollbar(definitionFrame)
definitionText = Text(definitionFrame, width=30, height=4, yscrollcommand=definitionScroll.set)
definitionScroll.config(command=definitionText.yview)
definitionText.delete("1.0", END) # an example of how to delete all current text
definitionText.insert("1.0", "n/a") # an example of how to add new text to the text area
descriptionFrame.pack(fill=BOTH, expand=True)
definitionFrame.pack(fill=BOTH, expand=True)
definitionScroll.pack(side=RIGHT, fill=Y)
definitionText.pack(side=LEFT, fill=BOTH, expand=True)
root.mainloop()
What I did was set make my root un-resizable: root.resizable(False, False), then I defined width and height of the canvas: Canvas(root, height=500, width=1500)
and finally placed my frame with a relative width and height of 1: frame.place(relheight=1, relwidth=1). Then I placed all my widgets with specific y/x values and pack(), but you could do the same process if you want everything sized to the canvas; I feel like this is a simpler method.