Limit threads inside tkinter - python-3.x

I am writing an application where the gui runs on one thread and the individual tasks run on another thread. Since I want to make ssh requests and the tasks are a bit more complex, I would like to limit the number of threads or if possible create a thread for the "first" task and run the other tasks, which are added by pressing a button, on this thread. If I run all tasks on one thread, then I can limit the tasks with asyncio.Semaphore(). This works fine so far, but I can't get the limitation of the threads right now. I know that I can use from async_tkinter_loop import async_handler, async_mainloop for the problem that app and tasks have to run seperately to not freeze the gui, but this method turned out to be not powerful.
import asyncio
import tkinter as tk
import threading
class pseudo_example():
def __init__(self):
self.que = queue.Queue()
self.pool_sem = threading.Semaphore(2)
self.lock = asyncio.Lock()
self.sem = asyncio.Semaphore(4)
self.lock_thread = threading.Lock()
self.root = tk.Tk()
self.root.minsize(100,100)
self.control= 1
def app(self,async_loop):
i = 0
list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
self.Label_ip_content = {}
for ip in list:
start_button = tk.Button(self.root, text="start{} ".format(ip), command=lambda ip=ip: self.start_single_task(async_loop,ip))
start_button.grid(column = 0, row = i)
self.testfield = tk.Label(self.root, text="test")
self.testfield.grid(column = 1, row = i)
self.Label_ip_content[ip] = (self.testfield, start_button)
i = i +1
self.root.update_idletasks()
self.root.mainloop()
def start_single_task(self, async_loop, ip):
self.root.update_idletasks()
self.Label_ip_content.get(ip)[0]["text"] = "lock"
self.Label_ip_content.get(ip)[1]["state"] = "disable"
self.worker = threading.Thread(target=self.create_async_single, args=(async_loop, ip,), daemon=False)
self.worker.start()
def create_async_single(self, async_loop, ip):
try:
async_loop.run_until_complete(self.boundary_task(ip))
except RuntimeError:
pass
async def boundary_task(self, ip):
async with self.sem:
return await self.await_fun(ip)
async def await_fun(self,ip):
self.Label_ip_content.get(ip)[0]["text"] = "start waiting"
await asyncio.sleep(5)
self.Label_ip_content.get(ip)[0]["text"] = "end waiting"
self.Label_ip_content.get(ip)[1]["state"] = "normal"
if __name__ == '__main__':
try:
gui = pseudo_example()
async_loop = asyncio.get_event_loop()
gui.app(async_loop)
except KeyboardInterrupt:
print("Interrupted")
sys.exit()

Related

Getting returning value from multithreading in python 3

I'm trying to get one or several returning values from a thread in a multithreading process. The code I show get cycled with no way to interrupt it with Ctrl-C, Ctrl+D.
import queue as Queue
import threading
class myThread (threading.Thread):
def __init__(self, threadID, name, region):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.region = region
def run(self):
GetSales(self.region)
def GetSales(strReg):
print("Thread-" + strReg)
return "Returning-" + strReg
def Main():
RegionList = []
RegionList.append("EMEA")
RegionList.append("AP")
RegionList.append("AM")
# Create threads
threads = []
x = 0
for region in RegionList:
x += 1
rthread = myThread(x, "Thread-" + region, region) # Create new thread
rthread.start() # Start new thread
threads.append(rthread) # Add new thread to threads list
que = Queue.Queue()
# Wait for all threads to complete
for t in threads:
t.join()
result = que.get()
print(t.name + " -> Done")
Main()
If I comment line "result = que.get()" the program runs with no issues.
What you are looking for is future and async management.
Firstly, your program loop indefinitely because of the line que.get(), because there is nothing in the queue, it wait that something happen, which will never happen. You don't use it.
What you want to do is an async task and get the result :
import asyncio
async def yourExpensiveTask():
// some long calculation
return 42
async main():
tasks = []
tasks += [asyncio.create_task(yourExpensiveTask())]
tasks += [asyncio.create_task(yourExpensiveTask())]
for task in tasks:
result = await task
print(result)
See also https://docs.python.org/3/library/asyncio-task.html

why the thread runs only once?

This is my client:
from tkinter import *
import tkinter.simpledialog
import socket
import select
import ssl
import threading
Host = '127.0.0.1'
Port = 87
def create_connection():
return socket.create_connection((Host, Port))
def gui():
global e1
global txt
root = Tk()
root.title('Amifre chat')
root.geometry("700x515")
txt = Text(root, width=70, height=30)
txt.config(state=DISABLED)
e1 = Entry(root, width=93)
e1.place(x=0, y=487)
txt.place(x=0)
t = threading.Thread(target=display_msg())
t.daemon = True
root.after(1, t.start())
root.mainloop()
def display_msg():
r, w, x = select.select([client_socket], [], [], 0.00001)
if client_socket in r:
data = client_socket.recv().decode()
txt.config(state=NORMAL)
txt.insert(INSERT, data + '\n')
txt.config(state=DISABLED)
if __name__ == '__main__':
start = Tk()
b = Button(start, text='Click to join the chat', command=create_user_name).grid(row=0)
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
client_socket = create_connection()
client_socket = context.wrap_socket(client_socket, server_hostname='127.0.0.1')
start.mainloop()
gui()
This is a client for chat and the thread in the gui function call the display_msg function only once so does anyone have any idea why is it? (sending data works well and it dislplayed in client without GUI)
You should pass only the function name to target option, and call t.start() directly without using after():
t = threading.Thread(target=display_msg, daemon=True)
t.start()
Then, you need to use while loop inside display_msg() to keep receiving data from server:
def display_msg():
while True:
r, w, x = select.select([client_socket], [], [])
if client_socket in r:
data = client_socket.recv(1024).decode()
txt.config(state=NORMAL)
txt.insert(INSERT, data + '\n')
txt.config(state=DISABLED)
This code:
t = threading.Thread(target=display_msg())
is functionally identical to this code:
result = display_msg()
t = threading.Thread(result)
And this code:
root.after(1, t.start())
is functionally identical to this code:
result = t.start()
root.after(1, result)
In both threading.Thread and after, the values given to the functions must be references to a function rather than the result of a function (unless the result is itself a reference to a function).
t = threading.Thread(target=display_msg)
...
root.after(1, t.start)
Regardless, the answer to "why the thread runs only once?" is because that's what threads do. Threads run until they are done. If you need code to run in a loop, you will need to write the loop yourself.

How to thread with tkinter in python3 using queue?

I'm trying to thread definitions in tkinter using queue in specifically python3
I've had similar code in python2 work great using a similar method without queue but in python3 from what i've read tkinter doesn't allow for multithreading with gui. I found some examples that uses Queue process. They outline i'm suppose to create an Queue object, a new thread with access to that queue and check for input in the main thread
#!/usr/bin/python3
from tkinter import *
import time
import threading
import queue
import subprocess
def temp_sensor(queue_list):
warning = 0
while True:
var = "cat /sys/class/thermal/thermal_zone*/temp"
temp_control = subprocess.check_output([var], shell=True)
temp_length = len(temp_control)
temp_control = temp_control[35:]
temp_control = temp_control[:-4]
temp_control = int(temp_control)
degree_sign= u'\N{DEGREE SIGN}'
displayed_temp = "Tempature: " + str(temp_control) + degree_sign + "C"
if temp_control > 79:
warning = warning + 1
if warning == 3:
print ("Warning Core Tempature HOT!")
warning = 0
if temp_control > 90:
time.sleep(3)
print ("Warning EXTREMLY to HOT!!!")
queue_list.put(displayed_temp)
time.sleep(1)
class Gui(object):
def __init__(self, queue_list):
self.queue_list = queue_list
self.root = Tk()
self.root.geometry("485x100+750+475")
main_tempature_status = StringVar(self.root)
Ts = Entry(self.root, textvariable=main_tempature_status)
Ts.pack()
Ts.place(x=331, y=70, width=160, height=25)
Ts.config(state=DISABLED, disabledforeground="Black")
self.root.after(1000, self.read_queue)
def read_queue(self):
try:
temp = self.queue.get_nowait()
self.main_tempature_status.set(temp)
except queue_list.Empty:
pass
self.root.after(1000, self.read_queue)
if __name__ == "__main__":
queue_list = queue.Queue()
gui = Gui(queue_list)
t1 = threading.Thread(target=temp_sensor, args=(queue_list,))
t1.start()
gui.root.mainloop()
My desired result is to run a some of these definitions to do various tasks and display their variables in the tkinter entry using python3.
when i run my code it gives me the variable from the queue but it won't post to the GUI. please forgive my less then pythonic code.
Change you Gui class to this:
class Gui(object):
def __init__(self, queue_list):
self.queue_list = queue_list
self.root = Tk()
self.root.geometry("485x100+750+475")
self.main_tempature_status = StringVar(self.root)
self.Ts = Entry(self.root, textvariable=self.main_tempature_status)
self.Ts.pack()
self.Ts.place(x=331, y=70, width=160, height=25)
self.Ts.config(state=DISABLED, disabledforeground="Black")
self.root.after(1000, self.read_queue)
def read_queue(self):
try:
temp = self.queue_list.get_nowait()
self.Ts.config(state=NORMAL)
self.main_tempature_status.set(temp)
self.Ts.config(state=DISABLED)
except queue.Empty:
pass
self.root.after(1000, self.read_queue)
Explanation:
variable main_temperature_status is used in function read_queue as class variable, but not defined as class variable.
You cannot show the change in value of Entry widget if it is always disabled, so enabling it before value change in read_queue.

What is a best way to run async process and notify changes to be displayed in tkinter canvas?

I want to run a program that after being load will refresh tkinter canvas automatically.
I want the other process to be independent from tkinter main loop
In pseudo code i want it to behave something like this:
if __name__ == "__main__":
app = loadTkinterWindowsWithCanvas()
app.mainloop()
manager = Manager.Manager()
dataProcess = Process(target = manager.start())
dataProcess.start()
oldX = None
x = None
while True:
time.sleep(1)
oldX, x = x , manager.x
if oldX is not x:
app.updateCanvas(x)
...
#in Manager Class
def start(self):
self.x = 0
while True:
self.x += 1
I was trying out a lot of examples from stackoverflow or tutorials, with process threads,lock and event but i cant get behavior i desire. My program either doesn't load or doesn't update. What is best approach ?
This is what my code looks right now
if __name__ == "__main__":
lock = mp.RLock()
app = MainFrame()
listOfCities = [(0,121),(112,5),(14,201),(45,88),(141,231),(1,8),(22,11),(101,84),(90,231)]
pop = Population.Population(200,listOfCities)
manager = EvolutionManager.EvolutionManager(100,pop,lock)
pro = mp.Process(target = manager.startTraining())
pro.start()
app.mainloop()
lastBest = None
best = None
while True:
print("x")
time.sleep(0.2)
lastBest, best = best,manager.population.bestSalesman
if lastBest is not best:
app.getCurrentTopFrame().updateFrame(best.dna.getAsListOfTuple())
Behavior is that while second process start window does not load until the process ends
UPDATE
This started to work.
def genethicAlgorithmPart(event,manager):
manager.startTraining(event)
def addChangerListiner(manager,app,event):
thread = t.Thread(target = changeListiner, args = (manager,app,event,))
thread.start()
def changeListiner(manager,app,event):
lastBest = None
best = None
print("Starting listiner thread")
while True:
print("x")
event.wait()
lastBest, best = best,manager.population.bestSalesman
if lastBest is not best:
app.getCurrentTopFrame().updateFrame(best.dna.getAsListOfTuple())
event.clear()
if __name__ == "__main__":
# listOfCities = [(631, 44), (612, 137), (441, 266), (447, 173), (52, 243), (104, 148), (333, 70), (474, 182), (419, 221), (238, 291), (264, 340), (290, 213), (332, 97), (473, 294), (188, 198), (180, 258), (433, 382), (394, 139)]
listOfCities = rmg.genRandomListOfPoints(15,800,400)
pop = Population.Population(400,listOfCities)
manager = EvolutionManager.EvolutionManager(100,pop)
event = mp.Event()
pro = t.Thread(target = genethicAlgorithmPart, args = (event, manager,))
app = MainFrame.MainFrame()
app.getCurrentTopFrame().updateFrame(manager.population.bestSalesman.dna.getAsListOfTuple())
app.after(111, addChangerListiner(manager, app, event))
pro.start()
app.mainloop()

Coroutine to mimic a OS's scheduler

I am following the :A Curious Course on Coroutines and Concurrency to learn coroutine, encounter problem to get the following codes running:
The code mimic an operating system to schedule tasks
from queue import Quue
class Task:
taskid = 0
def __init__(self, target):
Task.taskid += 1 #count the task
self.tid = Task.taskid
self.tartet = target
self.sendval = None
def run(self):
return self.target.send(self.sendval)
class Scheduler:
def __init__(self):
self.ready = Queue() # a queue of tasks that are ready to run.
self.taskmap = {} #dictionary that keeps track of all active tasks (each task has a unique integer task ID)
def new(self, target): #introduce a new task to the scheduler
newtask = Task(target)
self.taskmap[newtask.tid] = newtask
def schedule(self, task):
self.ready.put(task)
def mainloop(self):
while self.taskmap: #I think the problem is here
task = self.ready.get() #I think it should be while self.ready
result = task.run()
self.schedule(task)
Test it with
def foo():
while True:
print("I'm foo")
yield
def bar():
while True:
print("I'm bar")
yield
It pending instead of return value
In [85]: schedule.new(foo())
In [86]: schedule.new(bar())
In [87]: schedule.mainloop()
^C---------------------------------------------------------------------------
KeyboardInterrupt Traceback (most recent call last)
I review the codes and find problem with
def mainloop(self):
while self.taskmap: #I think the problem is here
task = self.ready.get() #I think it should be while self.ready
result = task.run()
self.schedule(task)
while self.taskmap, but there is no methods to remove elements, so it is an infinite loop
I changed it to
def mainloop(self):
while self.taskmap: #I think the problem is here
task = self.ready.get() #I think it should be while self.ready
result = task.run()
self.schedule(task)
However, it still not work.
What's the problem with my code.

Resources