Tkinter GUI don't shows up when running in parallel with another infinity loop;i have tried Threading and multiprocessing techniques, whereas used GUI in main code and calling livespeech code or vice versa ; and defining both codes in function and calling from the main thread.But the problem remains; different result are attached below although u find it comment but have tried that method too,
#*********************************** IMPORTING MODULES*****************
import tkinter
from tkinter import*
import tkinter.messagebox
import sqlite3
import os
from multiprocessing import Process
from pocketsphinx import LiveSpeech, get_model_path
import threading
from time import sleep
model_path = get_model_path()
#*************** TKINTER GUI CODE******************
def gui():
window = tkinter.Tk()
window.title("Smart Notice Board")
top = Canvas(window,width=400,height=200)
top.pack(fill=X)
button_5 = Button(text='PORTAL SYSTEM', height = 2, width=17, activebackground = '#33B5e5', bg = 'brown', fg = 'white',command = portal )
top.create_window(80,80, anchor='nw', window = button_5)
def portal():
print("2")
#**************** speech TO text CODE***************
def speech():
speech = LiveSpeech(
verbose=False,
sampling_rate=16000,
buffer_size=2048,
no_search=False,
full_utt=False,
hmm=os.path.join(model_path, 'en-us'),
lm=os.path.join(model_path, '8582.lm'),
dic=os.path.join(model_path, '8582.dict')
)
for phrase in speech:
print(phrase)
a=str(phrase)
print(a)
#************************** MAIN LOOP************************
if __name__ == "__main__":
#************ FOR THREADING************
#thread1 = threading.Thread(target=gui)
#thread2 = threading.Thread(target=speech)
#thread1.daemon = True
#thread1.start()
#thread2.start()
#thread1.join()
#thread2.join()
#************ FOR MULTIPROCESSING****************
#processes=[]
#P1 = Process(target=gui)
#P2 = Process(target=speech)
#processes.append(P1)
#processes.append(P2)
#P2.daemon = True
# Will execute both in parallel
#P1.start()
#P2.start()
# Joins threads back to the parent process, which is this
# program
#P1.join()
#P2.join()
#****************** live speech code*************
window = tkinter.Tk()
window.title("Smart Notice Board")
top = Canvas(window,width=400,height=200)
top.pack(fill=X)
button_5 = Button(text='PORTAL SYSTEM', height = 2, width=17, activebackground = '#33B5e5', bg = 'brown', fg = 'white',command = portal )
top.create_window(80,80, anchor='nw', window = button_5)
IN multiprocessing case; no error but nothing works
This is the code I use to get mouse move event's cursor position from queue and act accordingly:
def check_mouse(self):
while True:
item = self.mouse.get_item()
if item is None:
break
else:
self.master.after_idle(self.mouse_move, *item)
self.master.after(INTERVAL, self.check_mouse)
and it's first time called just before mainloop call, with yet another self.master.after(INTERVAL, self.check_mouse).
So make you Tkinter GUI do its work in the mainloop and you should create another loop with a task that will run after INTERVAL (in miliseconds) and it will call itself every INTERVAL period after its job is finished.
Related
I am trying to create a python script that will trigger a tkinter window every time a certain event happens. The python script will have a while true loop and during the loop the tkinter event may or may not happen (if-else block). Right now the actual loop part isn't done, so I am currently testing the tkinter part but I can't seem to open more than tkinter window.
Below is the test script I am using.
from tkinter import *
from sys import exit
import os
onetwo = "C:/Users/I/Downloads/Transfer_Out_1016_Outlook.txt"
def popupError(s):
popupRoot = Tk()
##popupRoot.after(20000, exit)
popupButton = Button(popupRoot, text = s, font = ("Verdana", 12), bg = "yellow", command = lambda: os.system(onetwo))
popupButton.pack()
popupRoot.geometry('400x50+700+500')
popupRoot.mainloop()
popupError("HelloWORLD")
def popupTwo(s):
popupRoot = Tk()
##popupRoot.after(20000, exit)
popupButton = Button(popupRoot, text = s, font = ("Verdana", 12), bg = "yellow", command = lambda: os.system(onetwo))
popupButton.pack()
popupRoot.geometry('400x50+700+500')
popupRoot.mainloop()
popupTwo("HEWWWWWEWEWKOO")
I apologize for the lack of an actual piece of code but this is the best I can do right now given the dev status of the other parts of the overall python script.
Note that the tkinter window may be triggered more than once in a single loop session.
If any other details are needed, I'll try my best to add more in.
Here’s what you can do:
from tkinter import *
def popup(winName):
newWin = Toplevel()
btn2 = Button(newWin, text=winName)
btn2.pack()
root = Tk()
btn = Button(root, text=“Popup”, command=lambda: popup(“text”))
btn.pack()
root.mainloop()
The purpose is that input fn takes string input and pass it into GUI fn which runs the condition and ammend tkinter window accordingly.
#*********************************** IMPORTING MODULES*****************
import tkinter
from tkinter import*
import tkinter.messagebox
import sqlite3
import os
import threading
from time import sleep
from input import*
conn = sqlite3.connect('portal.db')
c = conn.cursor()
global a
#*************** TKINTER GUI CODE******************
def gui(a):
window = tkinter.Tk()
window.title("Smart Notice Board")
#********************** FRAMES OF MAIN WINDOW(HOME)******************
top = Canvas(window,width=1024,height=184)
top.pack(fill=X)
middle = Canvas(window, width=1024, height=450, bg='steelblue')
middle.pack(fill=X)
main_left = Canvas(middle, width=275, height=450, bg='lightgreen')
main_left.pack(side=LEFT)
main_right = Canvas(middle, width=800, height=450, bg='steelblue')
main_right.pack(side=RIGHT)
bottom = Canvas(window, width=1024, height=70, bg='black')
bottom.pack(fill=X)
#************************** IMAGES********************
i_top = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\BG.png')
top.create_image(0,10, anchor=tkinter.NW,image = i_top)
i_right = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\aus1.png')
main_right.create_image(0,0, anchor=tkinter.NW,image = i_right)
#i_left = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\widget1.png')
#main_left.create_image(0,0, anchor=tkinter.NW,image = i_left)
t1 = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\first.png')
t2 = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\BG.png')
#***************** TIMETABLE IMAGE VIEWING FN***********************
def home():
main_right.create_image(0,0, anchor=tkinter.NW,image = t2)
#*********************** TIMETABLE BUTTON PRESS FN*************************
def timetable():
main_right.create_image(0,0, anchor=tkinter.NW,image = t1)
#******************************* CONDITIONS**********************
if a == "NULL":
timetable()
if a == "HOME":
home()
#*********************** MAIN MENU BUTTONS****************
button_1 = Button(text = ' HOME', anchor = 'w', height = 2, width = 8,activebackground = '#33B5e5',bg = 'brown',fg = 'white',command = home)
top.create_window(2,150,anchor = 'nw', window = button_1)
button_2 = Button(text='TIMETABLE', height = 2, width=12, activebackground = '#33B5e5', bg = 'brown', fg = 'white',command = timetable)
top.create_window(75,150, anchor='nw', window = button_2)
window.mainloop()
#************************** MAIN LOOP************************
if __name__ == "__main__":
#print(valuea())
a=valuea()
gui(a)
Now what I want is continuously run that thing and update Tkinter window; but the 2btn fn only takes it one time and pass it into GUI fn which runs tkinter and it stucks on window.mainloop as tkinter is infinity loop.
Please suggest me a solution also u can run this code by only setting pictures from your computer
The standard method to run code regularly in the mainloop is to register a timeout function using the after method of the root window.
But, whatever you do in such a timeout function (and indeed in all other callbacks), it should not block, because that would lock up the mainloop! So you cannot use input. But you could read from sys.stdin, which is a io.TextIOWrapper instance.
You could use input in a second thread. But since Tkinter isn't thread-safe, that second thread should not use Tkinter functions or methods. So you should not simply update a label from the second thread. You could save/append the input to a global variable, but you'd have to protect that with a lock or mutex. And you'd need to use a timeout function in the main Tkinter thread to test if the lock or mutex is released by the second thread so the Tkinter thread can claim it and access the data. As you can see this is really complicated. So mixing Tkinter and threads is generally not recommended.
In order to ammend tkinter window and show the text and open picture.I did slight change in the input.py file;and coded as below
def vala():
a=speech()
if a == "HOME":
home()
if a == "NULL":
timetable()
window.after(1000,vala)
what I have done is I convert it into recursive fn that call itself after 1000ms. In this way, it can be done.
Basically, I have done it with Pocketsphinx as Input (i.e. my project takes speech input and open file/image in tkinter screen and it runs continuously)
I want to interface pocketsphinx livespeech with Python tkinter GUI in such a way that GUI is visible on frontend and Livespeech works on Back-end.But when i merge tkinter code with livespeech code; livespeech code always runs first and GUI not shows till i stop the code;so i won't be able to perform my required task..,
#*********************************** IMPORTING MODULES*****************
import tkinter
from tkinter import*
import tkinter.messagebox
import sqlite3
import os
from pocketsphinx import LiveSpeech, get_model_path
conn = sqlite3.connect('portal.db')
c = conn.cursor()
window = tkinter.Tk()
window.title("Smart Notice Board")
top = Canvas(window,width=400,height=200)
top.pack(fill=X)
def portal():
print("2")
button_5 = Button(text='PORTAL SYSTEM', height = 2, width=17, activebackground = '#33B5e5', bg = 'brown', fg = 'white',command = portal )
top.create_window(80,80, anchor='nw', window = button_5)
#**************** TEXT TO SPEECH CODE***************
model_path = get_model_path()
speech = LiveSpeech(
verbose=False,
sampling_rate=16000,
buffer_size=2048,
no_search=False,
full_utt=False,
hmm=os.path.join(model_path, 'en-us'),
lm=os.path.join(model_path, '8582.lm'),
dic=os.path.join(model_path, '8582.dict')
)
for phrase in speech:
print(phrase)
a=str(phrase)
if a == "HOME":
print('ok')
portal()
print('1')
results are attached below;
Only live speech runs
GUI opens when code exit
I'm new to python coding and I have been working on a project which could click on an image based on a chosen color. I have been using a program which loops the search 50 times when I click the start button. However, I have been trying to implement a stop button, but the problem is that my code freezes when the loop is running. Any ideas?
I have heard to try threading but it seems very complicated and I have been unable to follow any tutorials properly in relation to my code. By the way, the image searched has been testing images I've been using stored inside the program files.
from imagesearch import *
import pyautogui
import tkinter as tk
from tkinter import *
from tkinter.ttk import *
import time
import threading
# ---Defined Programs---
def run():
global enterColor
enterColor = str(enterColorField.get())
program(enterColor)
def program(color):
whitePos = imagesearch_numLoop(str(color) + ".PNG", 0, 50)
pyautogui.moveTo(whitePos[0] + 20, whitePos[1] + 10)
pyautogui.click()
def stop():
print("Placeholder")
# ---Main Runner---
window = tk.Tk()
window.geometry("250x250")
window.configure(background="#181b54")
app = tk.Frame(window)
app.grid()
enterColorLabel = tk.Label(window, text="Enter Color:", bg="#181b54", fg="white")
enterColorLabel.place(x=10, y=50)
enterColorField = Combobox(window)
enterColorField['values'] = ("Black", "White")
enterColorField.current("0") # set the selected item
enterColorField.place(x=10, y=70)
submitButton = tk.Button(window, text="Start", bg="#66ff00", command=run)
submitButton.place(x=10, y=130)
stopButton = tk.Button(window, text="Stop", bg="red", command=stop)
stopButton.place(x=50, y=130)
window.mainloop()
#---New Python Script---
import cv2
import numpy as np
import pyautogui
import random
import time
def imagesearch_numLoop(image, timesample, maxSamples, precision=0.8):
pos = imagesearch(image, precision)
count = 0
while pos[0] == -1:
print(image+" not found, waiting")
count = count + 1
if count>maxSamples:
break
pos = imagesearch(image, precision)
return pos
Whenever clicking start, the whole code freezes. I can't even (x) out.
Here's a hopefully simple multiprocessing recipe that will work for you. We'll have three main functions. The first will be an example loop that you would put your processing inside. I included arguments in the function to show you that it's possible to pass args and kwargs while using multiprocessing.
def loop(a, b, c, d):
# Will just sleep for 3 seconds.. simulates whatever processing you do.
time.sleep(3)
return
Next is a function we will use to queue the multiprocessing process.
def queue_loop():
p = multiprocessing.Process(target = loop,
args = (1, 2),
kwargs = {"c": 3, "d": 4})
# You can pass args and kwargs to the target function like that
# Note that the process isn't started yet. You call p.start() to activate it.
p.start()
check_status(p) # This is the next function we'll define.
return
Then, you may be interested in knowing the status of your process throughout its execution. For example it is sometimes desirable to disable certain buttons while a command is being run.
def check_status(p):
""" p is the multiprocessing.Process object """
if p.is_alive(): # Then the process is still running
label.config(text = "MP Running")
mp_button.config(state = "disabled")
not_mp_button.config(state = "disabled")
root.after(200, lambda p=p: check_status(p)) # After 200 ms, it will check the status again.
else:
label.config(text = "MP Not Running")
mp_button.config(state = "normal")
not_mp_button.config(state = "normal")
return
Throwing this all together into one snippet:
import tkinter as tk
import multiprocessing
import time
def loop(a, b, c, d):
# Will just sleep for 3 seconds.. simulates whatever processing you do.
time.sleep(3)
return
def queue_loop():
p = multiprocessing.Process(target = loop,
args = (1, 2),
kwargs = {"c": 3, "d": 4})
# You can pass args and kwargs to the target function like that
# Note that the process isn't started yet. You call p.start() to activate it.
p.start()
check_status(p) # This is the next function we'll define.
return
def check_status(p):
""" p is the multiprocessing.Process object """
if p.is_alive(): # Then the process is still running
label.config(text = "MP Running")
mp_button.config(state = "disabled")
not_mp_button.config(state = "disabled")
root.after(200, lambda p=p: check_status(p)) # After 200 ms, it will check the status again.
else:
label.config(text = "MP Not Running")
mp_button.config(state = "normal")
not_mp_button.config(state = "normal")
return
if __name__ == "__main__":
root = tk.Tk()
mp_button = tk.Button(master = root, text = "Using MP", command = queue_loop)
mp_button.pack()
label = tk.Label(master = root, text = "MP Not Running")
label.pack()
not_mp_button = tk.Button(master = root, text = "Not MP", command = lambda: loop(1,2,3,4))
not_mp_button.pack()
root.mainloop()
The result is that when you click the "Using MP" button, the command buttons will be disabled and the process will be started without freezing your UI. Clicking the "Not MP" button will start the function like 'normal' and will freeze your UI as you noticed in your own code.
A simple answer is you cannot use while loop in GUI design.
But you can use the method .after(delay, callback=None) instead.
Here is an example:
from tkinter import *
root = Tk()
def loop():
print("Hi!")
root.after(1000, loop) # 1000 is equal to 1 second.
root.after(1000, loop) # This line is to call loop() in 1 second.
root.mainloop()
Middle aged dad (electrical engineer not programmer by trade) trying to teach my 13 year old daughter electronics and programming. So far, I love Python. I am building a program to display temperatures throughout our house with tkinter GUI and DS18B20 sensors.
We've pieced together the program below from reading books, online research and using Stack Overflow for trouble-shooting bugs (this site rocks!).
Now we're stumped, we keep getting an intermittent error, when we run the program the first time after loading idle on our Raspberry, it works fine.
The second time, and all subsequent times, we get this error message:
Traceback (most recent call last):
File "/home/pi/Code-working-library/stackoverflow-paste.py", line 140, in <module>
app.equipTemp.set(tempread)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 203, in set
return self._tk.globalsetvar(self._name, value)
RuntimeError: main thread is not in main loop
Note, our understanding is that in order to have a static window and update labels updated temps read off our sensor (DS18B20) we needed to use a thread. The sample code we started with has the _init_ statements with only one underscore preceding and trailing - not sure why, if I add a second underscore, I get error messages. The updating window code we used as our basis came from Raspberry Pi forum
Here is our code:
from Tkinter import *
import tkFont
import os
import glob
import time
import subprocess
import re
import sys
import time
import threading
import Image
import ImageTk
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
#28-000005c6ba08
sensors = ['28-000005c6ba08']
sensors1 = ['28-000005c70f69']
def read_temp_raw():
catdata = subprocess.Popen(['cat',device_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out,err = catdata.communicate()
out_decode = out.decode('utf-8')
lines = out_decode.split('\n')
return lines
def read_temp():
lines = read_temp_raw()
while lines[0].strip()[-3:] != 'YES':
time.sleep(0.2)
lines = read_temp_raw()
equals_pos = lines[1].find('t=')
if equals_pos != -1:
temp_string = lines[1][equals_pos+2:]
temp_c = float(temp_string) / 1000.0
temp_f = temp_c * 9.0 / 5.0 + 32.0
return temp_f
########### build window ###################
bground="grey"
class App(threading.Thread):
def _init_(self):
threading.Thread._init_(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
#Make the window
self.root = Tk()
self.root.wm_title("Home Management System")
self.root.minsize(1440,1000)
self.equipTemp = StringVar()
self.equipTemp1 = StringVar()
self.equipTemp2 = StringVar()
self.customFont = tkFont.Font(family="Helvetica", size=16)
# 1st floor Image
img = Image.open("HOUSE-PLANS-01.png")
photo = ImageTk.PhotoImage(img)
Label1=Label(self.root, image=photo)
Label1.place(x=100, y=100)
# 2nd floor
img2 = Image.open("HOUSE-PLANS-02.png")
photo2 = ImageTk.PhotoImage(img2)
Label1=Label(self.root, image=photo2)
Label1.place(x=600, y=100)
# Basement image
img3 = Image.open("HOUSE-PLANS-03.png")
photo3 = ImageTk.PhotoImage(img3)
Label1=Label(self.root, image=photo3)
Label1.place(x=100, y=500)
# Attic Image
img4 = Image.open("HOUSE-PLANS-04.png")
photo4 = ImageTk.PhotoImage(img4)
Label1=Label(self.root, image=photo4)
Label1.place(x=600, y=500)
# House Isometric Image
img5 = Image.open("house-iso.png")
photo5 = ImageTk.PhotoImage(img5)
Label1=Label(self.root, image=photo5)
Label1.place(x=1080, y=130)
#Garage Temp Label
Label2=Label(self.root, textvariable=self.equipTemp, width=6, justify=RIGHT, font=self.customFont)
Label2.place(x=315, y=265)
print "start monitoring and updating the GUI"
self.root.mainloop() #start monitoring and updating the GUI
########### Start Loop ###################
print "starting app"
app = App()
app.start()
print "app started"
################### Begin ds18b20 function ##############
while True:
# 28-000005c6ba08
i = "28-000005c6ba08"
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + i)[0]
device_file = device_folder + '/w1_slave'
tempread=round(read_temp(),1)
app.equipTemp.set(tempread)
time.sleep(5)
##################### END ds18b20 Function ######
You need to run the GUI code in the main thread, and your temperature reading code needs to be in the background thread. It's only safe to update the GUI in the main thread, so you can pass the temperature data you're reading from the background thread back to the main thread via a Queue, and have the main thread periodically check for data in the queue using self.root.after():
from Tkinter import *
import tkFont
import os
import glob
import time
import threading
import Image
import Queue
def update_temp(queue):
""" Read the temp data. This runs in a background thread. """
while True:
# 28-000005c6ba08
i = "28-000005c6ba08"
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + i)[0]
device_file = device_folder + '/w1_slave'
tempread=round(read_temp(),1)
# Pass the temp back to the main thread.
queue.put(tempread)
time.sleep(5)
class Gui(object):
def __init__(self, queue):
self.queue = queue
#Make the window
self.root = Tk()
self.root.wm_title("Home Management System")
self.root.minsize(1440,1000)
self.equipTemp = StringVar()
self.equipTemp1 = StringVar()
self.equipTemp2 = StringVar()
self.customFont = tkFont.Font(family="Helvetica", size=16)
# 1st floor Image
img = Image.open("HOUSE-PLANS-01.png")
photo = ImageTk.PhotoImage(img)
Label1=Label(self.root, image=photo)
Label1.place(x=100, y=100)
# 2nd floor
img2 = Image.open("HOUSE-PLANS-02.png")
photo2 = ImageTk.PhotoImage(img2)
Label1=Label(self.root, image=photo2)
Label1.place(x=600, y=100)
# Basement image
img3 = Image.open("HOUSE-PLANS-03.png")
photo3 = ImageTk.PhotoImage(img3)
Label1=Label(self.root, image=photo3)
Label1.place(x=100, y=500)
# Attic Image
img4 = Image.open("HOUSE-PLANS-04.png")
photo4 = ImageTk.PhotoImage(img4)
Label1=Label(self.root, image=photo4)
Label1.place(x=600, y=500)
# House Isometric Image
img5 = Image.open("house-iso.png")
photo5 = ImageTk.PhotoImage(img5)
Label1=Label(self.root, image=photo5)
Label1.place(x=1080, y=130)
#Garage Temp Label
Label2=Label(self.root, textvariable=self.equipTemp, width=6, justify=RIGHT, font=self.customFont)
Label2.place(x=315, y=265)
print "start monitoring and updating the GUI"
# Schedule read_queue to run in the main thread in one second.
self.root.after(1000, self.read_queue)
def read_queue(self):
""" Check for updated temp data"""
try:
temp = self.queue.get_nowait()
self.equipTemp.set(temp)
except Queue.Empty:
# It's ok if there's no data to read.
# We'll just check again later.
pass
# Schedule read_queue again in one second.
self.root.after(1000, self.read_queue)
if __name__ == "__main__":
queue = Queue.Queue()
# Start background thread to get temp data
t = threading.Thread(target=update_temp, args=(queue,))
t.start()
print "starting app"
# Build GUI object
gui = Gui(queue)
# Start mainloop
gui.root.mainloop()
Edit:
After actually taking a look at the tkinter source code, as well as the Python bug tracker, it appears that unlike almost every other GUI library out there, tkinter is intended to be thread-safe, as long you run the mainloop in the main thread of the application. See the answer I added here for more info, or go straight to the resolved issue about tkinter's thread safety on the Python bug tracker here. If the tkinter source and Python's bug tracker are correct, that would mean that as long as you run the mainloop in the main thread, you can happily call gui.equipTemp.set() directly from your temperature reading thread - no Queue required. And in my testing, that did indeed work just fine.
GUI toolkits are not threadsafe. You can only built and change your GUI from the main thread.
Since reading the temperature does not take that long, you can remove all the threading code and use the after-method from Tk.
Your read_temp_raw function is very complicated:
def read_temp_raw():
with open(device_file) as temp:
return temp.read().split('\n')