如何从CustomTkinter GUI正确启动Flask Web应用并处理异常?
解决CustomTkinter GUI与Flask Web应用联动的问题
核心问题分析
你的问题根源是Flask的app.run()会阻塞主线程,而CustomTkinter的GUI事件循环也依赖主线程处理,直接在GUI流程中启动Flask会导致GUI卡死、服务器无法正常运行,甚至重复创建实例引发异常。
解决方案步骤
1. 用子线程启动Flask,避免阻塞GUI
将Flask的启动逻辑放到独立线程中,让GUI主线程保持响应。同时必须关闭Flask的自动重载功能(use_reloader=False),否则重载机制会创建新进程,引发线程冲突。
修改cword_webapp/app.py:
from flask import Flask, render_template app = Flask(__name__) # 全局变量存储当前填字游戏参数 current_crossword_params = {} @app.route("/") def main(): return render_template( "index.html", word_count=current_crossword_params.get("word_count"), # 其他填字游戏数据参数 ) def start_app(host="127.0.0.1", port=5000, **params): global current_crossword_params current_crossword_params = params # 关闭自动重载,避免子线程冲突 app.run(host=host, port=port, debug=False, use_reloader=False)
2. 在GUI中启动Flask线程并显示访问链接
在CustomTkinter代码中,用threading.Thread启动Flask,自动获取可用端口并生成访问链接,通过消息框展示给用户。如果需要支持局域网访问,可将host设为0.0.0.0。
import threading import socket import customtkinter as ctk from tkinter import messagebox import cword_webapp.app as app class CrosswordGUI(ctk.CTk): def __init__(self): super().__init__() self.flask_thread = None self.flask_port = None # 初始化GUI组件 self.generate_btn = ctk.CTkButton(self, text="生成填字游戏并启动Web", command=self.start_flask_web) self.generate_btn.pack(pady=20) def get_available_port(self): # 自动获取系统可用端口 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("", 0)) return s.getsockname()[1] def start_flask_web(self): # 生成填字游戏数据 crossword = Crossword(params...) # 替换为你的填字游戏生成逻辑 self.flask_port = self.get_available_port() # 启动Flask子线程 self.flask_thread = threading.Thread( target=app.start_app, kwargs={ "port": self.flask_port, "word_count": crossword.word_count, # 其他填字游戏参数 }, daemon=True # 主线程退出时自动终止子线程 ) self.flask_thread.start() # 生成访问链接 access_url = f"http://127.0.0.1:{self.flask_port}" # 弹出消息框展示链接 messagebox.showinfo("Web应用已启动", f"请访问以下链接查看填字游戏:\n{access_url}") # 启动状态检查 self.check_flask_status() def check_flask_status(self): if self.flask_thread and not self.flask_thread.is_alive(): messagebox.showwarning("Web应用已关闭", "填字游戏Web应用已停止运行") else: # 每2秒检查一次线程状态 self.after(2000, self.check_flask_status) if __name__ == "__main__": app = CrosswordGUI() app.mainloop()
3. 检测Web应用关闭并同步到GUI
上述代码中的check_flask_status方法通过threading.Thread.is_alive()检测Flask线程存活状态,每2秒执行一次,一旦线程终止(Web应用关闭),就弹出提示同步GUI状态。
如果需要主动关闭Web应用,可在Flask中添加退出路由,再在GUI中调用:
在cword_webapp/app.py中添加:
from flask import request import sys @app.route("/shutdown", methods=["POST"]) def shutdown(): func = request.environ.get("werkzeug.server.shutdown") if func: func() return "Server shutting down..."
在GUI中添加关闭按钮的逻辑:
import requests def stop_flask_web(self): if self.flask_thread and self.flask_thread.is_alive(): try: requests.post(f"http://127.0.0.1:{self.flask_port}/shutdown") except requests.exceptions.ConnectionError: pass
关键注意事项
- 线程安全:所有GUI操作必须在主线程执行,因此状态检查后的提示需用
after方法确保在主线程运行。 - Debug模式:禁止在子线程中使用Flask的
debug=True,自动重载会创建新进程,导致线程监控失效。 - Daemon线程:设置Flask线程为
daemon=True,确保关闭GUI时线程自动终止,避免残留进程。
内容的提问来源于stack exchange,提问作者tomasvana10
相关产品推荐
相关产品推荐

