[英]Why httpserver won't on separate thread with tkinter
首先,我对 python 比较陌生,所以如果我做了一些非常愚蠢的事情,请告诉我。
我有这个桌面应用程序要在 raspberyy pi 上运行并使用 python 和 tkinter 开发用于 GUI,并且由于项目中的新要求,它需要能够远程接收命令以执行某些操作。
为此,我想在项目中添加一个 http 服务器
在我的主脚本中,我有这个:
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()
现在代码非常基本,只是为了让它运行,但想法是稍后,对 http 请求执行 IO 操作。 例如 /api/lights/on 会触发 GPIO。
我不得不使用 threading.Thread,否则脚本会在 webServer.serve_forever() 上阻塞
通过使用线程,它不再阻塞并正确显示 GUI。 同样通过 .netstat -lnt' 我可以看出 http 服务器正在侦听指定的端口。
当我在http://127.0.0.1:9080/上打开浏览器时,浏览器从未得到响应。
我在这里做错了什么吗?
我发现了两个错误
你忘了导入threading
所以它给出了错误信息
import threading
你创建了线程但你忘了启动它
t = threading.Thread(target=webServer.serve_forever, daemon=True) t.start()
顺便提一句:
您可以更好地组织代码。
请参阅: PEP 8 --Python 代码的样式指南
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()
我只是想知道使用http.server
是否是个好主意。 如果您想通过 web 页面访问,那么使用Flask
创建页面会更简单。 如果您想发送小命令,那么使用服务器MQTT而不是HTTP
可能会更简单。 一些IoT
设备我已经在使用MQTT
其他问题可以使线程之间进行通信。 Tkinter 不喜欢在子线程中运行,因此您不能直接在服务器线程中访问小部件,它需要队列将值发送到主线程,而 tkinter 需要after(millisecond, function)
定期检查队列以获取命令。
编辑:
使用queue
将信息从http server
发送到tkinter
并将其显示在小部件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()
编辑:
与Flask
相同。 它可以获取数据:args、json、表单、文件等。
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()
但现在的问题是:如果你可以在flask
中完成所有操作,为什么要使用tkinter
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.