Running a Tkinter form in a separate thread
From your comments it sound's like you do not need a GUI at all. Just write the image to disk and call an external viewer.
On most systems it should be possible to launch the default viewer using something like this:
import subprocess
subprocess.Popen("yourimage.png")
How to set tkinter textvariable running on separate Thread?
Most GUIs don't like to change values in widgets in separate thread.
You should rather use queue
to send value to thread and it should use root.after(time, function)
to run periodically function which will get value from queue and update value in GUI.
import tkinter as tk # PEP8: `import *` is not preferred
from threading import Thread
import queue
import time # simulate show program
class GUI(Thread):
def __init__(self, queue):
super().__init__()
self.queue = queue
self.start()
def run(self):
self.root = tk.Tk()
self.var = tk.StringVar()
self.var.set("Initiated")
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
width = int(screen_width*.12)
height = int(screen_height)
x = int(screen_width - width)
y = int(screen_height*.025)
self.root.geometry(f'{width}x{height}+{x}+{y}')
label = tk.Label(self.root, textvariable=self.var)
label.pack()
# run first time after 100ms (0.1 second)
self.root.after(100, self.check_queue)
self.root.mainloop()
def check_queue(self):
#if not self.queue.empty():
while not self.queue.empty():
i = self.queue.get()
self.var.set(f'Current Iteration: {i}')
# run again after 100ms (0.1 second)
self.root.after(100, self.check_queue)
def main():
q = queue.Queue()
gui = GUI(q)
for i in range(1000):
q.put(i)
# simulate show program
time.sleep(0.5)
if __name__ == '__main__':
main()
PEP 8 -- Style Guide for Python Code
Why httpserver won't on separate thread with tkinter
I found two mistakes
you forgot import
threading
so it gives error messageimport threading
you created thread but you forgot to start it
t = threading.Thread(target=webServer.serve_forever, daemon=True)
t.start()
BTW:
You could better organize code.
See: PEP 8 --Style Guide for Python Code
import threading
import tkinter as tk # PEP8: `import *` is not preferred
from http.server import BaseHTTPRequestHandler, HTTPServer
# --- classes ---
class HttpHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes("<html><head><title>https://pythonbasics.org</title></head>", "utf-8"))
self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
self.wfile.write(bytes("<body>", "utf-8"))
self.wfile.write(bytes("<p>This is an example web server.</p>", "utf-8"))
self.wfile.write(bytes("</body></html>", "utf-8"))
# --- functions ---
# empty
# --- main ---
webServer = HTTPServer(('localhost', 9080), HttpHandler)
print('running http server: http://localhost:9080') # some consoles will display URL as clickable so it is easier to run browser
t = threading.Thread(target=webServer.serve_forever, daemon=True)
t.start()
window = tk.Tk()
#window.attributes("-fullscreen", True)
window.config(cursor="none")
#winWidth = int(window.winfo_screenwidth() * 1)
#winHeight = int(window.winfo_screenheight() * 1)
#window.geometry(f"{winWidth}x{winHeight}")
window.mainloop()
I only wondering if using http.server
is good idea. If you want to access by web page then it can be simpler to create pages with Flask
. If you want to send small commands then maybe it would be simpler use server MQTT
instead of HTTP
. Some IoT
devices my already use MQTT
Other problem can make communications between threads. Tkinter doesn't like to run in subthreads so you can't access widget directly in thread with server and it will need queue to send values to main thread and tkinter will need after(millisecond, function)
to check queue periodically to get command.
EDIT:
Version which uses queue
to send information from http server
to tkinter
and it displays it in widget Text
import threading
import tkinter as tk # PEP8: `import *` is not preferred
from http.server import BaseHTTPRequestHandler, HTTPServer
import queue
# --- classes ---
class HttpHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(bytes("<html><head><title>https://pythonbasics.org</title></head>", "utf-8"))
self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
self.wfile.write(bytes("<body>", "utf-8"))
self.wfile.write(bytes("<p>This is an example web server.</p>", "utf-8"))
self.wfile.write(bytes("</body></html>", "utf-8"))
q.put("Request: %s" % self.path) # put in `queue`
# --- functions ---
def check_queue():
if not q.empty():
text.insert('end', q.get()+'\n') # get from `queue` and put in `Text`
window.after(100, check_queue) # check again after 100ms
# --- main ---
q = queue.Queue()
webServer = HTTPServer(('localhost', 9080), HttpHandler)
print('running http server: http://localhost:9080') # some consoles will display URL as clickable so it is easier to run browser
t = threading.Thread(target=webServer.serve_forever, daemon=True)
t.start()
window = tk.Tk()
text = tk.Text(window)
text.pack()
check_queue()
window.mainloop()
EDIT:
The same with Flask
. It can get data: args, json, form, files, etc.
import queue
import threading
import tkinter as tk # PEP8: `import *` is not preferred
from flask import Flask, request, render_template_string
# --- classes ---
# --- functions ---
app = Flask(__name__)
#@app.route('/')
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def index(path):
print('path:', path)
print(f'Request: {request.method} {request.url}')
print('args:', request.args)
print('form:', request.form)
print('data:', request.data)
print('json:', request.json)
print('files:', request.files)
q.put(f'Request: {request.method} {request.url}') # put in `queue`
return render_template_string('''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>https://pythonbasics.org</title>
</head>
<body>
<p>Request: {{ request.method }} {{ request.url }}</p>
<p>This is an example web server.</p>
</body>
</html>''', request=request)
def check_queue():
if not q.empty():
text.insert('end', q.get()+'\n') # get from `queue` and put in `Text`
window.after(100, check_queue) # check again after 100ms
# --- main ---
q = queue.Queue()
print('running http server: http://localhost:9080') # some consoles will display URL as clickable so it is easier to run browser
t = threading.Thread(target=app.run, args=('localhost', 9080), daemon=True)
t.start()
window = tk.Tk()
text = tk.Text(window)
text.pack()
check_queue()
window.mainloop()
But now question is: why to use tkinter
if you may do all in flask
multithreading from a tkinter app
I'm not too familiar with tkinter so i can't show you ze code, but you should look at tkinter's event system, particularly firing custom events.
This question may help you get started
Update Tkinter GUI from a separate thread running a command
In tkinter, you can submit an event from a background thread to the GUI thread using event_generate. This allows you to update widgets without threading errors.
- Create the tkinter objects in the main thread
- Bind the root to a vitual event (ie << event1 >>), specifying an event handler. Arrow brackets are required in the event name.
- Start the background thread
- In the background thread, use
event_generate
to trigger the event in the main thread. Use thestate
property to pass data (number) to the event. - In the event handler, process the event
Here's an example:
from tkinter import *
import datetime
import threading
import time
root = Tk()
root.title("Thread Test")
print('Main Thread', threading.get_ident()) # main thread id
def timecnt(): # runs in background thread
print('Timer Thread',threading.get_ident()) # background thread id
for x in range(10):
root.event_generate("<<event1>>", when="tail", state=123) # trigger event in main thread
txtvar.set(' '*15 + str(x)) # update text entry from background thread
time.sleep(1) # one second
def eventhandler(evt): # runs in main thread
print('Event Thread',threading.get_ident()) # event thread id (same as main)
print(evt.state) # 123, data from event
string = datetime.datetime.now().strftime('%I:%M:%S %p')
lbl.config(text=string) # update widget
#txtvar.set(' '*15 + str(evt.state)) # update text entry in main thread
lbl = Label(root, text='Start') # label in main thread
lbl.place(x=0, y=0, relwidth=1, relheight=.5)
txtvar = StringVar() # var for text entry
txt = Entry(root, textvariable=txtvar) # in main thread
txt.place(relx = 0.5, rely = 0.75, relwidth=.5, anchor = CENTER)
thd = threading.Thread(target=timecnt) # timer thread
thd.daemon = True
thd.start() # start timer loop
root.bind("<<event1>>", eventhandler) # event triggered by background thread
root.mainloop()
thd.join() # not needed
Output (note that the main and event threads are the same)
Main Thread 5348
Timer Thread 33016
Event Thread 5348
......
I added an Entry widget to test if the StringVar
can be updated from the background thread. It worked for me, but you can update the string in the event handler if you prefer. Note that updating the string from multiple background threads could be a problem and a thread lock should be used.
Note that if the background threads exits on its own, there is no error. If you close the application before it exits, you will see the 'main thread' error.
Tkinter is opening new windows when running a function in a thread
Since the child process will inherit resource from parent process, that means it will inherit tkinter from parent process. Put the initialization of tkinter inside if __name__ == '__main__'
block may solve the problem:
from tkinter import *
from multiprocessing import Process
import time
def threadFunction():
print('started')
time.sleep(5)
print('done')
def start():
thread1 = Process(target=threadFunction)
thread2 = Process(target=threadFunction)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
if __name__ == '__main__':
window = Tk()
window.title("Test threadinng")
window.geometry('400x400')
btn = Button(window, text="Click Me", command=start)
btn.grid(column=1, row=1)
window.mainloop()
Related Topics
How to Replace Back Slash Character with Empty String in Python
Python Argparse Ignore Unrecognised Arguments
Python Script Execute Commands in Terminal
How to Run a Python Script in a Web Page
How to "Zip Sort" Parallel Numpy Arrays
Typeerror: 'List' Object Is Not Callable While Trying to Access a List
Is There an Expression for an Infinite Iterator
How to Use Append with Pickle in Python
How to Switch Columns Rows in a Pandas Dataframe
Python: Why Does My List Change When I'm Not Actually Changing It
Python Webdriver to Handle Pop Up Browser Windows Which Is Not an Alert
How to Loop Through a List by Twos
How to Print Utf-8 Encoded Text to the Console in Python < 3
How to Generate Random Numbers That Are Different
How to Convert a List to a List of Tuples
Printing Without Newline (Print 'A',) Prints a Space, How to Remove