Tkinter frame has dimensions different from dimensions given to and idk why - python

I'm struggling with Tkinter now. I wanted to create layout but if I define window dimensions (800x600) and create frame which have to be wide 800 too, it have just half.
I tried googling and changing code, if I multiply width 2 times (to 1600) then frame fit the screen perfetly.
Here is code:
import tkinter as tk
SW, SH = 800, 600
win = tk.Tk()
win.geometry(f"{SW}x{SH}")
frm_appname = tk.Frame(
master = win,
bg = 'red'
)
frm_appname.place(
anchor = tk.N,
width = 800,
relheight = (1/6)
)
Here is output:
Can anyone explain me what happened here?

The anchor of "n" (tk.N) means that the top center portion of the frame is at the given coordinate. Since you didn't provide an x and y coordinate for place it defaults to 0,0. So, the top/middle (400,0) of the frame is at coordinate 0,0.
If you set the anchor to "n" or tk.NW, the top-left corner of your frame will be in the top-left corner of the window.
On an unrelated note, experience has taught me that layouts are nearly always easier to create with pack and/or grid. In my coupe of decades of using tk and tkinter, I've never used place for more than one or two special-case widgets.

Related

How to show only a portion of the tkinter canvas by cropping the tkinter window?

I want to be able to zoom into my tkinter canvas. My tkinter canvas is 500x500px, and I only want my window to display the center 200x200px portion of this canvas. How do I do this? I know that I can just specify my window size as 200x200px using root.geometry("200x200+0+0"), but this causes my window to display the top left corner of my canvas, and not the center. Before I do anything, my entire canvas looks like this:
Ultimately, I want my window to look like this, with the canvas centered within the window:
This is my code:
import tkinter
root = tkinter.Tk()
root.title("")
root.geometry("200x200+0+0")
canvas = tkinter.Canvas(master = root, width = 500, height = 500)
canvas.create_oval(200, 200, 300, 300, outline = "black", fill = "blue")
canvas.pack()
which returns:
As you can see, the canvas is not centered, and the window is showing the upper left hand corner at the moment. Does anyone have any suggestions?
Ok, thanks to this stackoverflow post, I found out there is an option when creating a tkinter canvas called scrollregion. The format of the argument is "x0 y0 x1 y1" for anyone that is wondering, where (x0, y0) is the upper-left corner of the area of the canvas I want to show and (x1, y1) is the bottom-right corner of the same area. My code should be fixed to this:
canvas = tkinter.Canvas(master = root, width = 500, height = 500, scrollregion = "150 150 350 350")
Be wary that these coordinates do not account for a scrollbar...I'm still working on figuring that out. Much thanks to this stackoverflow post as well, specifically the following words:
I don't see any difference between putting the y-scrollbar to the bottom or putting the canvas view to the bottom because the two are linked.

Why does this label go off screen when anchored?

I want to anchor this label but for some reason, it keeps going off the screen in my computer, i don't know if its just me but here's my code:
import tkinter as tk
root = tk.Tk()
root.attributes("-fullscreen", True)
label_frame = tk.Frame(root)
answer_label = tk.Label(label_frame, text="text")
answer_label.grid(row = 0, column = 0)
label_frame.place(anchor = "n")
root.mainloop()
Why does this label go off screen when anchored?
Consider this line of code:
label_frame.place(anchor = "n")
The anchor option tells tkinter what part of the frame is to be placed at the given coordinates. n means "north", or the top-center of the frame. So, whatever coordinates are given, the top-center portion of the frame will be at those coordinates.
Since you did not give any coordinates, they default to x=0 and y=0. Thus, the top-middle portion of the frame will be placed at 0,0. That means that the left half of the frame will be to the left of coordinate 0,0.
If you insist on using place, and if you want the frame centered, you can give a relx of .5, meaning that the anchor position (n) will be placed half-way across the width of the containing widget.
label_frame.place(relx=.5, y=0, anchor = "n")
try
label_frame.place(anchor = "nw")
Tested this and it seems to put the label in the right place.

Python Tkinter how to adjust the x,y default 0,0 coordinates

I've been learning some basic tkinter and I've come across simple code to centre the window in the middle of your monitor, except when I run it, it's off horizontally. It's pedantic but it bothers me a lot.
The code I used
# Imports
from tkinter import *
# tkinter Application
root = Tk()
#Root Geometry
root_Width = 600
root_Length = 600
# Coordinates of top left pixel of application for centred
x_left = int(root.winfo_screenwidth()/2-root_Width/2)
y_top = int(root.winfo_screenheight()/2-root_Length/2)
root_Pos = "+" + str(x_left) + "+" + str(y_top)
# Window size and position
root.geometry(str(root_Width) + "x" + str(root_Length) + root_Pos)
root.mainloop()
Even if I go basic, and just try to open a window the size of my monitor (1920x1080) at 0,0, it's misaligned horizontally by 8px.
from tkinter import *
root = Tk()
root.geometry("1920x1080+0+0")
root.mainloop()
I took a screenshot of the result:
I have a dual monitor set up so I've added a red line where the right monitor starts. I don't know what the issue is or how to fix it. If I change it to,
root.geometry("1920x1080+-8+0")
It opens where it should, but I want to fix this in general preferably. I want 0,0 to be the top left pixel of the monitor. I acknowledge the issue may not be with python, but any advice would be helpful.
Ok, there are two thing that you are missing. The first, in your first example, is that you need to assure tkinter is getting the right values, and to do this you have to use update_idletasks() method before any winfo.
The second thing, which explains why you have to use -8 to center the full screen window, is that tkinter widows have outer-frames. You can determine the size of this frame by checking for the top left coordinate of the window (winfo_rootx()) and of the outer-frame (winfo_x()). Now the frame width is the difference between both: frame_width = root.winfo_rootx() - root.winfo_x() while the real window width is real_width = root.winfo_width() + (2*frame_width), since you have to consider the frame in both sides.
In summary, to center the window horizontally you do:
width = root.winfo_width()
frame_width = root.winfo_rootx() - root.winfo_x()
real_width = root.winfo_width() + (2*frame_width)
x = root.winfo_screenwidth() // 2 - real_width // 2
(here you can print the frame width and you will see it is 8)
and then use geometry method to place the window at position x.
Off course you can do the same for the vertical alignment

Tkinter - relation between font type and width

I apologize in advance if my question is a duplicate however I have not found an answer to this question.
I'm learning Tkinter and I'm struggling with understanding the relation between a label's font type, it's size and it's width and the length of the string in it.
Specifically, what my problem is:
I have created a widget: a 800x640 canvas on which I want to place other
widgets.
On this canvas I want to place a label with some text which has the following
attributes: font: Helvetica, font size: 20, text = "main application". I want
to place this label widget at the very most top left corner of the
widget(meaning at point 0,0 with respect to the canvas). I want the label to
be 200 in width meaning it's background to take almost 1/3 of the canvas's
size(after I manage to do this I plan to add 2 more labels as well). I guess
the height of the label is determined by the font size in this case 20. I
placed the label at coordinate y=20 and this coordinate seems to be ok.
I did some googling and found out that the width parameter of the label widget is not the actual width but something related to the font and size of the label's text: something like if I understood correctly: if the width is 6 than the label will be wide enough to contain 6 characters of, in my case verdana size 20. But I was not able to figure out what width and what x coordinate I should give my label so it starts at the x point of the canvas. Here is the code that I wrote:
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
from tkinter import Menu
# Define the application class where we will implement our widgets
class Application(Frame):
def __init__(self, master):
super(Application, self).__init__(master)
# CANVAS COLOUR DEFAULTS TO THE COLOUR OF THE WORKING WINDOW
canvas = Canvas(master, width=800, height = 640, bg="gray") # IF YOU DO .PACK() HERE IT WILL RETURN NONE AND THEN YOU WILL HAVE PROBLEMS BECAUSE .PACK() RETURNS A 'NONE' TYPE OBJECT
canvas.place(relx=0.5, rely=0.5, anchor=CENTER)
# The 'menu' of the application. The selection labels
main_application_label = Label(master, text="main_application", font=("Helvetica", 20))
main_application_window = canvas.create_window(103,20, window=main_application_label)
main_application = Tk()
main_application.title("main_application")
app = Application(main_application)
app_width = 800
app_height = 640
screen_width = main_application.winfo_screenwidth()
screen_height = main_application.winfo_screenheight()
x_coord = (screen_width/2) - (app_width/2)
y_coord = (screen_height/2) - (app_height/2)
main_application.geometry("%dx%d+%d+%d" % (app_width, app_height, x_coord, y_coord))
main_application.mainloop()
I have managed to somehow get the label at around point 0,0(by giving more values till I got it right) but the actual width of the label is not 200 pixels(~1/3 of the canvas). Please help me understand what values to the width parameter I should give so that my label's background is 1/3 of the canvas's size and if possible explain the relation between character font and width parameter so I can do that for any widgets regardless of their text's length. Thank you for reading my post!
Edit: What I wanted to do was to place 3 widgets(labels in this case but it doesn't matter) on the canvas. I did not understand what the 'anchor' option does and that was confusing me because I was expecting the center of the widget to be placed at the given coordinates all times but as I was changing anchor the placement of the center of the widget was changing and that was confusing me. It's all clear now thanks to #Bryan Oakley. Thanks.
If you want the upper left corner of the text to be at (0,0), you don't have to adjust the coordinates based on the width. You can use the anchor option when creating the canvas object:
main_application_window = canvas.create_window(0, 0, anchor="nw",
window=main_application_label)
If you really need to compute the actual size of the string, you can create a Font object and then use the measure method to find the actual width of a string in the given font.
from tkinter.font import Font
font = Font(family="Helvetica", size=20)
string_width = font.measure("main_application")
string_height = font.metrics("linespace")
This gives you the size of the rendered string. If you're using a label widget you'll also need to take into account the amount of padding and borders that the widget uses.
When you create items on a canvas, you can specify the width and height. For example, this makes the widget 200 pixels wide:
main_application_window = canvas.create_window(0, 0, anchor="nw", width=200,
window=main_application_label, width=400)

change size of Frame even if there widget python

hi is there any way to change width and height of widget even if there's widget?
i have code like this
form = Tk()
form.geometry("500x500")
def click():
global frame
frame.config(height = 0 ,width = 0)
frame = LabelFrame(form , text = "vaaja")
frame.place(x = 20 , y = 30)
Label(frame, text ="1").grid(row = 0,column = 0 )
Label(frame, text = "2").grid(row = 1 ,column = 0 )
Button(form , text="Click", command = click).place(x = 200 , y = 200)
form.mainloop()
and when I click the button the size of the frame is the same ( I'cant use grid_forget() for labels and then change the size of frame)
Because you are using place, you have two solutions: you can use place to set the width and height to zero, or you can turn geometry propagation off.
Using place to set the width and height
place allows you to define the width and the height of the placed widget, so in your click function you can do this:
def click():
frame.place_configure(width=0, height=0)
Turning geometry propagation off
A frame is resized to fit its contents by something called "geometry propagation". If you turn this off, you can control the size of the frame with the width and height options of the frame itself. Usually it's better to let Tkinter decide the size for you, but sometimes there's a need to have an explicit size, which is why it's possible to turn geometry propagation off.
Since you are using grid to manage the widgets internal to the frame, you need to use grid_propagate(False) to turn geometry propagation off for that frame:
frame.grid_propagate(False)
By doing so, you're responsible for setting the initial width and height of the widget, though you could leave propagation on to get the initial size, then turn it off with the button click in order to work around that issue.
There's an interesting bug (or feature...) in that if you set the width and height to zero, Tkinter won't redraw the window. At least, it doesn't on the Mac. I don't recall the workaround for that because I never, ever need to set a widget to a zero size, but setting it to 1x1 pixel makes it nearly invisible.

Categories

Resources