You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 23:20:55