I want to run two loops at the same time in python (first_mainloop(), second_mainloop()), I use threading but it doesn't work at this situation (only first loop runs and after it gets closed second loops starts running) any ideas?
import threading
import runpy
from tkinter import *
root = Tk()
import os
def first_mainloop():
p1 = PhotoImage(file='Screenshot_13-removebg-preview.png')
root.iconphoto(False, p1)
frameCnt = 12
frames = [PhotoImage(file='light_ai_design_by_gleb.gif',format = 'gif -index %i' %(i)) for i in range(frameCnt)]
# set window size
root.geometry("500x400")
root.title("Assistant")
def update(ind):
frame = frames[ind]
ind += 1
if ind == frameCnt:
ind = 0
label.configure(image=frame)
root.after(100, update, ind)
label = Label(root)
label.pack()
root.resizable(False, False)
root.after(0, update, 0)
root.mainloop()
def second_mainloop():
runpy.run_path("main.py")
thread1 = threading.Thread(target=first_mainloop())
thread1.start()
thread2 = threading.Thread(target=second_mainloop())
thread2.start() ```
I think the problem is in parentheses, because when you write this thread1 = threading.Thread(target=first_mainloop()), you call the function. So remove the brackets, it should be something like: thread1 = threading.Thread(target=first_mainloop)
Here is great explanation of your problem: https://thispointer.com/python-how-to-create-a-thread-to-run-a-function-in-parallel/
Related
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.
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.
I made a Python turtle program that recursively generated a fractal tree, but, since it often took a few hours to fully draw, I wanted to try to use multithreading to have multiple turtles working together.
I was able to get two turtles moving at the same time, but, in this much more complex situation, everything seemed to fall apart. I've tried this many different ways, and thought this final solution would be the one, but it just throws a bunch of errors.
Here's my code:
import turtle
import threading
from queue import Queue
class Location:
def __init__(self, xpos=0, ypos=0, heading=90):
self.xpos = xpos
self.ypos = ypos
self.heading = heading
def getx(self):
return self.xpos
def gety(self):
return self.ypos
def geth(self):
return self.heading
class Turtle(turtle.Turtle):
def tolocation(self, location):
self.penup()
self.setx(location.getx())
self.sety(location.gety())
self.setheading(location.geth())
self.pendown()
def get_location(self):
return Location(self.xcor(), self.ycor(), self.heading())
def draw_tree(self, startpos=Location(), size=100):
tm.q.put(self.tolocation(startpos))
for _ in range(size):
tm.q.put(self.forward(1))
for _ in range(45):
tm.q.put(self.right(1))
t2 = Turtle()
t2.speed(0)
tm.new_thread(t2.draw_tree, self.get_location(), size / 2)
for _ in range(90):
tm.q.put(self.left(1))
tm.new_thread(self.draw_tree, self.get_location(), size / 2)
class ThreadManager:
def __init__(self):
self.q = Queue()
self.threads = []
def new_thread(self, func, *args):
self.threads.append(threading.Thread(target=func, args=(args,)))
self.threads[-1].daemon = True
self.threads[-1].start()
def process_queue(self, scr):
while not self.q.empty():
(self.q.get())(1)
if threading.active_count() > 1:
scr.ontimer(self.process_queue(scr), 100)
tm = ThreadManager()
scr = turtle.Screen()
t1 = Turtle()
t1.speed(0)
tm.new_thread(t1.draw_tree)
tm.process_queue(scr)
scr.exitonclick()
Can anyone give me an idea of where I went wrong here? The error messages say something along the lines of the recursion going too deep when process_queue calls itself. Am I using scr.ontimer() wrong?
There are several problems with your code:
draw_tree() doesn't have a base case to stop it's (conceptual)
recursion, it just keeps creating new threads.
Each call to draw_tree() divides size in half using floating
division so range(size) will fail as range() can only take ints.
Your calls to self.get_location() are not valid as it tells you
where the turtle is, not where it will be once the main thread
finishes processing outstanding graphics commands. You have to
compute where you will be, not look where you are.
This call, tm.q.put(self.tolocation(startpos)) isn't valid -- you
either need to do tm.q.put(self.tolocation, startpos) or call
tm.q.put() on each command inside self.tolocation().
You can't create new turtles anywhere but the main thread, as they
invoke tkinter on creation and that'll be on the wrong (not main)
thread. In my rework below, I simply preallocate them.
args=(args,) is incorrect -- should be args=args as args
is already in the correct format.
You don't need to create two new threads at each branching point, just
one. The new turtle goes one way, the old turtle continues on the
other.
Below is my rework of your code to address the above and other issues:
import math
import threading
from queue import Queue
from turtle import Turtle, Screen
class Location:
def __init__(self, xpos=0, ypos=0, heading=90):
self.xpos = xpos
self.ypos = ypos
self.heading = heading
def clone(self):
return Location(self.xpos, self.ypos, self.heading)
class Terrapin(Turtle):
def tolocation(self, location):
tm.q.put((self.penup,))
tm.q.put((self.setx, location.xpos))
tm.q.put((self.sety, location.ypos))
tm.q.put((self.setheading, location.heading))
tm.q.put((self.pendown,))
def draw_tree(self, startpos, size=100):
if size < 1:
return
self.tolocation(startpos)
tm.q.put((self.forward, size))
angle = math.radians(startpos.heading)
startpos.xpos += size * math.cos(angle)
startpos.ypos += size * math.sin(angle)
tm.q.put((self.right, 45))
startpos.heading -= 45
tm.new_thread(pond.get().draw_tree, startpos.clone(), size / 2)
tm.q.put((self.left, 90))
startpos.heading += 90
self.draw_tree(startpos, size / 2)
pond.put(self) # finished with this turtle, return it to pond
class ThreadManager:
def __init__(self):
self.q = Queue()
self.threads = Queue()
def new_thread(self, method, *arguments):
thread = threading.Thread(target=method, args=arguments)
thread.daemon = True
thread.start()
self.threads.put(thread)
def process_queue(self):
while not self.q.empty():
command, *arguments = self.q.get()
command(*arguments)
if threading.active_count() > 1:
screen.ontimer(self.process_queue, 100)
screen = Screen()
# Allocate all the turtles we'll need ahead as turtle creation inside
# threads calls into Tk which fails if not running in the main thread
pond = Queue()
for _ in range(100):
turtle = Terrapin(visible=False)
turtle.speed('fastest')
pond.put(turtle)
tm = ThreadManager()
tm.new_thread(pond.get().draw_tree, Location())
tm.process_queue()
screen.exitonclick()
if instead of large steps:
tm.q.put((self.right, 45))
you want to break down graphic commands into small steps:
for _ in range(45):
tm.q.put((self.right, 1))
that's OK, I just wanted to get the code to run. You need to figure out if this gains you anything.
Does any one know how to call a def form a thread.
Clock Program:
import sys
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
from time import sleep
import threading
class MyThread ( threading.Thread ):
def mclock(): # function that it can't call
x = 1
z = 0
while x != -1:
Label(mGui,text = str(x) + "second(s)").pack()
x = x+1
sleep(1)
if x == 60:
x = 1
z = z+1
Label(mGui, text= str(z) + " minute(s) has past.").pack()
return
return
MyThread().start()
mGui = Tk()
mGui.geometry("300x200+100+100")
mGui.title("Jono's Clock")
menubar = Menu(mGui)
filemenu = Menu(menubar, tearoff = 0)
filemenu.add_command(label = "Clock",command = mclock) # can't use function
menubar.add_cascade(label = "File",menu = filemenu)
mGui.config(menu = menubar)
mGui.mainloop()
If any one sees any other errors please state. I am also using windows 7 and python 3.3.
There are several syntax errors in the code you've posted, and I'm not sure exactly what you intended with them, so here's an overview of how to run stuff from threads.
If you want your thread to run your own code from a custom thread class, the usual way to do that is to put the code in a method named run, which will be executed automatically when the thread is started:
import threading
class MyThread(threading.Thread):
def run(self):
# do your stuff here
print("Hello World")
MyThread().start()
Alternatively, if you don't need a class, you can create your function at the top level of your module, and pass it as an argument to threading.Thread's constructor:
def my_function():
print("Hello World")
threading.Thread(target=my_function).start()
Note that you often want to keep a reference to the thread object, rather than letting it go as the code above does. This requires you to use two lines to create and then start the thread:
thread = MyThread() # or the alternative version
thread.start()
This lets you later do:
thread.join()
Which ensures that the thread has finished its work.
Hi I'm new at programming in python and gtk.
I'm writing a program to do some measurement.
For plotting the measurement, I use matplotlib.
The program will have a function to turn a heater on and off and to make the measurement.
I want to use separate threads for the heater and the measurement.
For now the communication with the hardware hasn't been implemented yet in this program.
The problem is when I click the "measurebutton", the "progressbar" doesn't work anymore.
I get a message:
gtk.ProgressBar object at 0x29b8460 (uninitialized at 0x0)
When I only use the heaterbutton, the progressbar keeps working
What am I doing wrong ?
This is the code
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import time
import gobject
import threading
import matplotlib
import numpy as np
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
class measure:
# callback to quit
def delete_event(self, widget, event, data = None):
gtk.main_quit()
return False
def heater_helper(self, widget, heater, progressbar):
print "starting heater thread"
threading.Thread(target=self.heater_cb, args=(widget, heater, progressbar)).start()
def heater_cb(self, widget, heater, progressbar):
heaterstring = "6.3"
heater = eval(heaterstring)
stap = 1
j = 0.1
heatervalue = widget.get_active()
print heatervalue
progressbar.set_fraction(0.1)
while (stap <= 10 ):
if widget.get_active():
print widget.get_active()
fraction = j * stap
print fraction
progressbar.set_fraction(fraction)
stap = stap + 1
time.sleep(1)
else:
stap = 11
progressbar.set_fraction(0.0)
break
def do_measurement_helper(self, widget, fig):
print " Start measurement thread"
threading.Thread(target=self.do_measurement, args=(widget, fig)).start()
def do_measurement(self, widget, fig):
fig.clear()
ax = fig.add_subplot(111)
x = np.arange(0, 5*np.pi, 0.01)
y = np.sin(x**2)*np.exp(-x)
ax.plot(x, y)
fig.canvas.draw()
def __init__(self):
# Create new Window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("delete_event", self.delete_event)
self.window.show()
mainbox = gtk.HBox(False, 0)
self.window.add(mainbox)
mainbox.show()
leftvbox = gtk.VBox(False, spacing = 10)
mainbox.pack_start(leftvbox, expand = False, fill = False, padding = 0)
leftvbox.show()
rightvbox = gtk.VBox(False, spacing = 10)
mainbox.pack_start(rightvbox, expand = False, fill = False, padding =0)
rightvbox.show()
heaterprogressbar = gtk.ProgressBar()
leftvbox.pack_start(heaterprogressbar, expand = False, fill = False, padding = 0)
heaterprogressbar.show()
heaterbutton = gtk.ToggleButton("Heater")
leftvbox.pack_start(heaterbutton, expand = True, fill = False, padding = 0)
heaterbutton.show()
heaterbutton.connect("toggled", self.heater_helper, heaterbutton, heaterprogressbar)
fig = matplotlib.figure.Figure(figsize=(5,4), dpi=64)
canvas = FigureCanvas(fig)
rightvbox.pack_start(canvas, expand = True, fill = True, padding = 0 )
canvas.show()
measurebutton = gtk.Button("Measure")
rightvbox.pack_start(measurebutton, expand = False, fill = False, padding = 0)
measurebutton.show()
measurebutton.connect("clicked", self.do_measurement_helper, fig)
def main():
gtk.main()
return(0)
if __name__ == "__main__":
gtk.gdk.threads_init()
measure()
main()
gtk.gdk.threads_leave()
Kind regards,
Joris Weijters
Combining threads, Matplotlib, and the GTK main loop is probably not supported and difficult to debug exactly what is going on. My advice is not to do any GUI calls from threads, but instead schedule them using gobject.idle_add().
Threads, Matplotlib and the GTK main loop can be combined, if you keep in mind some things:
I use gobject.threads_init() instead of gtk.gdk.threads_init(), the gtk.gdk variant did not work for me in combination with Matplotlib. I think you can also omit the gtk.gdk.threads_leave().
As ptomato mentioned, you should let the main gtk thread handle anything that has to do with gtk widgets by calling the gobject.idle_add() and gobject.timeout_add() functions.
I usually make a helper function to periodically update the statusbar from a float variable:
def do_measurement(self):
self.data = []
self.progress = 0
self.abort = threading.Event()
gobject.timeout_add(100, self.update_progressbar)
for point in some_generator_yielding_100_values():
if self.abort.is_set():
break
self.data.append(point)
self.progress += 0.01
self.progress = None
def update_progressbar(self):
if self.progress is None:
self.progressbar.set_fraction(0) # reset bar
return False # do not run again
self.progressbar.set_fraction(self.progress)
return True # run again after 100ms
def start_measurement(self):
threading.Thread(target=self.do_measurement).start()
def stop_measurement(self):
self.abort.set()
But you can of course also just call gobject.idle_add(self.progressbar.set_fraction, x) to set the new value x asynchroneously.