繁体   English   中英

为什么 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()

现在代码非常基本,只是为了让它运行,但想法是稍后,对 http 请求执行 IO 操作。 例如 /api/lights/on 会触发 GPIO。

我不得不使用 threading.Thread,否则脚本会在 webServer.serve_forever() 上阻塞

通过使用线程,它不再阻塞并正确显示 GUI。 同样通过 .netstat -lnt' 我可以看出 http 服务器正在侦听指定的端口。

当我在http://127.0.0.1:9080/上打开浏览器时,浏览器从未得到响应。

我在这里做错了什么吗?

我发现了两个错误

  1. 你忘了导入threading所以它给出了错误信息

    import threading
  2. 你创建了线程但你忘了启动它

    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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM