Why does "ttk.style()" adds additional styles to the Entry widget? - python

So, I have 4 Entry Widgets on my window and I just wanted to add some internal left padding on the last Entry Widget. I did so using ttk.style(), which added the desired padding but it also added some additional styling like a black border, some hover effect, then the entry widget gets a blue border when selected.
This is my Code:
from tkinter import *
from tkinter import ttk
root = Tk()
root.configure(padx=50)
input1 = Entry(root)
input1.grid(row=1, column=0, pady=10)
input2 = Entry(root)
input2.grid(row=2, column=0, pady=10)
input3 = Entry(root)
input3.grid(row=3, column=0, pady=10)
style = ttk.Style(root)
style.configure('padded.TEntry', padding=[15, 0, 0, 0])
e = ttk.Entry(root, style='padded.TEntry')
e.grid(row=4,column=0, pady=10)
root.mainloop()
Look how the 4th Entry Widget has a Black Border around it
Look how a Blue Border appears when the widget is selected
The only styling that I was excepting is the little increase in width because of the left-padding, but how are these other styles being triggered.

It is because the fourth entry is a ttk.Entry widget but the other three are tkinter.Entry widgets. If you make all four ttk.Entry widgets you'll see that they all have the additional styles.
Even though the tkinter and ttk modules have widgets with the same name, they are completely different widgets with their own sets of defaults.

Related

Add label to Tkinter button

I'm trying to add a label to a Tkinter button. A Tkinter button has a 'text' attribute which is great. However I need another similar text field on the button, i.e 2 text fields per button. So I would like to add a label as a child to the button. The problem with this is that it creates unpredictable behaviour. Actuating the button or even just hovering the mouse over the label causes the label to vanish.
button_1 = tk.Button(canvas, text='Take',command='take',width='12')
button_1.pack()
label_1 = tk.Label(button_1, text='Stream 1',font=('calibre',8))
label_1.pack()
label_1.place(x=10, y=10)
I can avoid this unwanted behaviour by adding the label to the same parent as the button.
label_1 = tk.Label(canvas, text='Stream 1',font=('calibre',8))
But by doing this I loose the convenience of positioning the label with the button's geometry, e.g inset by 10 pixels from left and 10 pixels from top of button. I plan to make a convenience method for adding hundreds of such buttons, and do not wish to be attempting to calculate each label in the parents coordinates. And besides, as a sub-child of the button, it becomes part of the button hierarchy, i.e. will move with the button, will be hidden with the button etc.
You can display multiple lines of text on a Button by embedding newlines in the text.
Like this:
import tkinter as tk
root = tk.Tk()
root.geometry('100x100')
canvas = tk.Canvas(root)
canvas.pack()
button_1 = tk.Button(canvas, text='Take\nStream 1',command='take',width='12')
button_1.pack()
#label_1 = tk.Label(button_1, text='Stream 1',font=('calibre',8))
#label_1.pack()
#label_1.place(x=10, y=10)
root.mainloop()
Result:
You can use the anchor and justify options to change the positioning of the text on the Label:
button_1 = tk.Button(canvas, text='Take\nStream 1', command='take', width='12',
anchor='w', justify='left')
button_1.pack()
Result 2:

Can't prevent label from extending the main window

Example
(mimics the relevant parts of the layout in my real code)
import Tkinter as tk
import ttk
# set up root
root = tk.Tk()
root.minsize(300, 50)
frame = ttk.Frame(root)
frame.grid(row=0, column=0, sticky=tk.EW)
# set up buttons that insert a short or a long string
textvar = tk.StringVar(value='foo')
def insert_short():
textvar.set('foo')
def insert_long():
textvar.set('foo'*30)
button_short = ttk.Button(frame, text='short', command=insert_short)
button_short.grid(row=0, column=0)
button_long = ttk.Button(frame, text='long', command=insert_long)
button_short.grid(row=0, column=0)
button_long.grid(row=0, column=1)
# set up label
# border for label to see its size
style = ttk.Style()
style.configure(
'Bordered.TLabel', foreground='black', borderwidth=1, relief='solid')
# make label extend to the right
frame.columnconfigure(2, weight=1)
# place label
label = ttk.Label(frame, textvariable=textvar, style='Bordered.TLabel')
label.grid(row=0, column=2, sticky=tk.EW)
# place some other widget under label to mimic my real code
ttk.Button(frame, text='some other widget').grid(row=1, column=2)
# TRIED, NOT WORKING:
#root.resizable(False, False)
#frame.propagate(False)
#frame.grid_propagate(False)
#label.propagate(False)
#label.grid_propagate(False)
root.mainloop()
Output
Question
How do I prevent label from extending the main window?
(Bonus question, but not important: is there a way to make the label scrollable if it gets too long?)
Attempts
I tried the following commands:
root.resizable(False, False)
frame.propagate(False)
frame.grid_propagate(False)
label.propagate(False)
label.grid_propagate(False)
You can create a scrollable label using an Entry in a read-only state and by using scrolling it will prevent the widget from extending the main window.
Try replacing your label definition with the following code:
child_frm = ttk.Frame(frame)
label = ttk.Entry(child_frm, textvariable=textvar, style='Bordered.TLabel', state='readonly')
scroll = ttk.Scrollbar(child_frm, orient='horizontal', command=label.xview)
label.config(xscrollcommand=scroll.set)
label.grid(row=0, sticky=tk.EW)
scroll.grid(row=1, sticky=tk.EW)
child_frm.grid(row=0, column=2)
By default, the width of a Label is calculated based on its contents. You can override this behavior by specifying a value for width when creating the Label.
label = ttk.Label(frame, textvariable=textvar, style='Bordered.TLabel', width=1)
Much to my surprise, when I update your code with this, the label doesn't shrink to a size suitable for displaying exactly one character. It appears that the sticky=tk.EW argument of your grid call ensures that the label stays as wide as the widest element in the column.

Adjust height of a row in Tkinter Treeview depending on wraped strings

I was making a treeview for my tkinter program, and when I insert the data into it, there are some rows with too many characters and some of them are unseen.
Is there any way to set an auto line feed for the row height? I don't want to add a horizontal scrollbar to solve this. This means when the width of a strings is bigger then th columns with a newline should be iserted (automaticly). In that case the height of the row should be adjusted (automaicly) in that case to display more then one line.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
frame = Frame(root)
frame.pack()
tree = ttk.Treeview(frame, height=15,
columns=('c1', 'c2'),
show="headings")
tree.column('c1', width=100, anchor='center')
tree.column('c2', width=200, anchor='center')
tree.heading('c1', text='text1')
tree.heading('c2', text='text2')
tree.pack()
tree.insert('', 1, values=['edew', 'dewd'])
tree.insert('', 2, values=['abcd'*10, 'wxyz'*10])
root.resizable(width=False, height=False)
root.mainloop()
The height of alls row can be modified with this code.
root = Tk()
style = ttk.Style(root)
style.configure('Treeview', rowheight=45)
It would be very nice to know if each row in a Treeview can have it's own style to defined individual rowheight (depending on newline characters).
In response to Aleksandar Beat's question:
In order to change the style of a specific Treeview, you have to define a different identifier before the ttk widget name:
Style().configure('AnythingYouWantHere.Treeview', rowheight=45)
# The '.Treeview' can not be changed
However, if you do it this way, you have to specify the style option of the ttk widget.
Sample usage:
root = Tk()
Style().configure('MyStyle1.Treeview', rowheight=45)
styledTreeView = Treeview(root, style='MyStyle1.Treeview')
normalTreeView = Treeview(root) # Style will not automatically apply
This also applies to any ttk widgets.

Tkinkter Grid() alignment not as expected: Python 2.7

I am trying to use grid() function to align the labels and option menu side by side. Here's the code which I used to create a simple GUI:
from Tkinter import *
win1 = Tk()
win1.title("Chumma")
#Option selection frame:
f3 = Frame(win1)
f3.grid(column=0,row=0)
f3.pack()
l1 = Label(f3, text="Select the function which you want to perform: ", bg = "yellow")
moduleList = StringVar(f3)
moduleList.set("Normal Walk") #to display the default module name
o1 = OptionMenu(f3, moduleList, "Normal Walk", "Brisk Walk", "Running", "Custom")
b3 = Button(f3, text="Execute the option", fg="blue")
b4 = Button(f3, text="Stop", fg="red")
#Packing the stuffs in required order:
l1.grid(row=0, column=0, sticky=W) #E means east
l1.grid_rowconfigure(0, weight=1)
l1.grid_columnconfigure(0, weight=1)
l1.pack(fill = X, padx = 5)
o1.grid(row=0,column=1)
o1.grid_rowconfigure(0, weight=1)
o1.grid_columnconfigure(1, weight=1)
o1.pack()
b4.pack()
win1.mainloop()
The result is:
I am expecting the option menu o1 to be at the right of the l1.
If I comment the pack command [ l1.pack() and o1.pack() ], the program is not displaying any GUI at all.
After you call grid, a couple of lines later you call pack which cancels out the use of grid. Use one or the other but not both for each widget. Sinc pack defaults to side='top', your widgets appear stacked on top of each other.
The reason you see nothing if you comment out those two calls to pack is because you are still calling b4.pack(), and you can't use both pack and grid for different widgets with the same parent.
Also, the calls to rowconfigure and columnconfigure need to be on the parent widget. Calling them on the label widget will only affect widgets you put inside the label (which is possible, but unusual)
I believe Tkinter does not allow mixing up packing schemes (grid, pack, place) in one frame. Here is example how to organize three widgets.
from Tkinter import *
root = Tk()
label = Label(root, text='blablabla')
someotherwidget = Entry(root)
button = Button(root, command=lambda: None, text='Boom')
label.grid(row=0, column=0)
someotherwidget.grid(row=0, column=1)
button.grid(row=1, column=0, columnspan=2)
root.mainloop()
Option 'columspan' is like how many columns you want to join to place the widget. We have 2 columns here so if we want to see button not below label but below of both label and someotherwidget we have to specify 'columnspan' option (obviously, analog for rows is 'rowspan')

How to set border color of certain Tkinter widgets?

I'm trying to change the background color of my Tkinter app, but for certain widgets it leaves a white border around the edges.
For example, this:
from tkinter import *
COLOR = "black"
root = Tk()
root.config(bg=COLOR)
button = Button(text="button", bg=COLOR)
button.pack(padx=5, pady=5)
entry = Entry(bg=COLOR, fg='white')
entry.pack(padx=5, pady=5)
text = Text(bg=COLOR, fg='white')
text.pack(padx=5, pady=5)
root.mainloop()
How can I set border colour of certain Tkinter widgets?
Just use
widget.config(highlightbackground=COLOR)
Furthermore, if you don't want that border at all, set the highlightthickness attribute to 0 (zero).
You have to set the two highlights (with and without focus) to have a continuous color.
from tkinter import *
root = Tk()
e = Entry(highlightthickness=2)
e.config(highlightbackground = "red", highlightcolor= "red")
e.pack()
root.mainloop()

Categories

Resources