I have a problem understanding where vertical padding in a widget comes from. The following code
import Tkinter as tk
import tkFont
currentfont = 'Muli'
root = tk.Tk()
# a single frame in root - the prod version has several
timestr = tk.Frame(root)
# the green background is added to show the limits of the Label
timeh = tk.Label(timestr, background='green', text="21")
timem = tk.Label(timestr, background='green', text="45")
timeh.grid(row=0, column=0)
timem.grid(row=0, column=1, sticky=tk.N)
timem.configure(font=(currentfont, 170))
font = tkFont.Font(family=currentfont, size=250, weight="bold")
timeh.configure(font=font)
timestr.grid(row=0, column=0, columnspan=3)
tk.mainloop()
produces
Why is there vertical padding above and below the glyphs? Is there a way to make them vertically fit into the window, similarly to how they fit horizontally?
The padding is likely part of the font, not the widget. Fonts need space above and below the space for average sized characters to make room for ascenders and descenders. The larger the font, the more space that is needed . Without the space below, for example, where would the bottom part of a lowercase "g" or "y" go?
Related
I am creating a registration form, and I have coded labels to show next to the text box for someone's username and password. This is the code I am using to place the text boxes and labels:
usernamebx.place(relx=0.5, rely=0.5, width=225, height=25,
anchor= CENTER)
userbx_label.place(relx=0.1, rely=0.5, anchor=CENTER)
passwbx.place(relx=0.5, rely=0.6, width=225, height=25, anchor = CENTER)
passwbx_label.place(relx=0.1, rely=0.6, anchor=CENTER)
The code for usernamebx and passwbx means that the text boxes don't move when I resize the tkinter window. However, I have done the same with the labels for each but it doesn't work. Any help?
The code for usernamebx and passwbx means that the text boxes don't move when I resize the tkinter window.
Actually, they do move! If you put a widget at relx 0.5 in a window that is 200 pixels wide, that means the center of the widget will be 100 pixels from the left edge of the window. When you grow the window to 400 pixels wide, the center of the widget now will be 200 pixels from the left edge. It moved 100 pixels. You don't see it because it's symmetrical so it stays in the center.
The same happens with a widget that is at 0.1. on a 200 pixel wide window it's going to be 20 pixels from the left edge. When you make the window 400 pixels widget it's going to be 40 pixels from the edge.
This is the nature of relative coordinates -- they will always change when the window is resized.
It's hard to see what your actual requirement is, though I'm guessing you want the username label+entry and password label+entry to be co-aligned in the center of the window.
If that's the case, one simple solution is to put those widgets in a frame. Use grid internally since it appears that you are in fact creating a grid. Then, you can place the frame in the window as a separate step.
Here's an example of the technique. For illustrative purposes the frame has a visible border, but that's not strictly necessary. You can remove the border to make it blend in with the background.
This example uses place to put the frame in the center, though you can also use pack.
import tkinter as tk
root = tk.Tk()
root.geometry("400x400")
inner_frame = tk.Frame(root, bd=2, relief="groove")
usernamebx = tk.Entry(inner_frame)
userbx_label = tk.Label(inner_frame, text="Username:")
passwbx = tk.Entry(inner_frame)
passwbx_label = tk.Label(inner_frame, text="Password:")
inner_frame.grid_columnconfigure(0, weight=1)
userbx_label.grid(row=0, column=0, sticky="e")
usernamebx.grid(row=0,column=1, sticky="ew")
passwbx_label.grid(row=1, column=0, sticky="e")
passwbx.grid(row=1, column=1, sticky="ew")
inner_frame.place(relx=.5, rely=.5, anchor="center")
root.mainloop()
If you want to use pack rather than place, have the packer expand the allocated space to be the whole window, and the frame will automatically be centered. In this case the window will shrink to fit the frame plus the padding.
inner_frame.pack(side="top", expand=True, padx=10, pady=10)
I'd like to create three text areas in a tkinter window and make them dinamically resizable. I thought that one solution was to pass the width and height parameters in pixels (such as height=int(win_height/2)), but as I read it isn't possible, in fact the width and height parameters in a tk.Text widget are calculated by characters for each line and column. I've also tried to pass the width and height parameters in percentages (such as height=50%) but it returns me a syntax error.
I've been trying to find out a solution for this problem in the net, and the best code I've found is this:
import tkinter as tk
root = tk.Tk()
root.geometry("500x500")
# Text Box
first_textbox = tk.Text(root, width=25, height=10, bg='yellow')
second_textbox = tk.Text(root, width=25, height=10, bg='blue')
third_textbox = tk.Text(root, width=50, height=20, bg='red')
# Packing
first_textbox.grid(column=1, row=1)
second_textbox.grid(column=1, row=2)
third_textbox.grid(column=2, row=1, rowspan=2)
root.mainloop()
By running this code I obtain a window with three different text areas which aren't dinamically resizabled and which take more space than the actual window width. I hope you can help me.
Sorry for any English mistake, it is my second lenguage
grid has several documented parameters to help you do what you want. You simply need to use them.
By default, grid won't give widgets any extra space -- they take up only the space they need and no more. If you widgets to be allocated extra space, you have to explicitly arrange for that.
For example, if you want all widgets to grow and shrink equally, you need to configure the rows and columns to have an equal weight greater than zero. That will tell grid how to allocate any extra space when the window is bigger than the size requested by all of the widgets.
For example:
root.grid_rowconfigure((1,2), weight=1)
root.grid_columnconfigure((1,2), weight=1)
That just tells grid what to do with extra space. If instead, you want two or more rows or columns to have exactly the same size, you can use the uniform option to tell grid that you want the rows or columns to have a uniform (identical) size.
For example, if you want both columns 1 and 2 to have the same width, you can give each column the same value for the uniform option. Note: the value passed to uniform can be anything you want. The important thing is that they are configured to have the same value.
root.grid_columnconfigure((1, 2), uniform="equal")
That alone won't solve the problem. You also must tell grid that you want the widgets to fill the space given to them. You do that with the sticky parameter, which tells grid to "stick" the widget to one or more sides of the allocated space.
To get the widgets to fill all allocated space you can give the string "nsew" which stands for "north, south, east, and west" which represent the four sides of the given space.
first_textbox = tk.Text(root, width=25, height=10, bg='yellow')
second_textbox = tk.Text(root, width=25, height=10, bg='blue')
third_textbox = tk.Text(root, width=50, height=20, bg='red')
I want to create a desktop-application using tkinter. When placing text (of big size) in Labels, I always get a large vertical padding. Can I anyhow get rid of this additional space? I would like to place the text right on the bottom of the label.
I have already tried to set pady as well as the text anchor.
self.lbl_temp = Label(self.layout, text='20°C', font=('Calibri', 140), bg='green', fg='white', anchor=S)
self.lbl_temp.grid(row=0, column=1, sticky=S)
Here is an image of how it looks:
I would like to remove the green space below (and on top of) the text.
Removing the space above and below the text cannot be done with a Label because the height corresponds to an integer number of lines whose height is determined by the font size. This line height reserves space for letters going below the baseline, like 'g', but since you don't use such letters, you have a lot of empty space below your text (I don't have as much extra space a the top on my computer though).
To remove this space, you can use a Canvas instead of a Label and resize it to be smaller.
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, bg='green')
canvas.grid()
txtid = canvas.create_text(0, -15, text='20°C', fill='white', font=('Calibri', 140), anchor='nw')
# I used a negative y coordinate to reduce the top space since the `Canvas`
# is displaying only the positive y coordinates
bbox = canvas.bbox(txtid) # get text bounding box
canvas.configure(width=bbox[2], height=bbox[3] - 40) # reduce the height to cut the extra bottom space
root.mainloop()
I was able to get the Scrollbar to work with a Text widget, but for some reason it isn't stretching to fit the text box.
Does anyone know of any way to change the height of the scrollbar widget or something to that effect?
txt = Text(frame, height=15, width=55)
scr = Scrollbar(frame)
scr.config(command=txt.yview)
txt.config(yscrollcommand=scr.set)
txt.pack(side=LEFT)
In your question you're using pack. pack has options to tell it to grow or shrink in either or both the x and y axis. Vertical scrollbars should normally grow/shrink in the y axis, and horizontal ones in the x axis. Text widgets should usually fill in both directions.
For doing a text widget and scrollbar in a frame you would typically do something like this:
scr.pack(side="right", fill="y", expand=False)
text.pack(side="left", fill="both", expand=True)
The above says the following things:
scrollbar is on the right (side="right")
scrollbar should stretch to fill any extra space in the y axis (fill="y")
the text widget is on the left (side="left")
the text widget should stretch to fill any extra space in the x and y axis (fill="both")
the text widget will expand to take up all remaining space in the containing frame (expand=True)
For more information see http://effbot.org/tkinterbook/pack.htm
Here is an example:
from Tkinter import *
root = Tk()
text = Text(root)
text.grid()
scrl = Scrollbar(root, command=text.yview)
text.config(yscrollcommand=scrl.set)
scrl.grid(row=0, column=1, sticky='ns')
root.mainloop()
this makes a text box and the sticky='ns' makes the scrollbar go all the way up and down the window
Easy solution to use a textbox with an integrated scrollbar:
Python 3:
#Python 3
import tkinter
import tkinter.scrolledtext
tk = tkinter.Tk()
text = tkinter.scrolledtext.ScrolledText(tk)
text.pack()
tk.mainloop()
To read the textbox:
string = text.get("1.0","end") # reads from the beginning to the end
Of course you can shorten the imports if you want.
In Python 2 you import ScrolledText instead.
I am trying to display an image with ttk/tkinter in python. The image has a white border, and I want to display this image on a larger white background - so it has lots of white space around.
For this I use "padx" and "pady" in the label with 100px each. Unfortunately the padding area is gray.
Now I tried changing the label's foreground and background color to no avail. The padding area stays gray. Then I put the label in a Frame widget, and tried changing the Frame's foreground/background color. But unfortunately the Frame widget does not listen to width= and height= arguments. Also if I change the foreground color the SUNKEN border changes color - really cool, but totally useless for me :/ .
Could anybody help me with this? The current nonworking code looks like this:
style = Style()
style.configure('Dlr.TFrame', background="blue", relief=SUNKEN)
frm = Frame(self, style="Dlr.TFrame") # does not work: ,height=500,width=500)
frm.grid(row=0, column=0, rowspan=8, columnspan=2, sticky=N+S+E+W)
style.configure("Dlr.TLabel", background="white")
style.configure("Dlr.TLabel.padding", background="white") # just guessed ...
self.IMG = Label(frm, style="Dlr.TLabel")
self.IMG.grid(row=0, column=0, padx=100, pady=100)
Your technique for putting the image inside a frame and then setting the frame color is the right technique.
The width and height don't work because both grid and pack cause the containing widget to "shrink-to-fit" by default. This is called geometry propagation. You can turn this feature on or off using the methods grid_propagate or pack_propagate on the containing widget.
For example, if you call frm.grid_propagate(False) and then set the widget and height of frm, the width and height will be honored.