I have recently started programming and written a fairly simple program. But stuck at a point. My program does a very simple thing that when you click a button it will say a line and shows that text on the screen. But it is speaking the text first and then displays it on the screen but I want its reverse, i.e it should first display it on the screen and then speak the text.
from tkinter import *
import pyttsx3
root = Tk()
def speak():
engine = pyttsx3.init()
say = "I am speaking."
text.insert("0.0", say)
engine.say(say)
engine.runAndWait()
text = Text(root)
text.pack()
btn = Button(root, text="speak", command=speak)
btn.pack()
root.mainloop()
Thanks in advance.
It is because engine.runAndWait() blocks the application, and so text box cannot be updated by tkinter mainloop until the speaking completed.
You can call text.update_idletasks() to force tkinter to update the text box before the speaking:
engine = pyttsx3.init() # initialize only once
def speak():
say = "I am speaking."
text.insert("0.0", say)
text.update_idletasks() # force tkinter update
engine.say(say)
engine.runAndWait()
You could delay the call to engine.say
from tkinter import *
import pyttsx3
def speak(sentence):
engine.say(sentence)
engine.runAndWait()
def display_and_speak():
sentence = "I am speaking."
text.insert("1.0", sentence)
root.after(1000, speak, sentence) # call to speak delayed 1 second
engine = pyttsx3.init()
root = Tk()
text = Text(root)
text.pack()
btn = Button(root, text="speak", command=display_and_speak)
btn.pack()
root.mainloop()
Related
So I am making a project which has numerous buttons and windows using Tkinter and I implemented tts using pyttsx3 module. Consider the following example:
import pyttsx3
from Tkinter import *
#configure text-to-speech
engine = pyttsx3.init()
def speak(text):
engine.setProperty("rate", 150)
engine.say(text)
engine.runAndWait()
root = Tk()
def submit():
top = Toplevel(root)
hello_world = Label(top, text = "Hello World")
hello_world.pack()
speak("Hello World")
click_me = Button(root, text = "Click Me", command = submit)
click_me.pack()
root.mainloop()
When the button is clicked the tts command will run first and the window will freeze until pyttsx3 finishes speaking and then only the window will load. And when there is a lot of text to speak this problem worsens.
What I want is that the tts command should execute after the window is loaded. How should I do this?
I had the exact same problem, I tried everything including threading
But the method
Root.after(2000, speak)
Will definitely work.
The number is in milliseconds.
Environment: macOS Catalina, Python 3.7.4, Tcl/Tk 8.6.9, VSC 1.39.1
I have a situation where I am using a bar/qr code scanner to provide a string to a ttk.Entry() method, which then fires off a function.
The reader is seen by the OS as an HID keyboard, so the text from the QR code is received by the ttk.Entry() widget that I give focus to during code execution. I have bound the widget to the key because the scanner sends a cr/lf at the end of the text string, which works as needed.
However, I am running into an issue where if the qr code lingers over the scanner too long it will rescan the qr code and the widget receives the qr code text again, which then causes it to be processed again.
I have tried disabling the ttk.Entry() in the function, deleting the widget contents, and removing focus to no avail. The behavior I'm seeing is occurring even though the widget is disabled and does not have focus, it is still getting input and executing the function again if the scanner rescans the qr code while the function is executing.
In this first example, I simply tried to disable the widget, but that doesn't work. The widget still gets the later scans while in the function.
# test-ttk-entry1.py
import time
import tkinter as tk
from tkinter import StringVar, ttk
root = tk.Tk()
def print_text(event):
global kbEntry
textValue = kbEntry.get()
kbEntry.configure(state="disabled")
time.sleep(2) # Add in a delay to allow for repeat scan
print(textValue)
time.sleep(2) # Add in a delay to allow for repeat scan
kbEntry.configure(state="active")
kbText = StringVar()
kbEntry = ttk.Entry(root, width=10, textvariable=kbText)
kbEntry.bind("<Return>", print_text)
kbEntry.pack()
kbEntry.focus_set()
root.mainloop()
The second attempt was to disable the entry widget and upon making it active again delete the text in the field.
# test-ttk-entry2.py
import time
import tkinter as tk
from tkinter import END, StringVar, ttk
root = tk.Tk()
def print_text(event):
global kbEntry
textValue = kbEntry.get()
kbEntry.delete(0, END)
kbEntry.configure(state="disabled")
time.sleep(2) # Add in a delay to allow for repeat scan
print(textValue)
time.sleep(2) # Add in a delay to allow for repeat scan
kbEntry.configure(state="active")
kbEntry.delete(0, END)
kbText = StringVar()
kbEntry = ttk.Entry(root, width=10, textvariable=kbText)
kbEntry.bind("<Return>", print_text)
kbEntry.pack()
kbEntry.focus_set()
root.mainloop()
And finally, I was reading about taking focus from a widget and giving focus to the root window, so I added that in and it still prints multiple times to the console like there is a keyboard buffer being read by the ttk.Entry() widget. The weird thing is it seems like widgets don't normally respond to any calls to methods when they are disabled, but it appears the ttk.Entry() widget's properties/attributes (excuse me if my OOP terms are not correct) can be manipulated while disabled.
# test-ttk-entry2.py
import time
import tkinter as tk
from tkinter import END, StringVar, ttk
root = tk.Tk()
def print_text(event):
global kbEntry
textValue = kbEntry.get()
kbEntry.delete(0, END)
kbEntry.configure(state="disabled")
root.focus_set()
time.sleep(2) # Add in a delay to allow for repeat scan
print(textValue)
time.sleep(2) # Add in a delay to allow for repeat scan
kbEntry.configure(state="active")
kbEntry.delete(0, END)
kbEntry.focus_set()
kbText = StringVar()
kbEntry = ttk.Entry(root, width=10, textvariable=kbText)
kbEntry.bind("<Return>", print_text)
kbEntry.pack()
kbEntry.focus_set()
root.mainloop()
So how can I prevent the ttk.Entry() widget from accepting any input from the HID/keyboard device while my function is executing?
Since I am unable to find a way to temporarily disable the .Entry() widget programmatically to keep it from reading successive inputs, I have come up with this solution:
# test-ttk-entry2.py
import time
import tkinter as tk
from tkinter import END, StringVar, ttk
root = tk.Tk()
tempStr = "" # Global temporary string variable to trip repeated scans
def print_text(event):
global tempStr
textValue = kbEntry.get()
if tempStr == textValue:
kbEntry.delete(0, END)
print ("Duplicate scan")
return
tempStr = textValue
kbEntry.delete(0, END)
print(textValue)
time.sleep(3) # Simulate the time it takes for the function to complete
kbEntry = ttk.Entry(root, width=10)
kbEntry.bind("<Return>", print_text)
kbEntry.grid(row=0, column=0, padx=10, pady=10)
kbEntry.focus_set()
root.mainloop()
from tkinter import *
def printSomething():
inputValue=textBox.get("1.0","end-1c")
res=response(inputValue)
label = Label(root, text=res)
#this creates a new label to the GUI
label.pack()
root = Tk()
button = Button(root, text="Print Me", command=printSomething)
button.pack()
textBox=Text(root, height=2, width=10)
textBox.pack()
root.mainloop()
I have written a python code that returns text. and print that in tkinter label.while i try to execute it shows "None" in label.
It would probably be better to create the label in the global namespace once and then just update the label every time you press the button.
I also recommend using import tkinter as tk vs from tkinter import * as it provides better maintainability as your code grows and you do not end up overwriting built in methods.
I have updated your code and changed a few things to better fit the PEP8 standard.
import tkinter as tk
def print_something():
label.config(text=text_box.get("1.0", "end-1c"))
root = tk.Tk()
tk.Button(root, text="Print Me", command=print_something).pack()
text_box = tk.Text(root, height=2, width=10)
text_box.pack()
label = tk.Label(root)
label.pack()
root.mainloop()
Just changing your line:
res = response(inputValue)
to
res = inputValue
worked for me, creating a new label every time I pressed the button.
I have this problem: I created this program with Python3 and Tkinter, that show the picture of a place (like mountain or sea) and the program speak what is the place shown in the picture. The problem is when the picture change, it only appears for a few moments, just the time the program say what place is, and the disappear and remain only the label where it is written what represent the picture.
This is the code:
from tkinter import *
from PIL import Image,ImageTk
import pyttsx3
root = Tk()
root.title("Try change image")
root.geometry("1000x600")
root.resizable(height=FALSE,width=FALSE)
Leftframe = Frame(root)
Leftframe.pack(side = LEFT)
Rightframe = Frame(root)
Rightframe.pack(side=RIGHT)
engine = pyttsx3.init()
voices = engine.getProperty('voices')
rate = engine.getProperty('rate')
engine.setProperty('rate', rate -20)
for voice in voices:
engine.setProperty('voice',voice.id)
def start():
global back_label,label
back = ImageTk.PhotoImage(file=directory mountain picture)
back_label = Label(Leftframe,image = back)
back_label.pack()
label=Label(Rightframe,text="Mountain")
label.pack()
root.update()
engine.say("Mountain")
engine.runAndWait()
def change():
back_label.destroy()
label.destroy()
back = ImageTk.PhotoImage(file=directory sea picture)
back_label1 = Label(Leftframe,image = back)
back_label1.pack()
label1 = Label(Rightframe,text="Sea")
label1.pack()
root.update()
engine.say("Sea")
engine.runAndWait()
start()
change()
root.mainloop()
I hope you understand what I want to do, every help will be appreciated
I know that it's often best practice to write Tkinter GUI code using object-oriented programming (OOP), but I'm trying to keep things simple because I'm new to Python.
I have written the following code to create a simple GUI:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
def ChangeLabelText():
MyLabel.config(text = 'You pressed the button!')
def main():
Root = Tk()
MyLabel = ttk.Label(Root, text = 'The button has not been pressed.')
MyLabel.pack()
MyButton = ttk.Button(Root, text = 'Press Me', command = ChangeLabelText)
MyButton.pack()
Root.mainloop()
if __name__ == "__main__": main()
The GUI looks like this.
I thought the text in the GUI (MyLabel) would change to "You pressed the button!" when the button is clicked, but I get the following error when I click the button:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\elsey\AppData\Local\Programs\Python\Python35-32\lib\tkinter\__init__.py", line 1549, in __call__
return self.func(*args)
File "C:/Users/elsey/Documents/question code.py", line 6, in ChangeLabelText
MyLabel.config(text = 'You pressed the button!')
NameError: name 'MyLabel' is not defined
What am I doing wrong? Any guidance would be appreciated.
MyLabel is local to main() so the way you can not access it that way from ChangeLabelText().
If you do not want to change the design of your program, then you will need to change the definition of ChangeLabelText() like what follows:
def ChangeLabelText(m):
m.config(text = 'You pressed the button!')
And withing main() you will need to pass MyLabel as an argument to ChangeLabelText().
But again, you will have a problem if you code this command = ChangeLabelText(MyLabel) when you declare and define MyButton because the program will execute directly the body of ChangeLabelText() at the start and you will not have the desired result.
To resolve this later problem, you will have to use (and may be read about) lambda
Full program
So your program becomes:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
def ChangeLabelText(m):
m.config(text = 'You pressed the button!')
def main():
Root = Tk()
MyLabel = ttk.Label(Root, text = 'The button has not been pressed.')
MyLabel.pack()
MyButton = ttk.Button(Root, text = 'Press Me', command = lambda: ChangeLabelText(MyLabel))
MyButton.pack()
Root.mainloop()
if __name__ == "__main__":
main()
Demo
Before clicking:
After clicking:
Are your sure you don't want to do it as a class (i think it makes the code a bit more clean as your project grows)? Here is a way to accomplish what you'e looking for:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
class myWindow:
def __init__(self, master):
self.MyLabel = ttk.Label(root, text = 'The button has not been pressed.')
self.MyLabel.pack()
self.MyButton = ttk.Button(root, text = 'Press Me', command = self.ChangeLabelText)
self.MyButton.pack()
def ChangeLabelText(self, event=None):
self.MyLabel.config(text = 'You pressed the button!')
if __name__ == "__main__":
root = Tk()
mainWindow = myWindow(root)
root.mainloop()
In a Mac, is looks like this before pressing the button:
And when you press it:
But basically, in order to be able to change the text in a Label or a button, you need to ensure it has an active reference. In this case, we are doing it by creating the window as a class and referencing the widgets in the form self. widget_name = widget().
but I'm trying to keep things simple because I'm new to Python Hopefully this helps in understanding that classes are the simple way, otherwise you have to jump through hoops and manually keep track of many variables. Also, the Python Style Guide suggests that CamelCase is used for class names and lower_case_with_underlines for variables and functions. https://www.python.org/dev/peps/pep-0008/
from tkinter import *
from tkinter import ttk
class ChangeLabel():
def __init__(self):
root = Tk()
self.my_label = ttk.Label(root, text = 'The button has not been pressed.')
self.my_label.pack()
## not necessary to keep a reference to this button
## because it is not referenced anywhere else
ttk.Button(root, text = 'Press Me',
command = self.change_label_text).pack()
root.mainloop()
def change_label_text(self):
self.my_label.config(text = 'You pressed the button!')
if __name__ == "__main__":
CL=ChangeLabel()