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)
)
Related
I am trying to make a website in Python and I have 5 frames to separate what I want.
By themselves the website looks like this:
and it has this code:
from tkinter import *
root = Tk()
root.geometry(str(root.winfo_screenwidth()) + "x" + str(root.winfo_screenheight()) + "+" + "0" + "+" + "0")
top = Frame(root, bg="lightGreen").grid(row=0, column=0, columnspan=3, stick=NSEW)
title = Label(top, text="website", bg="lightGreen", font=("Courier", 50, "bold"))
title.place(x=0, y=0)
left = Frame(root, bg="lightBlue").grid(row=1, column=0, stick=NSEW)
center = Frame(root, bg="pink", height=50)
center.grid(row=1, column=1, stick=NSEW)
right = Frame(root, bg="yellow", height=50).grid(row=1, column=2, stick=NSEW)
bottom = Frame(root, bg="silver").grid(row=2, column=0, columnspan=3, stick=NSEW)
# configurations and mainloop and binding
root.columnconfigure(0, weight=2)
root.columnconfigure(1, weight=5)
root.columnconfigure(2, weight=2)
root.rowconfigure([0, 2], weight=1)
root.rowconfigure(1, weight=5)
root.mainloop()
Then when I add stuff into the pink Frame, it looks like this:
The code is the same except with this small piece of coded added with it:
Label(center, text="Welcome", bg="pink", font=("Courier", 50, "bold")).pack(side=TOP, anchor=W)
If you look closely, the blue and yellow frames are smaller and the pink is bigger when the text is added. Why does this happen and is there any way to solve this problem?
If you look closely, the blue and yellow frames are smaller and the pink is bigger when the text is added. Why does this happen and is there any way to solve this problem?
When you use weight, it does not define the ratio of all space to give to each column. It defines how much of the extra space is given to each column. Since none of your widgets have a defined size, they all expand according to the rules you've set forth so it appears that the weight defines the actual width.
When you add a widget to the center section, that label requires a certain amount of space, and thus the center frame now requires a certain amount of space. That means that there is less extra space to be allocated, and this extra space will be added to the space already being used by the label.
When you start out, none of the frames have a width. Let's say we end up setting the width of the window to 900 pixels. Because of the weights, this 900 pixels is split up into 200 pixels on the left, 500 in the middle, and 200 on the right, like you see in your first picture.
Now, let's say that the middle label is added and it takes up 100 pixels. That means there are now 800 extra pixels that have to be distributed, instead of 900. The left and right columns each get 2/9ths of 800 or about 178 pixels. The center section gets 5/9th of that extra space or about 444 pixels. These 444 pixels are added to the existing 100 pixels required by the middle, resulting in a middle section that gets 544 pixels. Thus, the side columns are narrower and the middle column is wider than when there was nothing in the center.
As to the fix, it's hard to say. The fix for this specific case where you have nothing in the left and right is going to be different than the case where you have widgets in the left and right.
As a general rule, don't depend on the weight to define the actual widths, because that's not what it is for. Design your GUI by giving minimum widths to the widgets inside the columns and letting tkinter compute the actual widths. Then, the weight can be used to decide how to distribute extra space when the window grows. Alternately, you can use the minsize attribute of the columnconfigure command to force the left and right sizes to have a minimum width.
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
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)
import tkinter as tk
root = tk.Tk()
root.title("window")
yellow_header = tk.Label(root, text = 'Header', bg = 'light yellow')
yellow_header.pack(side = tk.TOP, anchor = tk.N, expand = 1, fill = tk.X)
yellow_header2 = tk.Label(root, text = 'paragraph', bg = 'light yellow')
yellow_header2.pack(side = tk.TOP, anchor = tk.N, expand = 1, fill = tk.X)
root.mainloop()
For the above code I am trying to have both these labels anchored to the top and directly below one another. Although the first label (yellow_header) anchors to the top, where as the second label (yellow_header2) when expanded move towards the centre. How can I fix this?
Thank you in advance!
Don't use expand=1. From effbot:
The expand option tells the manager to assign additional space to the widget box. If the parent widget is made larger than necessary to hold all packed widgets, any exceeding space will be distributed among all widgets that have the expand option set to a non-zero value.
With expand=1, when you make the window larger, the space is distributed between the two labels. So even though you only tell them to fill it in the X direction, they are given the space in both directions. The second label is placed directly under the space that is available to the first label, which is half of the window.
I've tried to explain and visualize the difference between expand and fill in this answer.
P.S. You don't need anchor=tk.N either. When the space available to the widget and the size of the widget are the same, the anchor option makes no difference. Also, side=tk.TOP is the default so you could decide to omit that too, leaving you with only fill=tk.X.
Looking at the docs I see:
The Text widget is used to display text in multiple lines.
and this seems to work:
import tkinter as tk
root = tk.Tk()
root.title("window")
yellow_header = tk.Label(root, text = 'Header\nParagraph', bg = 'light yellow')
yellow_header.pack(side = tk.TOP, anchor = tk.N, expand = 1, fill = tk.X)
That might be a bit OS specific and perhaps the proper way would be:
import os
...
yellow_header = tk.Label(root, text = 'Header' + os.linesep + 'Paragraph', bg = 'light yellow')
When increasing the length of the first string the second still remains in the center.
I was trying out something new on Tkinter (I am still a newbie), but it keeps failing...maybe someone could help out?
I wanted to create a window with several Frames, so that I can open and close them and show that way different content. However I am already stuck with not being able to "place" the, in this case a button, to the frame. Instead I get a blank frame with nothing inside...
The reason I want to use the place manager is so that I can easily choose the x and y coordinates. I don't want to create empty columns just in order to get a button appear in the middle of the screen.
Here the code:
from Tkinter import *
root = Tk()
root.title("Tkinter window")
root.geometry("800x600")
StartFrame = Frame(root)
StartFrame.pack()
Button1 = Button(StartFrame, command = StartTkinter, text = "Start", bg = "white", fg = "black", height = 2, width = 15)
Button1.place(x=0, y=50)
root.mainloop()
The problem is that you forgot to specify the dimensions of the frame. So, by default, it is created to be just 1 pixel high and 1 pixel wide. This means that its contents will not be visible on the window.
To fix the problem, you can either set exact values for these dimensions when you create the frame:
StartFrame = Frame(root, height=600, width=800)
or you can do:
StartFrame.pack(expand=True, fill="both")
to have the frame fill all available space.