Main thread is not in main loop - python

I am trying to ping and get the result of 100+ IPs within 1-2 sec.But running this code using Tkinter is giving me the following error please help me with this.
RuntimeError: main thread is not in main loop
Attaching the code. Please have a look at it and let me know if any other information is needed
Thank you.
import tkinter.messagebox
from tkinter import ttk
import os
import socket
import sys
import subprocess
import re
import threading
from tkinter import *
import multiprocessing.dummy
import multiprocessing
class Demo1:
data=[]
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.pack()
def new_window(self):
self.inputValue=self.t.get("1.0",'end-1c')
Demo1.data=self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2(Demo1):
t1=[]
s1=True
display=[]
def __init__(self, master):
self.master=master
self.kas(master)
def kas(self,master):
Demo2.t1=Demo1.data
self.master = master
cols = ('IP','Ping status')
self.listBox = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.listBox.heading(col, text=col)
self.listBox.column(col,minwidth=0,width=170)
self.listBox.column('#0',width=50)
self.listBox.grid(row=1, column=0, columnspan=2)
self.ping_range(Demo2.t1)
def ping_func(self,ip):
p=[]
pingCmd = "ping -n 1 -w 1000 " + ip
childStdout = os.popen(pingCmd)
result = (childStdout.readlines())
childStdout.close()
p.append(ip)
if (any('Reply from' in i for i in result)) and (any('Destination host unreachable' not in i for i in result)):
p.append("sucess")
else:
p.append("failed")
for i,(a) in enumerate(p):
self.listBox.insert('', 'end',value=(a))
return result
def ping_range(self,ip_list):
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in ip_list])
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
I have tried the code using queue and after(). but still getting the same error. not have much idea on these please guide me where i am going wrong and what can i do to correct it. Thank you
import tkinter.messagebox
from tkinter import ttk
import os
import socket
import sys
import subprocess
import re
import threading
import multiprocessing.dummy
import multiprocessing
import time, queue
class Demo1:
data=[]
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.pack()
def new_window(self):
self.inputValue=self.t.get("1.0",'end-1c')
Demo1.data=self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2(Demo1):
t1=[]
s1=True
display=[]
def __init__(self, master):
self.master=master
self.kas(master)
def kas(self,master):
self.running = True
self.queue = queue.Queue() #queue
Demo2.t1=Demo1.data
self.master = master
cols = ('IP','Ping status')
self.listBox = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.listBox.heading(col, text=col)
self.listBox.column(col,minwidth=0,width=170)
self.listBox.column('#0',width=50)
self.listBox.grid(row=1, column=0, columnspan=2)
#self.ping_range(Demo2.t1)
self.running = True
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map(self.ping_func, [x for x in Demo2.t1])
def ping_func(self,ip):
while self.running:
pi=[]
pingCmd = "ping -n 1 -w 1000 " + ip
childStdout = os.popen(pingCmd)
result = (childStdout.readlines())
childStdout.close()
pi.append(ip)
if (any('Reply from' in i for i in result)) and (any('Destination host unreachable' not in i for i in result)):
pi.append("sucess")
else:
pi.append("failed")
self.queue.put(pi) #Thread value to queue
m = self.queue.get_nowait()
print(m) #getting the correct value but after this statement, getting error as main thread is not in main loop
for i,(a) in enumerate(m):
self.listBox.insert('', 'end',value=(a))
self.periodic_call()
def periodic_call(self):
self.master.after(200, self.periodic_call) #checking its contents periodically
self.ping_func()
if not self.running:
import sys
sys.exit(1)
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()

The reason you're still getting the RuntimeError even with the changes, is because you're is still trying to update the GUI from a thread — the one running the ping_func() method — that is not the same one running the tkinter GUI (which is a required because it doesn't support multithreading).
To fix that I have split ping_func() up into two separate parts, one the runs the ping command in another process and appends the results to the queue, and another that updates the GUI — the latter now being done in new method I added named process_incoming() (similar to the example to which I referred you).
Also note that the Demo2 class is no longer a subclass of Demo1 since there's no reason to do so (and it might confuse matters). I also changed the self.listBox attribute to self.treeview because that's what it is.
Although those changes alone would avoid the RuntimeError, in theory the GUI could still "freeze" until all the tasks completed because the pool.map() function blocks until all the tasks have completed, which could interfere with tkinter's mainloop() depending on how long that takes. To avoid that I changed pool.map() to pool.async() — which doesn't block because it's unnecessary given the Queue's contents are repeatedly polled.
import multiprocessing.dummy
import multiprocessing
import os
import socket
import sys
import subprocess
import re
import time
import threading
import tkinter.messagebox
from tkinter import ttk
import queue
class Demo1:
data = []
def __init__(self, master):
self.master = master
self.label=tkinter.Label(text="Add IP/Hostname")
self.label.pack()
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.pack()
self.button = tkinter.Button(self.master,height=3,width=10, text="OK",
command=self.new_window)
self.button.pack()
def new_window(self):
self.inputValue = self.t.get("1.0",'end-1c')
Demo1.data = self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Tk() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.configure(bg='#6EBFE4')
self.master.mainloop()
class Demo2:
t1 = []
s1 = True
display = []
def __init__(self, master):
self.master = master
self.kas(master)
def kas(self,master):
self.running = True
self.queue = queue.Queue()
Demo2.t1 = Demo1.data
self.master = master
cols = ('IP','Ping status')
self.treeview = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.treeview.heading(col, text=col)
self.treeview.column(col,minwidth=0,width=170)
self.treeview.column('#0',width=50)
self.treeview.grid(row=1, column=0, columnspan=2)
num_threads = 5 * multiprocessing.cpu_count()
p = multiprocessing.dummy.Pool(num_threads)
p.map_async(self.ping_func, [x for x in Demo2.t1 if x])
self.periodic_call() # Start polling the queue for results.
def ping_func(self, ip):
pi = []
pingCmd = "ping -n 1 -w 1000 " + ip
with os.popen(pingCmd) as childStdout:
result = childStdout.readlines()
pi.append(ip)
if(any('Reply from' in i for i in result)
and any('Destination host unreachable' not in i for i in result)):
pi.append("success")
else:
pi.append("failed")
self.queue.put(pi) #Thread value to queue
def process_incoming(self):
""" Process any messages currently in the queue. """
while self.queue.qsize():
try:
msg = self.queue.get_nowait()
print(msg)
self.treeview.insert('', 'end', value=(msg)) # Update GUI.
except queue.Empty: # Shouldn't happen.
pass
def periodic_call(self):
self.master.after(200, self.periodic_call) # checking its contents periodically
self.process_incoming()
if not self.running:
import sys
sys.exit(1)
def main():
root = tkinter.Tk()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()

Related

How to start a thread to update network data in tkinter when window opens without using a button

The following code updates a label with the price of BTCUSDT downloaded from the Binance public API. The thread is started with a button, but I can't work out how to start it without the button, when the window opens.
from tkinter import *
import time
import requests
import threading
def get_price():
while True:
root_url = 'https://api.binance.com/api/v3/avgPrice'
symbol = 'BTCUSDT'
url = root_url + '?symbol=' + symbol
data = requests.get(url).json()
my_label.config(text=data['price'])
time.sleep(60)
root = Tk()
root.geometry('400x200')
my_label = Label(root, text='Hello There')
my_label.pack(pady=20)
my_button = Button(root, text='Start', command=lambda:threading.Thread(target=get_price).start())
my_button.pack(pady=20)
root.mainloop()
I would suggest to decouple the main thread with the one that performs the
reading of the data using the library queue,thing that you can easily do with
an Object Oriented approach.
I've put time to 3 seconds, regards.
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import threading
import queue
import time
import requests
class MyTrhead(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.queue = queue.Queue()
self.check = True
def stop(self):
self.check = False
def run(self):
"""Feeds the tail."""
while self.check:
root_url = 'https://api.binance.com/api/v3/avgPrice'
symbol = 'BTCUSDT'
url = root_url + '?symbol=' + symbol
data = requests.get(url).json()
msg = data['price']
print("on run: {0}".format(msg))
time.sleep(3)
self.queue.put(msg)
def check_queue(self, obj):
"""Returns a formatted string"""
while self.queue.qsize():
try:
x = self.queue.get(0)
msg = "{0}".format(x)
print("check_queue {0}".format(msg))
obj.set(msg)
except queue.Empty:
pass
class App(tk.Tk):
"""Application start here"""
def __init__(self):
super().__init__()
self.title("Simple App")
self.protocol("WM_DELETE_WINDOW", self.on_close)
self.data = tk.StringVar()
self.data.set("Hello World")
self.init_ui()
self.on_start()
def init_ui(self):
f0 = ttk.Frame(self)
self.my_label = ttk.Label(f0, textvariable=self.data)
self.my_label.pack()
f0.pack(fill=tk.BOTH, expand=1)
def on_start(self):
self.my_thread = MyTrhead()
self.my_thread.start()
self.periodic_call()
def periodic_call(self):
self.my_thread.check_queue(self.data)
if self.my_thread.is_alive():
self.after(1, self.periodic_call)
else:
pass
def on_close(self, evt=None):
msg = "Do you want to quit?"
if messagebox.askokcancel(self.title(), msg, parent=self):
if self.my_thread is not None:
self.my_thread.stop()
self.destroy()
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()

python tkinter- more than one windows to show and do different processes

I have a GUI application. It has more than one windows. Main windows is updating every second with new values. I am using threading and queue here. There is a button in the main window. Pressing it will open a new window. What I want is, when new window is shown, stop the process in the main window (updating the values process) and some new process will be shown in the new window. Closing the new window should revoke the main window again and continue the process. Do i need to use multi threading or multi processing?
Unfortunately, I am asking this question for the second time. sorry for that.
import tkinter
import time
import threading
import random
import queue
class GuiPart:
def __init__(self, master, queue, endCommand,newWindow):
self.queue = queue
self.pause = False
# Set up the GUI
console = tkinter.Button(master, text='Done', command=endCommand)
console.pack()
console2 = tkinter.Button(master, text='New', command=newWindow)
console2.pack()
self.output = tkinter.StringVar()
#output.set(1)
output_1_label = tkinter.Label(master, textvariable= self.output, height=2, width=12)
output_1_label.pack()
# Add more GUI stuff here
self.temp_process()
def temp_process(self):
if not self.pause:
print ("handling messages")
else:
print ("Not handling messages")
root.after(1000,self.temp_process)
def processIncoming(self):
while self.queue.qsize():
try:
msg = self.queue.get(0)
print (msg)
self.output.set(msg)
except queue.Empty:
pass
class ThreadedClient:
def __init__(self, master):
self.master = master
# Create the queue
self.queue = queue.Queue()
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.endApplication,self.create_window)
self.running = 1
self.thread1 = threading.Thread(target=self.workerThread1) #this is for sending data to queue.
# what about second window?
self.thread1.start()
self.periodicCall()
def on_quit(self):
self.gui.pause = False
self.window.destroy()
def create_window(self):
self.window = tkinter.Toplevel(root)
self.gui.pause = True
self.window.protocol("WM_DELETE_WINDOW",self.on_quit)
def periodicCall(self):
self.gui.processIncoming()
if not self.running:
import sys
sys.exit(1)
self.master.after(1000, self.periodicCall)
def workerThread1(self):
while self.running:
time.sleep(rand.random() * 1)
msg = rand.random()
self.queue.put(msg)
def endApplication(self):
self.running = 0
rand = random.Random()
root = tkinter.Tk()
client = ThreadedClient(root)
root.mainloop()
You can have your function processIncoming check for a flag, and pause/resume when required.
class GuiPart:
def __init__(self, master, queue, endCommand,newWindow):
self.queue = queue
self.pause = False
....
def processIncoming(self):
while self.queue.qsize() and not self.pause:
try:
msg = self.queue.get(0)
print (msg)
self.output.set(msg)
except queue.Empty:
pass

What is the best way to repeatedly execute a function every few seconds, without intterupting other functions, for a Python gui?

I want to repeatedly run a function that retrieves data from an API every 60 seconds. However, I've realized that I can't put it to sleep, or use a while loop, without interrupting the other functions.
My question is, how do I repeatedly retrieve the data from the API/rerun the code that gets the data in the first place, without interrupting the other functions?
below a complete script that retrieve ISS position longitude and latitude from Open Notify and print it on a thread.
I' ve set a 1 second time. You can start and stop the thread. Try now you to add your funcions.
Notice that you have to import this modules
import requests
import json
import tkinter as tk
import threading
import queue
import datetime
import time
import requests
import json
class MyThread(threading.Thread):
def __init__(self, queue,):
threading.Thread.__init__(self)
self.queue = queue
self.check = True
def stop(self):
self.check = False
def run(self):
while self.check:
response = requests.get("http://api.open-notify.org/iss-now.json")
data = response.json()
time.sleep(1)
self.queue.put(data)
class App(tk.Frame):
def __init__(self,):
super().__init__()
self.master.title("Hello World")
self.master.protocol("WM_DELETE_WINDOW",self.on_close)
self.queue = queue.Queue()
self.my_thread = None
self.init_ui()
def init_ui(self):
self.f = tk.Frame()
w = tk.Frame()
tk.Button(w, text="Start", command=self.launch_thread).pack()
tk.Button(w, text="Stop", command=self.stop_thread).pack()
tk.Button(w, text="Close", command=self.on_close).pack()
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
self.f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
def launch_thread(self):
if (threading.active_count()!=0):
self.my_thread = MyThread(self.queue)
self.my_thread.start()
self.periodiccall()
def stop_thread(self):
if self.my_thread is not None:
if(threading.active_count()!=1):
self.my_thread.stop()
def periodiccall(self):
self.checkqueue()
if self.my_thread.is_alive():
self.after(1, self.periodiccall)
else:
pass
def checkqueue(self):
while self.queue.qsize():
try:
ret = self.queue.get(0)
print("The ISS is currently over: {0} ".format(ret['iss_position']))
except queue.Empty:
pass
def on_close(self):
if self.my_thread is not None:
if(threading.active_count()!=1):
self.my_thread.stop()
self.master.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

How to get return value of sub process in main process

I want to start a webserver if the user does click on a button. Therefore I have written the following code:
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox
import time
from multiprocessing import Process
class MainWindow(ttk.Frame):
def __init__(self, parent):
self.parent = parent
ttk.Frame.__init__(self, master=self.parent)
self.parent.grab_set()
self.parent.title("Test_Program")
self.parent.geometry("500x500")
ttk.Button(self.parent, text="Start Web-Server", command=self.start_webserver).pack()
def start_webserver(self):
if __name__ == '__main__':
loading_screen = LoadingScreen(self)
result = [0]
sub_process = Process(target=start_webserver, args=(result,))
sub_process.start()
sub_process.join()
sub_process.terminate()
loading_screen.destroy()
if result[0] == 0:
messagebox.showinfo("Webserver Status", "Webserver is running!")
else:
messagebox.showerror("Webserver Status", "Webserver can't be started an Error occured!")
class LoadingScreen(tk.Toplevel):
def __init__(self, parent):
self.parent = parent
tk.Toplevel.__init__(self, master=self.parent)
self.geometry("200x50")
self.title("Loading Screen")
self.transient(self.parent)
self.grab_set()
ttk.Label(self, text="Copying data!").pack()
status_bar = ttk.Progressbar(self, length=180, mode="indeterminate")
status_bar.start(5)
status_bar.pack()
def __del__(self):
if isinstance(self.parent, tk.Toplevel):
self.parent.grab_set()
def start_webserver(result):
time.sleep(2) # This does represent the time thats necessary to start the webserver
result[0] = 1
def main():
root = tk.Tk()
main_window = MainWindow(root)
main_window.pack()
root.mainloop()
if __name__ == "__main__":
main()
If I click on "start webserver" its two seconds frozen because of time.sleep(2) and no progress bar does get displayed. Afterwards its done. I don't know why there is no progressbar displayed. I have used an array named "result" as parameter to get the return value of start_webserver(result) from sub_process to main_process. Unfortunately this is not working too because the value of "result[0]" stays 0. I don't know what to do. The idea of using an array I have found on stackoverflow too.
Here is the Link:
how to get the return value from a thread in python?
I have found out an answer by my own. Thanks for your help join() wasn't right. I have done it a different way and closing the "loading_screen" as well as opening the messagebox after the job has been done in the second thread.
That's the Code:
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox
import time
from threading import Thread
class MainWindow(ttk.Frame):
def __init__(self, parent):
self.parent = parent
ttk.Frame.__init__(self, master=self.parent)
self.parent.grab_set()
self.parent.title("Test_Program")
self.parent.geometry("200x50")
ttk.Button(self.parent, text="Start Web-Server", command=self.start_webserver).pack()
def start_webserver(self):
if __name__ == '__main__':
loading_screen = LoadingScreen(self)
thread = Thread(target=start_webserver, args=(loading_screen, ))
thread.start()
class LoadingScreen(tk.Toplevel):
def __init__(self, parent):
self.parent = parent
tk.Toplevel.__init__(self, master=self.parent)
self.geometry("200x50")
self.title("Loading Screen")
self.transient(self.parent)
self.grab_set()
ttk.Label(self, text="Copying data!").pack()
status_bar = ttk.Progressbar(self, length=180, mode="indeterminate")
status_bar.start(2)
status_bar.pack()
def __del__(self):
if isinstance(self.parent, tk.Toplevel):
self.parent.grab_set()
def start_webserver(widget):
return_val = do_some_stuff()
if return_val:
widget.destroy()
messagebox.showinfo("Webserver Status", "Webserver is running!")
else:
widget.destroy()
messagebox.showerror("Webserver Status", "Webserver can't be started an Error occured!\n"
"Please look inside 'Logfiles/logfile' for more details.")
def do_some_stuff():
time.sleep(2) # This does represent the time thats necessary to start the webserver
return True
def main():
root = tk.Tk()
main_window = MainWindow(root)
main_window.pack()
root.mainloop()
if __name__ == "__main__":
main()

Tkinter: Separating Processes

I am trying to build a Tkinter program that supports multiprocessing. I need to read from multiple Modbus devices and display the output onto the GUI.
I have successfully done this with a command line by using processes, but in Tkinter, my GUI freezes every time I attempt to read.
Here is my code:
import os
from multiprocessing import Process
import threading
import queue
import tkinter as tk
from tkinter import *
from tkinter import ttk
import time
import time as ttt
import minimalmodbus
import serial
minimalmodbus.CLOSE_PORT_AFTER_EACH_CALL = True
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.gas = minimalmodbus.Instrument('COM3', 1)
self.gas.serial.baudrate = 9600
self.gas.serial.parity = serial.PARITY_NONE
self.gas.serial.bytesize = 8
self.gas.serial.stopbits = 1
self.gas.serial.timeout = 0.25
self.gas.mode = minimalmodbus.MODE_RTU
self.pack()
self.create_widgets()
def create_widgets(self):
self.first_gas_labelframe = LabelFrame(self, text="Gas 1", width=100)
self.first_gas_labelframe.pack()
self.value_label = Label(self.first_gas_labelframe, text="Value")
self.value_label.pack()
self.unit_label = Label(self.first_gas_labelframe, text="Unit")
self.unit_label.pack()
self.temp_label = Label(self.first_gas_labelframe, text="Temp")
self.temp_label.pack()
self.timer_button = tk.Button(self, text='Start', command=self.process)
self.quit = tk.Button(self, text="QUIT", fg="red", command=root.destroy)
self.quit.pack()
self.gas_list = [self.gas]
def reader():
self.read = gas_num.read_registers(0,42)
self.value_label.config(text=self.read[0])
self.unit_label.config(text=self.read[1])
self.temp_label.config(text=self.read[2])
def process(self):
for sen in self.gas_list:
self.proc = Process(target=self.reader, args=(sen,))
self.proc.start()
self.proc.join()
if __name__ == '__main__':
root = tk.Tk()
app = Application()
app.mainloop()
When I press the start button the program will freeze until the process is done. How do I correctly set up a system to have the GUI operational while having processes run?
The simplest solution would be to put this all on a separate thread. The reason your GUI freezes up is because the process method has consumed the Main Thread and until it completes, Tkinter won't update anything.
Here's a simple example:
self.timer_button = tk.Button(self, text='Start', command=lambda: threading.Thread(target=self.process).start())
However, this doesn't stop the user from clicking the button twice. You could create a new method which controls this. Example:
import os
from multiprocessing import Process
import threading
import queue
import tkinter as tk
from tkinter import *
from tkinter import ttk
import time
import time as ttt
import minimalmodbus
import serial
minimalmodbus.CLOSE_PORT_AFTER_EACH_CALL = True
THREAD_LOCK = threading.Lock()
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.gas = minimalmodbus.Instrument('COM3', 1)
self.gas.serial.baudrate = 9600
self.gas.serial.parity = serial.PARITY_NONE
self.gas.serial.bytesize = 8
self.gas.serial.stopbits = 1
self.gas.serial.timeout = 0.25
self.gas.mode = minimalmodbus.MODE_RTU
self.pack()
self.create_widgets()
def create_widgets(self):
self.first_gas_labelframe = LabelFrame(self, text="Gas 1", width=100)
self.first_gas_labelframe.pack()
self.value_label = Label(self.first_gas_labelframe, text="Value")
self.value_label.pack()
self.unit_label = Label(self.first_gas_labelframe, text="Unit")
self.unit_label.pack()
self.temp_label = Label(self.first_gas_labelframe, text="Temp")
self.temp_label.pack()
self.timer_button = tk.Button(self, text='Start', command=self.start_thread)
self.quit = tk.Button(self, text="QUIT", fg="red", command=root.destroy)
self.quit.pack()
self.gas_list = [self.gas]
def check_thread(self):
if self.thread.is_alive():
root.after(50, self.check_thread)
else:
# Thread completed
self.timer_button.config(state='normal')
def start_thread(self):
self.timer_button.config(state='disabled')
self.thread = threading.Thread(target=self.process)
self.thread.start()
root.after(50, self.check_thread)
def reader(self):
self.read = gas_num.read_registers(0,42)
self.value_label.config(text=self.read[0])
self.unit_label.config(text=self.read[1])
self.temp_label.config(text=self.read[2])
def process(self):
with THREAD_LOCK:
for sen in self.gas_list:
self.proc = Process(target=self.reader, args=(sen,))
self.proc.start()
self.proc.join()
if __name__ == '__main__':
root = tk.Tk()
app = Application()
app.mainloop()
The Lock is to ensure no two threads are running at the same time. I've also disables the button so that the user can't click until it's done. By using the root.after method, you can create callbacks that wait for a period of time before running.
As far as the multiprocessing, you are running the processes on a separate process, but only one at a time. If you want to run many at one time, then you need to move the join call somewhere else. I'm not sure how many processes are running at once but you could do something like this:
processes = []
for sen in self.gas_list:
proc = Process(target=self.reader, args=(sen,))
processes.append(proc)
proc.start()
[x.join() for x in processes]
In this implementation, I've removed assigning proc as a class variable.
I do not have the libraries or data to properly test all this, but it should work...
EDIT
This will initiate a pool of 6 processes that loop through self.gas_list, passing the item to self.reader. When those complete, it will check to make sure a second has gone by (waiting if it hasn't) and restarting the above process. This will run forever or until an exception is raised. You will need to import Pool from the multiprocessing module.
def process(self):
with THREAD_LOCK:
pool = Pool(6)
while 1:
start_time = time.time()
pool.map(self.reader, self.gas_list)
execution_time = time.time() - start_time
if execution_time < 1:
time.sleep(1-execution_time)

Categories