First of all, I'm relatively new to python so in case I'm doing something incredibly stupid please let me know.
I have this desktop app to be run on a raspberyy pi and developed with python and tkinter for the GUI and due to new requirements in the project, it needs to be able to receive a command remotely to do some action.
To achieve this i wanted to add a http server to the project
In my main script i have this:
from tkinter import *
from http.server import BaseHTTPRequestHandler, HTTPServer
import threading
# Create GUI app and define general properties
window = 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}")
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"))
#Start HTTP Server
webServer = HTTPServer(('localhost', 9080), HttpHandler)
print('running http server on port: ', 9080)
threading.Thread(target=webServer.serve_forever, daemon=True)
window.mainloop()
Right now the code is very basic just to get it running but the idea was to later on, do an IO operation on a http request. For example /api/lights/on would trigger a GPIO.
I had to use threading.Thread because otherwise the script would block on webServer.serve_forever()
By using threading it no longer blocks and shows the GUI properly. Also with a 'netstat -lnt' I can tell that the http server is listening on the specified port.
When I open the browser on http://127.0.0.1:9080/ the browser never gets a response.
Am I doing something wrong here?
I found two mistakes
you forgot import threading
so it gives error message
import 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