I've searched all over, but have yet to find a simple example showing how to change a minor element of a ttk widget style and applying it dynamically (after widget creation).
I have some ttk checkbuttons representing some configuration items, and a ttk button used to update a remote system. The checkbuttons are initialized to the state of the remote system.
If the user modifies any of the checkbuttons, I want both the checkbutton text and the update button text to become red, to remind the user that the checkbutton state no longer matches the remote state, and that the update button should be pressed to send the modified configuration to the remote system.
When the update button is pressed, the text color of the update button and all checkbuttons reverts to black.
I know this is possible (and is probably easy), but how?
Edit: Modifying the background color would also be OK.
You will need to create a custom style, and then apply that style to the widget. To create a custom style, first get an instance of ttk.Style, and then use the configure method to derive a new style from an existing one. The following example creates a new style named "Red.TCheckbutton":
style = ttk.Style()
style.configure("Red.TCheckbutton", foreground="red")
Next, you simply associate this style with the widget when you want the color to change:
my_checkbutton.configure(style="Red.TCheckbutton")
The best resource for learning how to work with the ttk styles is tkdocs.com. Specifically, https://www.tkdocs.com/tutorial/styles.html.
Here's a complete working example:
import ttk
import Tkinter as tk
class ExampleApp(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.var1 = tk.StringVar()
self.var2 = tk.StringVar()
f1 = ttk.Frame(self)
red_button = ttk.Button(f1, text="Red", command=self.make_red)
default_button = ttk.Button(f1, text="Default", command=self.make_default)
red_button.pack(side="left")
default_button.pack(side="left")
f2 = ttk.Frame(self)
self.cb_one = ttk.Checkbutton(f2, text="Option 1", variable=self.var1,
onvalue=1, offvalue=0)
self.cb_two = ttk.Checkbutton(f2, text="Option 2", variable=self.var2,
onvalue=1, offvalue=0)
self.cb_one.pack(side="left")
self.cb_two.pack(side="left")
f1.pack(side="top", fill="x")
f2.pack(side="top", fill="x")
style = ttk.Style()
style.configure("Red.TCheckbutton", foreground="red")
def make_red(self):
self.cb_one.configure(style="Red.TCheckbutton")
self.cb_two.configure(style="Red.TCheckbutton")
def make_default(self):
self.cb_one.configure(style="TCheckbutton")
self.cb_two.configure(style="TCheckbutton")
if __name__ == "__main__":
root = tk.Tk()
ExampleApp(root).pack(fill="both", expand=True)
root.mainloop()
You can use to change the background color using:
import tkinter as tk
root = tk.Tk()
root.configure(bg='blue')
Related
In Tkinter, (Python), I want to add a button that creates a new window with some other widgets such as other buttons and Labels. How do I do that?
If I create a button as:
Button(text = "click", command = new_window).pack()
So now what code should I use in the new_window() function in my program?
You can use Toplevel for that.
from tkinter import Tk, Toplevel
from tkinter.ttk import Label, Button
root = Tk()
root.title("Creating multiple windows")
root.geometry("500x500")
def new_window():
top = Toplevel()
top.title("Second window")
top.geometry("400x500") # By default, it is kept as the geometry of the main window, but you can change it.
lab = Label(top, text="This is second window!")
lab.pack(pady=20)
l = Label(root, text="This is the first window")
l.pack(pady=20)
b = Button(root, text="Create new window", command=new_window)
b.pack(pady=50)
root.mainloop()
You can use the “Toplevel” widget for that.
Here’s how you can do that:
from tkinter import *
# NOTE: You can replace the variable names with your own
# Defining a function for a new window to appear
def new_window():
# You can also customize this toplevel with similar attributes
new_toplevel = Toplevel()
# Creating the main GUI
root = Tk()
# You can customize the root window with attributes like .title(), .iconbitmap(), .geometry() etc.
new_button = Button(text=“New Window”, command=new_window)
new_button.pack(pady=10, padx=10) # Paddings are optional and you can also use .grid() with slightly different arguments
root.mainloop()
I am making a Speed Typing Test script using tkinter just like this website. But I am stuck in making selection to the text when the focus is in another widget.
Is it even possible to do make a selection visible in Text widget
when the focus is in Entry widget?
You can set the exportselection option of the text widget to False. That will prevent the selected text from automatically being associated with the clipboard. It is that automatic association which causes the selection to be removed whenever focus changes.
I figured it out myself that adding tags solves the problem.
text_widget.tag_add('highlight', '1.0', '1.24')
text_widget.tag_config('highlight', background='#0078d7')
Set the exportselection to False and the inactiveselect attribute to the same color of the selectbackground attribute.
Example:
import tkinter as tk
root = tk.Tk()
text = tk.Text(root, exportselection = False)
text.config(inactiveselect = text.cget("selectbackground"))
text.pack(fill = "both", expand = True)
root.mainloop()
Example using class inheritance:
import tkinter as tk
class myText(tk.Text):
def __init__(self, master, **kw):
tk.Text.__init__(self, master = master, **kw)
if 'exportselection' not in kw.keys():
self['exportselection'] = False
if 'inactiveselect' not in kw.keys():
self['inactiveselect'] = self['selectbackground']
root = tk.Tk()
text = myText(root)
text.pack(fill = "both", expand = True)
root.mainloop()
This way the Text widget will automatically set the exportselection to false and the inactiveselect to selectbackground if you dont define any value to then.
I'd like to create a simple quiz with Tkinter (Python 2.7). I have a list of audios and for each of them I want to have the following:
A button that reproduces the audio.
An entry where the user can introduce any text.
A label displaying "Incorrect" by default and "Correct!" whenever the text in the entry is the title of the song in the audio.
I managed to create the three objects, but I'm having a hard time trying to update the label according to the entry text: there are many references around but I couldn't get it working. I guess I do not understand well how the loop works and when events are triggered.
Could you please provide a minimal example that does what I intend to? I provide my code below, but it is very likely to be bloated (I'm totally novice to Tkinter and to object oriented programming) since I basically built it from an existing example in the Internet:
#!/usr/bin/env python
#encoding=utf-8
import Tkinter as tk
import vlc
tk.Tk()
var_entry= tk.StringVar()
var_label= tk.StringVar()
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
self.audio= tk.Button(self,text='Play Audio',command=lambda:vlc.MediaPlayer('./audios/my_audio.mp3').play())
self.audio.grid()
self.entry = tk.Entry(self,width=50,textvariable=var_entry)
self.entry.grid()
var_label.set('Correct!\n' if var_entry.get()=='my_audio_title' else 'Incorrect\n')
self.label = tk.Label(self,textvariable=var_label)
self.label.grid()
self.quitButton = tk.Button(self, text='Quit',command=self.quit)
self.quitButton.grid()
app = Application()
app.master.title('Audio Quiz')
app.mainloop()
I'm sure that the line starting by var_label.set is missplaced, but I don't really know where I should write it and how to make the update for the label.
Thanks in advance.
You can use trace to achieve:
A label displaying "Incorrect" by default and "Correct!" whenever the text in the entry is the title of the song in the audio.
When trace is used with 'w' option, it calls a method whenever the variable class(BooleanVar, DoubleVar, IntVar, StringVar) it is attached to is re-written. Below is an example checking whether the text in the entry is "Valid String" or not:
import tkinter as tk
def check_entry(*args):
global entry, entry_var, label
if entry_var.get() == "Valid String":
label['text'] = "Correct"
else:
label['text'] = "Incorrect"
root = tk.Tk()
entry_var = tk.StringVar()
label = tk.Label(root)
entry = tk.Entry(root, textvariable=entry_var)
label.pack()
entry.pack()
entry_var.trace('w', check_entry)
root.mainloop()
I am having a big issue. The Canvas loads perfectly but the image does not display.
I started Python 1 week ago and I have no clue why does is not working. Can anyone please show me the way to solve the issue of the image not loading on the canvas?
from Tkinter import *
from PIL import ImageTk
from PIL import Image
class Fake_Virus:
def __init__(self, master):
self.master = master
master.title("Totally not a virus!")
b = Button(master, text="Help", command=self.prank)
b.pack(padx=10, pady=10, side=LEFT)
quit = Button(master, text="Close", command=self.close_window)
quit.pack(padx=10, pady=10, side=RIGHT)
photo = PhotoImage("eh.gif")
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
f = Frame(master, height=150, width=150)
f.pack_propagate(0) # don't shrink
f.pack()
def prank(self):
print "work"
return
def close_window(self):
root.destroy()
return
root = Tk()
my_gui = Fake_Virus(root)
root.mainloop()
You should use the file option to initialize the photo image object.
This means you need to change photo = PhotoImage("eh.gif") to photo = PhotoImage(file="eh.gif")
Now your code will work. But a working code is not necessarily a good code. There are other issues with your code. Let me go through them quickly:
It is better to code import Tkinter as Tk than from Tkinter import *
Why that hyphen in your class name? Follow PEP8 so that, in the futur, people will find it easy to review and understand your code.
Good that you have written self.master = master (read complete code to know why) but then you have never used it. This means you made a good decision and you render it useless.
You set the title of the window within the initializer. It is better if you do that in a separate function so that whenever you want to add additional settings to your GUI (such as the size, font or whatever) you will only add code to that function instead of vomiting lot of trash inside the initializer which rather needs to be clean.
None of the widgets you created is 'selfed' (you may read Why explicit self has to stay)
It is better you create the widgets in a separate function otherwise your __init__() will be dirty.
Why do you use return in prank() and close_window()? By default, Python functions that do not return something return None anyway so it is useless to code that.
Why did you pack one button to left and the other one to right and then no pack siding for the label? Read about the pack() geometry manager.
Why you did not attach the label to a parent widget as you did for the 2 other buttons? All Tkinter widgets need to be clung into a parent widget. The grand parent of those widgets is an instance of Tkinter.Tk()
Why did you create that frame and then you never used it? You are not doing anything with it, so ..?
Given these remarks, I want to provide you an improved -but not perfect- version of your program. You can then follow this 'philosophy' to add or modifying existing widgets:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import Tkinter as Tk
from PIL import ImageTk
class FakeVirus:
def __init__(self, master):
self.master = master
self.configure_gui()
self.create_widgets()
def configure_gui(self):
self.master.title('Totally not a virus!')
def create_widgets(self):
self.create_buttons()
self.create_label_for_image()
def create_buttons(self):
self.help = Tk.Button(self.master, text='Help', command=self.prank)
self.help.pack(side=Tk.LEFT)
self.quit = Tk.Button(self.master, text='Close', command=self.close_window)
self.quit.pack(side=Tk.LEFT)
def create_label_for_image(self):
self.image_label = Tk.Label(self.master)
self.image_label.pack(side=Tk.LEFT)
self.load_image_to_label()
def load_image_to_label(self):
self.photo = ImageTk.PhotoImage(file='eh.gif')
self.image_label.image = self.photo
self.image_label.config(image=self.photo)
def prank(self):
print "work"
def close_window(self):
root.destroy()
if __name__ == '__main__':
root = Tk.Tk()
my_gui = FakeVirus(root)
root.mainloop()
The output of the above program is:
I am trying to incorporate a scrollbar in my Tkinter window so as I add more things the user can scroll down. From what I've read so far it seems I need to use a Listbox widget but running into trouble adding the scrollbar as it is only getting added to the bottom of the program and not wrapping the entire thing? My code is below.
import Tkinter as Tk
from Tkinter import StringVar
class SampleApp(Tk.Tk):
def __init__(self):
Tk.Tk.__init__(self)
self.button = Tk.Button(self, text="Get", command=self.on_button)
self.button.pack()#place(x=150, y=600)
####Name#####
labelText=StringVar()
labelText.set(" Name")
labelDir=Tk.Label(self, textvariable=labelText, height=1)
labelDir.pack()
directory=StringVar(None)
self.name =Tk.Entry(self,textvariable=directory,width=25)
self.name .pack()
def on_button(self):
with open('filename.html', 'w') as myfile:
myfile.write('The button worked')
app = SampleApp()
scrollbar = Tk.Scrollbar(app)
scrollbar.pack( side = Tk.RIGHT, fill=Tk.Y )
app.mainloop()
The issue is that the Scrollbar widget is only implemented for Canvas, Listbox, and Text widgets. The Scrollbar must be configured to one of those widgets, and then you add widgets to that parent. Unfortunately, it's not the most straightforward widget to use. See Effbot's Scrollbar page for an example.