How to I prevent the automatic widening of Tkinter widgets (specifically labels)? I have a label in my code to which I pass strings of varying length. In the case that the strings are wider than the column width (using the grid layout manager), I would prefer them to be moved to a new line rather than stretching the column. Below is some code that illustrates the problem.
import Tkinter
class window(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
self.grid()
self.columnconfigure(0, minsize=50)
self.columnconfigure(0, minsize=150)
self.rowconfigure(0,minsize=20)
self.rowconfigure(1,minsize=20)
self.rowconfigure(2,minsize=20)
self.labvar = Tkinter.StringVar()
self.lab = Tkinter.Label(self,bg='white',relief='groove',
textvariable=self.labvar)
self.lab.grid(row=0,column=0,rowspan=2,sticky='NSEW')
self.labvar.set("I don't want this to resize (Y dimension) ...")
self.but = Tkinter.Button(self, text='Click me!',command=self.onbut)
self.but.grid(row=2,column=0, sticky='NSEW')
def onbut(self):
self.labvar.set("I don't want this to resize (Y dimension) ...I'd rather this on a new line!")
if __name__ == "__main__":
app = window(None)
app.title('Window')
app.mainloop()
As a quick side note: what is the correct way to avoid the self.labvar.set("I dont...") line stretching over the 80 character limit? I tried using """ and breaking it over two lines but the string was then put in to the label with two lines as well.
Use wraplength option:
self.lab = Tkinter.Label(self,bg='white', relief='groove',
textvariable=self.labvar, wraplength=250)
According to The Tkinter Label Widget documentation:
Labels can display multiple lines of text. You can use newlines or use
the wraplength option to make the label wrap text by itself. When
wrapping text, you might wish to use the anchor and justify options to
make things look exactly as you wish.
...
wraplength=
Determines when a label’s text should be wrapped into multiple lines. This is given in screen units. Default is 0 (no wrapping).
If you give a label a width, it will try its best to honor that width even if the content changes. If you add text that is larger than the width, the text will be truncated. So, for example, you could create your label like this:
self.lab = Tkinter.Label(self,..., width=40)
If you instead want the text to wrap, you can define the wraplength argument, and text longer than the value will wrap to the next line. Unfortunately, the wraplength requires a screen unit (pixels) rather than character width, so you might have to do some math based on the font that you're using. Or, wait until the original widget is rendered, get it's width, and use that for the wraplength.
Related
for my GUI I want to use a grid with 6 columns. The size of the table is set. How do I fill the width of the grid with the columns? Can you set the size of the columns? I only found padx to change the padding, but not the actual size of the columns.
For now, this is supposed to work on a canvas. Is that even possible?
If you've fixed the size of the table, you can configure the columns to each be 1/6th the width of the window as a whole. The trick is to give each column the same non-zero weight, and use the uniform option. The uniform option takes a string, and all columns with the same value will be the same width.
If you run this code, notice how you can resize the window and the columns will automatically resize as well.
import tkinter as tk
import sys
root = tk.Tk()
root.geometry("600x400")
columns = []
for i in range(6):
frame = tk.Frame(root, borderwidth=1, relief="raised", background="bisque")
columns.append(frame)
root.grid_rowconfigure(0, weight=1)
for column, f in enumerate(columns):
f.grid(row=0, column=column, sticky="nsew")
root.grid_columnconfigure(column, weight=1, uniform="column")
root.mainloop()
With only the information you passed i would recommend you following:
Grid layouts your objects. Its not a table.
If you work with grid here. The simplest thing should be to set the width of the object you put on the wanted grid. For example:
If you add a Label set the label to the width you want it to be. This label will then
be displayed in the grid with its width and height.
If you want to display a table. Or even better load a table from a database. I would recommend a Treeview. Its good for design allows multiselect and its easy to fill and you dont have the layout problems :)
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.
How to I prevent the automatic widening of Tkinter widgets (specifically labels)? I have a label in my code to which I pass strings of varying length. In the case that the strings are wider than the column width (using the grid layout manager), I would prefer them to be moved to a new line rather than stretching the column. Below is some code that illustrates the problem.
import Tkinter
class window(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
self.grid()
self.columnconfigure(0, minsize=50)
self.columnconfigure(0, minsize=150)
self.rowconfigure(0,minsize=20)
self.rowconfigure(1,minsize=20)
self.rowconfigure(2,minsize=20)
self.labvar = Tkinter.StringVar()
self.lab = Tkinter.Label(self,bg='white',relief='groove',
textvariable=self.labvar)
self.lab.grid(row=0,column=0,rowspan=2,sticky='NSEW')
self.labvar.set("I don't want this to resize (Y dimension) ...")
self.but = Tkinter.Button(self, text='Click me!',command=self.onbut)
self.but.grid(row=2,column=0, sticky='NSEW')
def onbut(self):
self.labvar.set("I don't want this to resize (Y dimension) ...I'd rather this on a new line!")
if __name__ == "__main__":
app = window(None)
app.title('Window')
app.mainloop()
As a quick side note: what is the correct way to avoid the self.labvar.set("I dont...") line stretching over the 80 character limit? I tried using """ and breaking it over two lines but the string was then put in to the label with two lines as well.
Use wraplength option:
self.lab = Tkinter.Label(self,bg='white', relief='groove',
textvariable=self.labvar, wraplength=250)
According to The Tkinter Label Widget documentation:
Labels can display multiple lines of text. You can use newlines or use
the wraplength option to make the label wrap text by itself. When
wrapping text, you might wish to use the anchor and justify options to
make things look exactly as you wish.
...
wraplength=
Determines when a label’s text should be wrapped into multiple lines. This is given in screen units. Default is 0 (no wrapping).
If you give a label a width, it will try its best to honor that width even if the content changes. If you add text that is larger than the width, the text will be truncated. So, for example, you could create your label like this:
self.lab = Tkinter.Label(self,..., width=40)
If you instead want the text to wrap, you can define the wraplength argument, and text longer than the value will wrap to the next line. Unfortunately, the wraplength requires a screen unit (pixels) rather than character width, so you might have to do some math based on the font that you're using. Or, wait until the original widget is rendered, get it's width, and use that for the wraplength.
Why can't I see a red frame with the following code?
import Tkinter
root = Tkinter.Tk()
root.geometry("220x300")
container_frame = Tkinter.Frame(background = "red", width = 100, height = 120)
container_frame.pack()
widget_button = Tkinter.Button(master = container_frame)
widget_button.pack()
root.mainloop()
You don't see it because you have no padding between the button and the frame. By default, containers "shrink to fit" around their contents. Even if you add an explicit width or height to the frame, it will shrink to exactly fit its children.
There are several ways to achieve the effect you're looking for, but it's not clear exactly what effect you want. You can turn off this "shrink-to-fit" behavior (using container_frame.pack_propagate(False)). Or, you can add padding around the widget. Or, you can apply the background to the container of the frame. Or you could pack the frame to fill its container (the main window), then make sure the containing window is large enough to expose the frame.
For an example of that last suggestion, you can change one line to be this:
container_frame.pack(side="top", fill="both", expand=True)
If you change to:
widget_button.pack(padx=10, pady=10)
You can see that the frame has been resized when call widget_button.pack(...)
I've been playing around with tkinter a bit and I can't figure out why the "sticky" attribute doesn't seem to be working with my button. I've specified sticky to be NW which should cause my button to stick to the top left edge but for some reason it sticks to the top right. Any idea why?
from tkinter import *
from tkinter import ttk
def test():
name = userName.get()
text = "Hello {0}! Pleased to meet you.".format(name)
greeting.set(text)
window = Tk()
greeting = StringVar()
userName = StringVar()
name = Entry(window, textvariable=userName)
name.grid(column=1, row=1, sticky=NW)
button = Button(window, text="greeting", command=test)
button.grid(column=2, row=1, sticky=NW)
label = Label(window, textvariable=greeting)
label.grid(column=1, row=2, sticky=NW)
#creating a rectangle
canvas = Canvas(window)
canvas.grid(column=1, row=2)
#attributes are x,y coordinates of two points
x = canvas.create_rectangle(5,5,115,115)
mainloop()
The sticky attribute applies to the cell that the widget is in, rather than to the whole grid or whole window. So, the widget is anchored to the nw corner of its cell, it's just that you can't tell because the cell is exactly the same width as the button.
Since you are placing the button in the upper right cell (row 1, column 2) but say you want it in the upper left (of the whole window?) it's hard to know exactly what you want. Without knowing what you're trying to achieve it's hard to make any recommendations.
The easiest way to learn the grid layout manager is with paper and pencil. Get out some gridded graphing paper and draw your widgets on the paper. It then becomes obvious where to put your widgets.
You also need to learn about the rowconfigure and columnconfigure commands, especially with respect to the weight attribute. With this attribute you can identify which rows and columns grown and shrink to take up any extra space. It's also useful to know you can apply these attributes to empty rows and columns. This is useful if you want your interior widgets to stay the same size, and have any extra applied to the edges of your gui (generally not useful, though sometimes it is).
As a rough rule of thumb, each window should have one "major" widget -- the one that dominates the UI. Usually this is a canvas or text widget, but it doesn't have to be. Find that widget and give the row and column it is in a weight of 1 (one) so that it grows and shrinks as the user resizes the window. In your case this would be the canvas in row 2, column 1.
The point is that the grid cell is exactly the same size as the button - you won't notice if it in E or W...You can check that by placing all your widgets below each other (all row 0, and column 0-4) you'll see that in that case the button shows up NW. Hope this helps...