Python tkinter 初探Toplevel控件搭建父子窗口

目录

Toplevel控件搭建父子窗口

最简明的父子窗口框架

改进一:屏蔽和开放按钮

改进二:子窗口始终在主窗口之上

改进三:增加子窗口的关闭协议

改进四:使子窗口长获焦点

总结


Toplevel控件搭建父子窗口

最近,用Python给单位里用的"智慧食堂"系统编制了一个餐卡充值文件生成器,自动匹配餐卡号并快速生成导入数据用的Excel表格,截图如下:

使用tkinter Toplevel控件弹出子窗口,用作设置备注的子窗口。在编程过程中,边学边写探索到不少新知识,简单介绍如下:

最简明的父子窗口框架

创建一个主窗口、一个子窗口,各放一个按钮,代码如下:

python 复制代码
import win32api, tkinter as tk

def _toplevel():
    top = tk.Toplevel(root)
    top.title("Toplevel Window")
    W,H=400,300
    top.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')

    btn_Close = tk.Button(top, text="Close", command=top.destroy)
    btn_Close.pack()  

if __name__=='__main__':
    
    # 创建主窗口
    root = tk.Tk()
    root.title("Main Window")
    # 获取windows系统桌面分辨率
    X,Y=win32api.GetSystemMetrics(0),win32api.GetSystemMetrics(1)
    W,H=600,480
    root.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')

    # 创建一个打开Toplevel窗口的按钮
    btn_Open = tk.Button(root, text="Open Toplevel", command=_toplevel)
    btn_Open.pack()

    # 运行Tkinter事件循环
    root.mainloop()

上述代码的缺点是主窗口上的Open按钮可以反复点击打开多个子窗口,要想办法按需要来屏蔽它的点击功能。

改进一:屏蔽和开放按钮

以下代码可以调整按钮的使用状态:tk.DISABLED、tk.NORMAL

button.config(state=tk.DISABLED)

button.config(state=tk.NORMAL)

打开子窗口时,Open按钮的状态改为tk.DISABLED,此时已无法点击了。

python 复制代码
import win32api, tkinter as tk  

class TopWindow:
    def __init__(self, parent):  
        top = self.top = tk.Toplevel(parent)
        top.title("Toplevel Window")
        W,H=400,300
        top.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')
        btn_Close = tk.Button(top, text="Close", command=self.on_close)
        btn_Close.pack()
    def on_close(self):
        btn_Open.config(state=tk.NORMAL)
        self.top.destroy()

def on_open():
    TopWindow(root)
    btn_Open.config(state=tk.DISABLED)

if __name__=='__main__':  

    root = tk.Tk()  
    root.title("Main Window")  
    X,Y=win32api.GetSystemMetrics(0),win32api.GetSystemMetrics(1)
    W,H=600,480
    root.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')

    btn_Open = tk.Button(root, text="Open Toplevel", command=on_open)
    btn_Open.pack()
    root.mainloop()

改进二:子窗口始终在主窗口之上

top.transient(root) # 设置Toplevel窗口始终在主窗口root的上方

python 复制代码
import win32api, tkinter as tk  

class TopWindow:
    def __init__(self, parent):  
        top = self.top = tk.Toplevel(parent)
        top.title("Toplevel Window")
        W,H=400,300
        top.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')
        top.transient(root) # 设置Toplevel窗口始终在主窗口上方
        btn_Close = tk.Button(top, text="Close", command=self.on_close)
        btn_Close.pack()
    def on_close(self):
        btn_Open.config(state=tk.NORMAL)
        self.top.destroy()

def on_open():
    TopWindow(root)
    btn_Open.config(state=tk.DISABLED)

if __name__=='__main__':  

    root = tk.Tk()  
    root.title("Main Window")  
    X,Y=win32api.GetSystemMetrics(0),win32api.GetSystemMetrics(1)
    W,H=600,480
    root.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')

    btn_Open = tk.Button(root, text="Open Toplevel", command=on_open)
    btn_Open.pack()
    root.mainloop()

另外一种方法也能设置子窗口永远在前:

top.wm_attributes('-topmost', True) # 设置Toplevel窗口在所有窗口的上方

两种方法的区别在于后者是全局的设置,它使得子窗口在操作系统中所有窗口的上面,包括其它应用程序的窗口。

如下图,请比较一下与上一张截图的效果有啥区别:

改进三:增加子窗口的关闭协议

如下图,直接点击子窗口右上关闭按钮,只触发窗口默认的top.destroy事件。这样关闭子窗口后,主窗口的按钮状态并不能恢复;以下代码使得子窗口的"WM_DELETE_WINDOW"关闭协议绑定了自定义的关闭事件self.onclose:

top.protocol("WM_DELETE_WINDOW", self.on_close)

完整代码如下:

python 复制代码
import win32api, tkinter as tk  

class TopWindow:
    def __init__(self, parent):  
        top = self.top = tk.Toplevel(parent)
        top.title("Toplevel Window")
        W,H=400,300
        top.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')
        top.transient(root)
        top.protocol("WM_DELETE_WINDOW", self.on_close)
        btn_Close = tk.Button(top, text="Close", command=self.on_close)
        btn_Close.pack()
    def on_close(self):
        btn_Open.config(state=tk.NORMAL)
        self.top.destroy()

def on_open():
    TopWindow(root)
    btn_Open.config(state=tk.DISABLED)

if __name__=='__main__':  

    root = tk.Tk()  
    root.title("Main Window")  
    X,Y=win32api.GetSystemMetrics(0),win32api.GetSystemMetrics(1)
    W,H=600,480
    root.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')

    btn_Open = tk.Button(root, text="Open Toplevel", command=on_open)
    btn_Open.pack()
    root.mainloop()

改进四:使子窗口长获焦点

top.grab_set() # 确保Toplevel窗口长获焦点

使用这个方法,前面提到的按钮状态的切换以及子窗口绑定关闭协议的代码都不需要了,非常简洁。top.grab_set() 配合 top.transient(root) 共同使用(如下标注红色部分),效果最佳:

import win32api, tkinter as tk

class TopWindow:

def init(self, parent):

top = self.top = tk.Toplevel(parent)

top.title("Toplevel Window")

W,H=400,300

top.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')
top.grab_set()
top.transient(root)

btn_Close = tk.Button(top, text="Close", command=top.destroy)

btn_Close.pack()

def on_open():

TopWindow(root)

if name=='main':

root = tk.Tk()

root.title("Main Window")

X,Y=win32api.GetSystemMetrics(0),win32api.GetSystemMetrics(1)

W,H=600,480

root.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')

btn_Open = tk.Button(root, text="Open Toplevel", command=on_open)

btn_Open.pack()

root.mainloop()


源代码复制框如下:

python 复制代码
import win32api, tkinter as tk  

class TopWindow:
    def __init__(self, parent):  
        top = self.top = tk.Toplevel(parent)
        top.title("Toplevel Window")
        W,H=400,300
        top.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')
        top.grab_set()
        top.transient(root)
        btn_Close = tk.Button(top, text="Close", command=top.destroy)
        btn_Close.pack()

def on_open():
    TopWindow(root)

if __name__=='__main__':  

    root = tk.Tk()  
    root.title("Main Window")  
    X,Y=win32api.GetSystemMetrics(0),win32api.GetSystemMetrics(1)
    W,H=600,480
    root.geometry(f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}')

    btn_Open = tk.Button(root, text="Open Toplevel", command=on_open)
    btn_Open.pack()
    root.mainloop()

总结

通过对toplevel控件的编程操练,掌握了tkinter子窗口的调用方法,了解了topleve的多种特殊方法、响应事件以及绑定协议。

相关推荐
张张123y7 分钟前
# 后端第七课:FastAPI 实战入门(用刚学的 Python 写第一个 API 接口)
开发语言·python·mysql·api·fastapi
CCblandy8 分钟前
一致性哈希到底省了多少搬迁?本机实测 3 轮:取模 6→7 搬 85.8%,V=128 只搬 14.3%,虚拟节点太少会被倾斜骗
后端·python·云原生·性能优化·架构
Yan-英杰13 分钟前
【论文精读】 基于 TensorFlow 对卷积神经网络的优化研究:异构式 CPU+GPU 协同计算 + ReLU-Swish 激活函数
python·卷积神经网络
小陈不好吃20 分钟前
Java 泛型与类型擦除:从 PECS 原则到 TypeToken 绕过
java·开发语言·windows·pecs 原则·typetoken绕过
ydjs678899-bot32 分钟前
如何在Java中打印pdf静默打印和打印对话框选择
java·python·pdf
Shaoshing34 分钟前
存储引擎与架构
java·开发语言·架构
额额额对了1 小时前
Linux 驱动开发:基于 platform 总线 + misc 设备的 DS18B20 单总线温度驱动
linux·c语言·开发语言·驱动开发·嵌入式硬件
devilnumber1 小时前
Java 代理模式(简单理解回顾)
java·开发语言·代理模式
把我格子衫拿来1 小时前
误差预算怎么做?从雅可比到蒙特卡洛的三维定位不确定度分析
python·蒙特卡洛·三维定位·误差分析·不确定度
invicinble1 小时前
对服务器资源的理解--理解用户请求,部署,服务器资源相关的映射层
开发语言