Python Tkinter button width and heigth - python

I'm new to tkinter, trying to create a square botton but I can't.
from tkinter import *
root = Tk()
button1 = Button(text = "Cuadrado", height = 10, width = 10).pack()
root.mainloop()
The widht and the height are the same valor, but the button that create with the code is a rectangle.
Why does it happen?

The width and height of a button that has text but no image are in the number of characters (ie: height=10 means it should be 10 characters tall).
The average character in your font is almost certainly not a square -- fonts are typically taller than they are wide. Thus, 10 characters wide will likely be fewer pixels than 10 characters tall.

The height of the characters is about 2.3 times greater than the width. So, to draw a square button, you can use proportions like this:
Button(width=2, height=1)
Button(width=9, height=4)
Button(width=19, height=8)
Button(width=37, height=16)

Related

Adjusting text in tkinter Label to occupy all available space

Hy, I hope you are all doing well. I am building a Eye chart software in python tkinter which consists of Opto Charts.
In Opto Charts all alphabet comes directly below each other. But when I try to add labels in tkinter, it forms V shape as with each row font size is decreasing.
I want to occupy the label all the available space.
I managed to do that using mainFrame.rowconfigure(0, weight=1) but it only makes the label to full width not the text inside it. I have attached a screenshot for how it looks.
In the screenshot you can see the labels are set to full length of screen but text is in V shape as font size is decreasing from top to bottom.
Is there a way to anchor the text to full width also. In others words each alphabet should come directly below the above one.
I hope I was clear, If you need to know anything else let me know.
If an image or bitmap is being displayed in the label then the value is in screen units; for text it is in characters.
To occupy same space for different size of font, try to use image mode and use an empty image.
import tkinter as tk
from tkinter.font import Font
texts = ('EDFHT', 'FPYUI', 'TOZQW', 'LPEDA', 'PECFD')
sizes = (28, 24, 20, 16, 12)
root = tk.Tk()
factor = 2
tkfont = Font(font=("Courier New", max(sizes), 'bold'))
width, height = tkfont.measure("W")*factor, tkfont.metrics("linespace")*factor
image = tk.PhotoImage(data='')
for row, (text, size) in enumerate(zip(texts, sizes)):
for column, t in enumerate(text):
label = tk.Label(root, text=t, font=("Courier New", size, 'bold'), image=image, width=width, height=height, compound=tk.CENTER)
label.grid(row=row, column=column)
root.mainloop()

Why is this canvas one pixel too large?

MCVE
import Tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=100, height=100)
canvas.grid(row=0, column=0)
def print_click(event):
print event.x, event.y
canvas.bind('<Button-1>', print_click)
root.mainloop()
Issue
Clicking the canvas on the very top left prints (0, 0).
Clicking the canvas on the very bottom right prints (100, 100). I expected (99, 99).
This means the canvas is actually 101 pixels wide and high, not 100.
In my real program, I am showing an array (as an image) on the canvas and need the precise click position. If that position does not exist in the underlying image (i.e. (100, 100) for an 100x100 array), the program will crash.
Questions
Am I doing something wrong creating the canvas? Why is it one wider and higher than expected?
Is the simple fix here to just subtract 1 from width and height whenever setting up a canvas that needs to have width width and height height?
There are other things that contribute to the overall width and height of a widget besides just the width and height attributes. For example, both borderwidth and highlightthickness contribute to the overall size of the widget. Since you aren't setting those to zero, you're relying on the defaults for your platform, and those defaults apparently aren't zero.
You need to explicitly set those attributes to zero:
canvas = tk.Canvas(root, width=100, height=100, highlightthickness=0, borderwidth=0)

Why does winfo_width() return a larger size of button widget than the size of what it exactly looks like?

I want buttons/labels in the same column will share the same width with a specific/first button/label. The problem is winfo_width() seems not to return what I want. The return value of winfo_width() is multiple times the button.
I don't want to make width fixed by a number I select. Thus, I did not find a solution to my problem.
Here is part of my code:
button_1.update_idletasks()
print(button_1.winfo_width())
new_label = Label(frame_1, bg= "#8432C7", width = 30, height = 5)
new_label.grid(row = 2, column = 0)
Since I don't have 10 reputation to post images, here is the link for the generated interface:
If you could see the above image, you should find the lower label (width = 30) is larger than the upper button (width = 157 ?).
But, according to my attempts, 157 seems not to be the width of button_1. I feel confused about what exactly winfo_width() returns here. Thus, I want to know what winfo_width() returns (why winfo_width() return 157 which should be a smaller number than 30) and how to get the exact width of the button.
I am stuck here for an hour since I just started to learn Tkinter recently.
Thanks in advance for anyone who can give me suggestions.
Fun fact: you don't have to bother with this at all.
Just pass sticky when you grid your widgets:
import tkinter as tk
root = tk.Tk()
button_1 = tk.Button(root,text="button_1")
button_1.grid(row = 1, column = 0, sticky="ew")
new_label = tk.Label(root, bg= "#8432C7", height = 5)
new_label.grid(row = 2, column = 0, sticky="ew")
root.mainloop()
Then your columns will be auto-fit and scaled to the same size.
This is because the width used in the arguments to create a button are in different units than what tkinter uses.
From the Documentation of a Button in tkinter. Width is The width of the button. If the button displays text, the size is given in text units. If the button displays an image, the size is given in pixels (or screen units). If the size is omitted, or zero, it is calculated based on the button contents. (width/Width)
You will find that if you use tkinters .place() to set a size, the size that .winfo_width() returns will be the same.
For example:
button_1.update_idletasks()
print(button_1.winfo_width())
new_label = Label(frame_1, bg= "#8432C7")
new_label.place(x=40, y=0, width=157, height=20)
You will find that the new_label will now have the same width as the button

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)

Set specific width for Python label as this many pixels long?

I am making a Python program, and I am looking for ways to make a nice centre-aligned title at the top of inside my program. However, I am having trouble doing this because with the current tools I have, I would have to experiment my label spacing length to match how many characters I have for my title. Is there a way to make a Python label be certain amount of pixels long regardless of length of the text? I am specifically looking for a way of specifically making a label be a specific number of pixels long so I don't have to experiment with all these different lengths.
I tried using the "padx" and "pady" parameters, but I have found that the lengths will always change depending on the length of the text of the label itself. I also tried the "width" parameter, but it only extends by a number of textlengths long.
from tkinter import *
root = Tk()
root.geometry("960x600")
label_toptitle = Label(root,
text="Program Name",
padx=40,
pady=40,
font=(None, 40),
#height=2,
#width=30
)
root.mainloop()
This is my current code right here, but if I change the name "Program Name" to another name, the padx and pady and/or width or height functions will become not the same amount of pixels long. I need a way to make the label a fixed amount of pixels long.
Your question title mentions setting the length of a label, but it seems your real goal is to center a label without having to calculate margins.
The best solution for that is to not use padding or margins to center the widget. Just tell tkinter you want things centered when you use grid or pack, which is actually the default. You don't have to do much at all.
For example, the following will give a window with a centered title:
import tkinter as tk
root = tk.Tk()
root.geometry("960x600")
label_toptitle = tk.Label(root,
text="Program Name",
font=(None, 40),
)
label_toptitle.pack(side="top", fill="x")
root.mainloop()
Grid can center things too, though the exact syntax depends on what other rows and columns you have. Without knowing that it's hard to give a useful example.
If you really need to specify an exact width in pixels for a label, you can add a transparent one-pixel image to the label and hide it behind the text. When you add an image, the width attribute represents a width in pixels rather than characters.
Example:
pixel = tk.PhotoImage(width=1, height=1)
label = tk.Label(root, image=pixel, text="Hello", compound="center", width=100)
You could make a variable that declares the max size of a label and then use it to fix the width/height issues on all label declarations as follows:
maxSizeX = 100
maxSizeY = 100
labelTitleText = "Program Name"
labelTitleTextSize = len(labelTitleText)
labelTitle = Label(root,
text=labelTitleText,
width=(maxSizeX-labelTitleTextSize),
height=maxSizeY,
font=(None, 40)
)
Although I'm unsure as to how you would get character pixel width on the display if you could then you could change the above to:
maxSizeX = 100
maxSizeY = 100
labelTitleText = "Program Name"
labelTitleTextSize = len(labelTitleText) * pixelWidth
labelTitle = Label(root,
text=labelTitleText,
padx=(maxSizeX-labelTitleTextSize),
pady=maxSizeY,
font=(None, 40)
)

Categories

Resources