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

tkinter调用API时加载对话框不显示/应用冻结及对话框按钮移除咨询

问题根源分析

1. 界面冻结、加载框不显示的原因

  • tkinter 所有 UI 操作必须运行在主线程,直接在主线程执行阻塞式的 requests 请求会占用主线程资源,导致 UI 无法重绘,加载框自然无法渲染出来
  • 你尝试的多线程写法错误使用了 join() 方法,该方法会阻塞主线程等待子线程执行完成,本质和直接在主线程跑请求没有区别,所以还是会出现冻结问题
  • 额外注意:tkinter 不支持在子线程中操作 UI 控件,所有加载框关闭、页面跳转的逻辑必须放回主线程执行

2. 移除对话框控制按钮的方法

可根据需求选择两种方案:

  • 完全移除整个标题栏:调用 top.overrideredirect(True),对话框将没有标题栏和任何控制按钮,仅显示你自定义的内容
  • 保留标题栏仅移除最小化、禁用关闭按钮:先设置 top.attributes("-toolwindow", True) 移除最小化、最大化按钮,再绑定关闭事件为空函数 top.protocol("WM_DELETE_WINDOW", lambda: None),用户就无法手动关闭对话框
正确实现代码
import tkinter as tk
from tkinter import ttk
import threading
import requests
import json
import pickle
# 需确保 dashboardPage 已提前正确导入

class login_page(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        # 此处省略你原来的用户名输入框、密码输入框、提交按钮的创建代码
        # username_entry = ...
        # password_entry = ...
        # 提交按钮绑定 login_button_function 即可
        
        def try_login(result_container):
            # 子线程仅处理请求逻辑,不碰任何UI操作
            url = "XXXXXXXXXXXXXXX"
            payload = json.dumps({
                "username": username_entry.get(), 
                "password": password_entry.get()
            })
            headers = {'Content-Type': 'application/json'}
            try:
                response = requests.post(url+"/users/getuser", data=payload, headers=headers)
                result_container.append(response)
            except Exception as e:
                # 请求异常也存入容器方便主线程处理
                result_container.append(e)

        def check_login_result(top, result_container):
            # 主线程轮询检查子线程执行状态
            if not result_container:
                # 未执行完成则100ms后再次检查,不会阻塞UI
                self.after(100, check_login_result, top, result_container)
                return
            # 拿到请求结果后处理后续逻辑
            result = result_container[0]
            top.destroy()
            if isinstance(result, Exception):
                print("请求出错", str(result))
                return
            if result.json()["resultCode"] == 100:
                print("success")
                data = {
                    "realname": result.json()["data"]["realname"],
                    "phone": result.json()["data"]["phone"],
                    "email": result.json()["data"]["email"]
                }
                with open("user_data.dat", "wb") as f:
                    pickle.dump(data, f)
                self.controller.show_frame(dashboardPage.dashboard_page)
            else:
                print("error", result.json().get("msg", "未知错误"))

        def login_button_fuction():
            # 主线程创建并显示加载框
            top = tk.Toplevel(self)
            top.title("loading")
            top.geometry("200x100")
            top.resizable(0, 0)
            # 移除控制按钮,此处用保留标题栏的方案,可按需替换为overrideredirect方案
            top.attributes("-toolwindow", True)
            top.protocol("WM_DELETE_WINDOW", lambda: None)
            bar = ttk.Progressbar(top, orient="horizontal", length=100, mode="indeterminate")
            bar.pack(pady=25)
            bar.start()
            # 加载框置顶,避免被主窗口挡住,同时禁止操作主窗口
            top.transient(self)
            top.grab_set()
            
            # 启动子线程执行请求
            result_container = []
            login_thread = threading.Thread(target=try_login, args=(result_container,))
            login_thread.daemon = True
            login_thread.start()
            
            # 启动轮询检查请求结果
            self.after(100, check_login_result, top, result_container)
核心逻辑说明
  • 使用列表result_container存储子线程的请求结果,利用列表可变的特性实现跨线程数据传递
  • after是tkinter原生的主线程定时调度方法,不会阻塞UI渲染,每隔100ms检查一次请求是否完成
  • 子线程设置daemon=True,主程序关闭时子线程会同步退出,不会出现残留进程

内容的提问来源于stack exchange,提问作者lexi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 14:24:03