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()
Related
I am trying to develop a small database application using tkinter in python. I have a small database which has inventory numbers for various assets. I have successfully constructed the listbox using tkinter and managed to populate it using sqlite3's library.
I've been trying to get the << ListBoxSelect >> event bound to which item is selected in the listbox and to output the asset's information. Several hours later, all I'm trying to do now is get the bind event to fire at all and print out a simple string until I can figure it out.
Before I post my code, I used This code snippet to see if I could get ANY binding event to work. This code snippet does in fact work however trying to integrate the way its written into my own code has not been successful. The event binder simply does not fire in my code at all. Can anyone please identify why this is or what I'm doing incorrectly?
Please see relevant code below (please note the indenting is not correct when trying to paste it into this forum):
class MainWindow(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# Create Widgets
self.invListbox = tk.Listbox(self, height=20, width=10, selectmode=tk.SINGLE)
self.assetDescLabel1 = tk.Label(self, text="Asset Description")
self.assetDescEntry1 = tk.Entry(self)
self.exitButton = tk.Button(self, text="Exit", command=exit)
# Configure Widgets
# Updates assetDescEntry based on invListbox selection
self.invListbox.bind('<<ListboxSelect>>', self.invOnSelect())
# Place Widgets
self.invListbox.grid(row=1, column=1)
self.assetDescLabel1.grid(row=1, column=2)
self.assetDescEntry1.grid(row=2, column=2)
self.exitButton.grid(row=2, column=1)
# Populate invListbox
self.InvList = main.sqlInventoryNumberList()
for row in self.InvList:
item = row[0]
self.invListbox.insert(1, item)
def invOnSelect(self):
selection = "This is just a regular old string"
print(selection)
I can't get the code above to run as too much is missing. You must bind a function in the bind statement not the result of a function. So .bind( ... , func ) NOT .bind(..., func() ). Your function must take an event object too. Below is a minimal working example.
import tkinter as tk
def invOnSelect(event):
selection = "This is just a regular old string"
print( event, selection )
root = tk.Tk()
invListbox = tk.Listbox( root, height=20, width=10, selectmode=tk.SINGLE )
invListbox.grid()
invListbox.bind( '<<ListboxSelect>>', invOnSelect ) # invOnSelect has no brackets here.
InvList = [ '1', '2', '3' ]
for item in InvList:
invListbox.insert(tk.END, item)
root.mainloop()
The ideas should integrate into your application.
Since no one in the Spanish StakOverFlow hasn't answered me yet, I'm asking here. I'm from ARG. I am working on an mass document upload automation. The first widget I made with tkinter asks the user what type of document wants to upload to the web. Once this process is done, I want to throw another widget to ask the same question. The thing is I don't know how to write that code. I have not learned yet how to handle classes. And the code for my widget is a copy from an example of the web, and formatted to fit my specs.
from Tkinter import Tk, Label, Button
class DocumentTypeOption:
def __init__(self, master):
self.master = master
master.iconbitmap("bigpython.ico")
master.minsize(280,150)
master.geometry("280x150")
master.title("DOCUMENT TYPE")
self.label = Label(master, text="SELECT THE DOCUMENT TYPE")
self.label.pack()
self.tipo1_button = Button(master, text="Tipo1", command=self.opcion_tipo1)
self.tipo1_button.pack()
self.tipo2_button = Button(master, text="Tipo2", command=self.opcion_tipo2)
self.tipo2_button.pack()
def funciontipo1(self):
def subirtipo1():
"things to upload doc type1"
time.sleep(0.5)
root.destroy()
time.sleep(1)
subirtipo1()
"SHOULD THE WIDGET BE CALLED HERE?"
def funciontipo2(self):
def subirtipo1():
"things to upload doc type2"
time.sleep(0.5)
root.destroy()
time.sleep(1)
subirtipo2()
"SHOULD THE WIDGET BE CALLED HERE?""
root = Tk()
my_gui = OpcionTipoDeDocumento(root)
root.mainloop()
When one type of document was uploaded I need to throw the widget once more in order to ask the user if he wants to continue with the other type of document.
There are a few options. You could simply keep the Tkinter window open and ask the user if they want to load another file. You also are using sleep() inside a tkinter instance. You cannot use sleep() within Tkinter. There is another method called after() that is for setting up timed events to replace the use of sleep(). In this case I don't think you need a delay anyway.
Here is a simple example using a tkinter class and 1 function for the doc and 1 function to ask if you want to load another one.
# import tkinter as tk # for python 3.X
# from tkinter import messagebox # for python 3.X
import Tkinter as tk
import tkMessageBox as messagebox
class DocumentTypeOption(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.iconbitmap("bigpython.ico")
self.minsize(280,150)
self.geometry("280x150")
self.title("DOCUMENT TYPE")
self.label = tk.Label(self, text="SELECT THE DOCUMENT TYPE")
self.label.pack()
self.tipo1_button = tk.Button(self, text="Tipo1", command=lambda: self.do_stuff("Tipo1"))
self.tipo1_button.pack()
self.tipo2_button = tk.Button(self, text="Tipo2", command=lambda: self.do_stuff("Tipo2"))
self.tipo2_button.pack()
def do_stuff(self, doc_type):
# things to upload doc type1
# you can do this part with a single function as long as you check the doc type first.
print(doc_type) # just to verify buttons are working.
self.check_next(doc_type)
def check_next(self, doc_type):
x = messagebox.askyesno("DOCUMENT OPTION", "Would you like to load another {}?".format(doc_type))
if x != True:
self.destroy()
my_gui = DocumentTypeOption()
my_gui.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 would like to make a Tkinter window able to ask for a multi line entry
(so the user will add one or more lines of text)
And then when we clic on the button be able to retrieve the values entered by user for further use.
Until now I have this script:
from Tkinter import *
import ScrolledText
class EntryDemo:
def __init__(self, rootWin):
#Create a entry and button to put in the root window
self.textfield = ScrolledText(rootWin)
#Add some text:
self.textfield.delete(0,END)
self.textfield.insert(0, "Change this text!")
self.textfield.pack()
self.button = Button(rootWin, text="Click Me!", command=self.clicked)
self.button.pack()
def clicked(self):
print("Button was clicked!")
eText = self.textfield.get()
print("The Entry has the following text in it:", eText)
#Create the main root window, instantiate the object, and run the main loop
rootWin = Tk()
#app = EntryDemo( rootWin )
rootWin.mainloop()
But it didn't seem to work, A window appear with nothing inside.
Could you help me?
#########EDIT
New code:
from Tkinter import *
import ScrolledText
class EntryDemo:
def __init__(self, rootWin):
self.textfield = ScrolledText.ScrolledText(rootWin)
#Add some text:
#self.textfield.delete(0,END)
self.textfield.insert(INSERT, "Change this text!")
self.textfield.pack()
self.button = Button(rootWin, text="Click Me!", command=self.clicked)
self.button.pack()
def clicked(self):
eText = self.textfield.get(1.0, END)
print(eText)
rootWin = Tk()
app = EntryDemo( rootWin )
rootWin.mainloop()
Sorry if it look like done with no effort by some down voters (even if I spent more than a day on it) but the Multi line text entry is not exactly what we can call well documented to learn by ourself.
Your first problem is that you commented out the app = EntryDemo( rootWin ) call, so you're not actually doing anything but creating a Tk() root window, then starting its main loop.
If you fix that, your next problem is that you're trying to use the ScrolledText module as if it were a class. You need the ScrolledText.ScrolledText class.
If you fix that, your next problem is that you're trying to delete from an empty text field, which is going to raise some kind of Tcl index error, and then you're also trying to insert at position 0 in an empty text field, which will raise the same error. There's no reason to do the delete at all, and for the insert you probably want to use INSERT as the position.
You still have multiple problems after that, but fixing these three will get your edit box up and displayed so you can start debugging everything else.
A working example based on your code. Note the comment above that both the file you import and the class within the file are named "ScrolledText"
from Tkinter import *
import ScrolledText
class EntryDemo:
def __init__(self, rootWin):
#Create a entry and button to put in the root window
self.textfield = ScrolledText.ScrolledText(rootWin)
self.textfield.pack()
#Add some text:
self.textfield.delete('1.0', END)
self.textfield.insert('insert', "Change this text!")
self.button = Button(rootWin, text="Click Me!",
command=self.clicked)
self.button.pack()
def clicked(self):
print("Button was clicked!")
eText = self.textfield.get('1.0', END)
print("The Entry has the following text in it:", eText)
self.textfield.delete('1.0', END)
rootWin = Tk()
app = EntryDemo( rootWin )
rootWin.mainloop()
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')