通过递归遍历应用根节点下的所有子控件,筛选出 Label 实例并手动更新其 font_name 属性,即可在不重启应用的前提下实时生效用户选择的默认字体。
通过递归遍历应用根节点下的所有子控件,筛选出 label 实例并手动更新其 `font_name` 属性,即可在不重启应用的前提下实时生效用户选择的默认字体。
在 Kivy 中,修改 Config.set('kivy', 'default_font', ...) 仅影响新创建的控件,对已渲染的 Label、Button 等继承自 LabelBase 的文本类控件无效。因此,要实现运行时全局字体切换,必须主动通知现有控件更新其 font_name 属性。
核心思路是:编写一个递归函数,深度遍历当前应用的 UI 树(从 App.get_running_app().root 开始),识别所有 Label(以及可选的 Button、TextInput 等含 font_name 的控件),并将其 font_name 显式设为当前配置中的字体路径。
以下是一个完整、健壮的实现示例:
from kivy.app import App
from kivy.config import Config
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
def update_font_recursive(widget):
# 支持多种文本控件(可根据需要扩展)
if isinstance(widget, (Label, Button, TextInput)):
try:
# 读取最新配置值;注意:Config.get 返回字符串或列表,需适配
default_font_config = Config.get('kivy', 'default_font')
if isinstance(default_font_config, list) and len(default_font_config) > 0:
font_path = default_font_config[0]
else:
font_path = default_font_config # 兜底处理
widget.font_name = font_path
except Exception as e:
print(f"Warning: failed to set font for {type(widget).__name__}: {e}")
# 递归处理子控件(兼容 Screen、BoxLayout、GridLayout 等容器)
if hasattr(widget, 'children') and widget.children:
for child in widget.children:
update_font_recursive(child)
? 关键注意事项:
- ✅ Config.write() 必须调用,确保下次启动时保留用户选择;
- ✅ update_font_recursive(App.get_running_app().root) 应在字体配置更新后立即执行;
- ⚠️ 若使用 ScreenManager,确保 root 已完成构建(例如在 on_font_selected 回调末尾调用);
- ⚠️ 部分自定义组件若内部封装了 Label,需在其类中重载 font_name 属性或提供刷新接口;
- ? 进阶优化:可缓存字体路径、监听 on_font_name 变更、或结合 kivy.properties.ObjectProperty 实现响应式更新。
最后,在设置页中触发更新逻辑(如按钮回调):
def on_font_selected(self, font_filename, font_filepath):
Config.set('kivy', 'default_font', [font_filename, font_filepath] * 5) # 按 Kivy 要求补全5项
Config.write()
update_font_recursive(App.get_running_app().root)
该方案轻量、兼容性强,适用于绝大多数 Kivy 2.x/3.x 项目,是实现运行时主题字体切换的推荐实践。










