Kivy中如何准确获取TextInput按实际显示折行划分的每行文本
Kivy TextInput 软折行文本提取可靠方案
别直接用_lines属性拿结果,这是TextInput的内部中间变量,存的是混了硬换行、没走完渲染校验的临时数据,碰到组件刚调整尺寸、文本刚更新的时序,行数统计错误、内容串行都是常事。texture_size属性TextInput确实支持,但没必要靠这个自己算折行——组件本身渲染的时候已经算过一遍准确的折行位置了,直接拿渲染层的结果就行,比自己重写折行逻辑靠谱得多。
具体实现
首先卡准取数时机:别在组件初始化的时候读,那会尺寸、字体渲染都没跑完,拿什么值都不准。给on_size、on_text事件绑回调,用Clock.schedule_once延迟1帧等渲染走完再取值,就不会拿到中间状态的错误数据。
取值直接读TextInput内部负责文本渲染的_label实例的lines属性,这个列表里存的就是实打实画到屏幕上的每行内容,和用户看到的软折效果完全一致。
可直接复用的代码示例:
from kivy.app import App from kivy.uix.textinput import TextInput from kivy.clock import Clock class WrappedTextInput(TextInput): def get_visible_wrapped_lines(self): # 渲染实例未初始化时直接返回空 if not getattr(self, '_label', None): return [] # 过滤掉渲染预留的尾部空行占位 return [line for line in self._label.lines if line] def on_size(self, *args): # 尺寸变更后等1帧渲染完成再触发计算 Clock.schedule_once(lambda dt: print(self.get_visible_wrapped_lines()), 0) def on_text(self, *args): Clock.schedule_once(lambda dt: print(self.get_visible_wrapped_lines()), 0) class TestWrapApp(App): def build(self): ti = WrappedTextInput(text="Hello I came from the earth and my name is") return ti if __name__ == "__main__": TestWrapApp().run()
效果说明
- 组件宽度足够折为2行时,输出为
["Hello I came from the", "earth and my name is"] - 拉窄组件折为3行时,输出为
["Hello I came", "from the earth", "and my name is"] - 碰到硬换行、中英文混排、特殊字符的场景,返回结果和实际显示完全匹配,不会出现
_lines偶发的错位问题。
别自己手动算文本宽度拆行,不同操作系统、不同字体的字宽、字间距渲染有细微差异,自己算出来的折行位置总会和实际显示差几个像素,凑不对位置。
内容的提问来源于stack exchange,提问作者Ten Kho
相关产品推荐
相关产品推荐

