如何使用tkinter的ttk.Button实现OSX原生Tab+空格导航交互?
问题概述
我希望基于tkinter实现模拟OSX原生交互的UI控制逻辑:通过Tab键循环切换组件聚焦状态、聚焦后按空格键触发组件功能,同时保持接近操作系统原生的GUI外观效果,聚焦状态需有明确的视觉高亮提示。目前ttk的多数组件都已支持该交互逻辑,仅按钮控件存在适配问题。
该需求具体包含以下要求:
- 用户可通过Tab键让组件获得“focused(聚焦)”状态
- 组件聚焦后需显示可视化高亮效果
- 组件处于聚焦状态时,可通过空格键触发对应功能
- 难点在于需保持接近操作系统原生的GUI外观效果
相关StackOverflow参考问题
我在本站找到的最接近的参考是相关提问。需要注意的是该提问是针对Python 2.7.9版本提出的。该提问的内容很有意思,它指出将tk.Button()替换为ttk.Button()可解决聚焦高亮问题,但我实际测试后发现结果相反:tk.Button()在OSX上可实现聚焦高亮但视觉效果粗糙,而ttk.Button()完全没有聚焦状态的视觉反馈。以下是适配2021年Python 3.X版本运行的测试代码:
import tkinter as tk from tkinter import ttk class App(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) self.master = master entry1 = tk.Entry(self) entry1.pack() entry2 = tk.Entry(self) entry2.pack() # 将下方代码修改为ttk.Button()即可测试 button1 = tk.Button(self, text="Test", command=self.yup) button1.pack() def yup(self): print("yup") root = tk.Tk() app = App(root).pack() root.mainloop()
我已尝试的解决方案
方案一:常规使用ttk.Button(),搭配ttk.Style()自定义聚焦提示样式
优点:
- 使用操作系统原生样式
- 支持Tab键聚焦、空格键触发功能
缺点: - 无默认聚焦视觉提示,需手动通过
ttk.Style()强制设置 - 据我所知
ttk.Style()无法设置高亮边框,仅能通过调整文字颜色区分聚焦状态
示例代码:
from tkinter import Tk from tkinter import ttk root = Tk() def one(): print("one") def two(): print("two") style = ttk.Style() style.configure('C.TButton') style.map('C.TButton', foreground = [('pressed','red'),('focus','blue')], background = [('pressed','!disabled','red'),('focus','white')], relief=[('pressed', 'sunken'), ('!pressed', 'raised')]) # 定义底部GUI B1 = ttk.Button(root, text='Button One', command=one, style='C.TButton') B1.pack(padx=20, pady=(20,0)) B2 = ttk.Button(root, text='Button Two', command=two, style='C.TButton') B2.pack(padx=20, pady=10) root.mainloop()
方案二:直接使用tk.Button()控件
优点:
- 支持Tab键聚焦、空格键触发功能
- 原生支持聚焦状态边框高亮
缺点: - 视觉效果不佳,高亮边框为普通矩形且存在对齐问题
- OSX系统下
activebackground、highlightthickness等参数无法正常生效,样式自定义空间有限
示例代码:
import tkinter as tk from tkinter import ttk root = tk.Tk() def one(): print("one") def two(): print("two") B1 = tk.Button(root, text='Button One', command=one) B1.pack(padx=20, pady=(20,0)) B2 = tk.Button(root, text='Button Two', command=two) B2.pack(padx=20, pady=10) root.mainloop()
方案三:使用tkmacosx库提供的Button()控件
优点:
- 专门针对该适配问题开发
- Tab按下后会显示类OSX原生风格的聚焦高亮
- 完全符合我的所有功能需求
缺点: - 聚焦状态下按空格键无法触发按钮功能
该问题比较特殊,根据官方文档takefocus属性说明该功能应正常生效,但我在Catalina 10.15.7系统上测试未实现对应效果。
示例代码:
from tkinter import Tk from tkmacosx import Button root = Tk() def one(): print("one") def two(): print("two") B1 = Button(root, text='Button One', command=one) B1.pack(padx=20, pady=(20,0)) B2 = Button(root, text='Button Two', command=two) B2.pack(padx=20, pady=10) root.mainloop()
总结
我了解tkinter与OSX系统一直存在部分兼容性问题,若需要更精准的原生控件控制可以切换到QT框架。我非常认可tkinter的价值,也希望尽量基于tkinter实现需求。在尝试fork仓库或更换技术方案前,我希望确认是否是我的操作存在问题。
同时针对tkmacosx的空格触发异常问题,希望能得到其他用户的复现确认,以判断是否适合向官方GitHub仓库提交Issue。
非常感谢阅读本文,若需要更多信息可随时告知。
内容的提问来源于stack exchange,提问作者jpodolski

