为什么 httpserver 不会与 tkinter 在单独的线程上
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()
现在代码非常基础,只是为了得到它 运行ning 但我的想法是稍后对 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 --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()
我只是想知道使用 http.server
是否是个好主意。如果你想通过网页访问,那么用 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
首先,我是 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()
现在代码非常基础,只是为了得到它 运行ning 但我的想法是稍后对 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 --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()
我只是想知道使用 http.server
是否是个好主意。如果你想通过网页访问,那么用 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