Kivy开发中点击单词按钮触发的Label无法正常显示如何解决
问题背景
- 已实现基础功能:从预设列表读取句子,将句子转为按钮排布在嵌套于FloatLayout的BoxLayout中,支持自定义句子显示位置;点击句子按钮后,原按钮移除,替换为句子拆分出的多个独立单词按钮。
- 待实现需求:在BoxLayout下方添加Label控件,点击任意单词按钮时,Label立刻显示与被点击按钮完全一致的文本。例:点击句子按钮
| Hello World, this is an example sentence. |后拆分出多个单词按钮,点击sentence按钮时,下方Label同步显示sentence。 - 现存问题:运行代码后点击单词按钮,Label无任何内容显示,原代码如下:
import kivy from kivy.app import App from kivy.uix.button import Button from kivy.uix.floatlayout import FloatLayout from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Widget, Label from kivy.config import Config sentences = ['This is example sentence 1.', 'This is example sentence 2.', 'This is example sentence 3.', 'This is example sentence 4.', 'This is example sentence 5.', 'This is example sentence 6.'] words = [ ['This', 'is' , 'example' , 'sentence' , '1' ], ['This', 'is' , 'example' , 'sentence' , '2' ], ['This', 'is' , 'example' , 'sentence' , '3' ], ['This', 'is' , 'example' , 'sentence' , '4' ], ['This', 'is' , 'example' , 'sentence' , '5' ], ['This', 'is' , 'example' , 'sentence' , '6' ] ] class AdaptiveButton(Button): """自适应尺寸按钮,大小匹配文本内容""" def __init__(self, **kwargs): super(AdaptiveButton, self).__init__(**kwargs) self.size_hint = (None, None) self.bind(texture_size = self.setter("size")) class Boxlayout(BoxLayout): """排布所有交互控件""" def __init__(self, **kwargs): super(Boxlayout, self).__init__(**kwargs) self.orientation = "vertical" self.spacing = "10dp" for i, line in enumerate(sentences): abtn = AdaptiveButton(text = line, font_size=30) abtn.bind(on_press = lambda btn, j = i : self.destroy_then_create(btn, j)) self.add_widget(abtn) def destroy_then_create(self, btn, index): self.remove_widget(btn) box = BoxLayout(size_hint = (None, None), spacing = "10dp") box.bind(minimum_size = box.setter("size")) sentence = words[index] i = len(sentences)-index-1 for word in sentence: abtn = AdaptiveButton(text = word) abtn.bind(on_press=lambda btn, j=i: show(word)) # 绑定标签显示逻辑 box.add_widget(abtn) self.add_widget(box, i) class Meaningclass(Label): # 标签类 pass def show(k): # 标签更新函数 MeC = Meaningclass() Meaning = Label(text=k, font_size=30) MeC.add_widget(Meaning) class MyApp(App): def build(self): self.icon = 'budspencer.png' Fl = FloatLayout() Box = Boxlayout(pos_hint={'x': .23, 'y': .58}) meaning = Meaningclass(pos_hint={'x': .9, 'y': .1}) Fl.add_widget(Box) Fl.add_widget(meaning) # 将标签添加到FloatLayout return Fl if __name__ == "__main__": MyApp().run()
核心问题点
- 完全没操作界面上已存在的Label实例:
show函数每次触发都会新建一个没挂载到任何布局上的Meaningclass和内部Label,这些控件根本不在界面渲染树里,不可能显示出来。 - 存在闭包陷阱:单词按钮绑定事件时用的
word是循环变量,循环结束后所有按钮的回调拿到的word都是最后一个单词的值,和按钮实际文本不匹配。 - 冗余嵌套:
Meaningclass本身已经继承了Label,不需要再往里面嵌套一个新的Label控件,属于多余写法。 - 引用断裂:单词按钮的回调逻辑拿不到已经添加到FloatLayout里的那个
meaning标签实例,没法修改它的文本属性。
修改后可运行代码
修改逻辑:
- 删掉全局无效的
show函数,给Boxlayout增加参数接收目标Label的实例引用,存在类属性里供回调调用。 - 修复闭包问题,点击单词按钮时直接取按钮自身的
text属性赋值给Label,完全避免循环变量取值错误。 - 简化
Meaningclass,初始化时直接设置好字体大小属性,去掉冗余的子Label嵌套。
import kivy from kivy.app import App from kivy.uix.button import Button from kivy.uix.floatlayout import FloatLayout from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label sentences = ['This is example sentence 1.', 'This is example sentence 2.', 'This is example sentence 3.', 'This is example sentence 4.', 'This is example sentence 5.', 'This is example sentence 6.'] words = [ ['This', 'is' , 'example' , 'sentence' , '1' ], ['This', 'is' , 'example' , 'sentence' , '2' ], ['This', 'is' , 'example' , 'sentence' , '3' ], ['This', 'is' , 'example' , 'sentence' , '4' ], ['This', 'is' , 'example' , 'sentence' , '5' ], ['This', 'is' , 'example' , 'sentence' , '6' ] ] class AdaptiveButton(Button): """自适应尺寸按钮,大小匹配文本内容""" def __init__(self, **kwargs): super(AdaptiveButton, self).__init__(**kwargs) self.size_hint = (None, None) self.bind(texture_size = self.setter("size")) class Boxlayout(BoxLayout): """排布所有交互控件,接收外部传入的目标显示标签引用""" def __init__(self, meaning_label, **kwargs): super(Boxlayout, self).__init__(**kwargs) self.orientation = "vertical" self.spacing = "10dp" # 存下要更新的Label实例 self.meaning_label = meaning_label for i, line in enumerate(sentences): abtn = AdaptiveButton(text = line, font_size=30) abtn.bind(on_press = lambda btn, j = i : self.destroy_then_create(btn, j)) self.add_widget(abtn) def destroy_then_create(self, btn, index): self.remove_widget(btn) box = BoxLayout(size_hint = (None, None), spacing = "10dp") box.bind(minimum_size = box.setter("size")) sentence = words[index] insert_pos = len(sentences)-index-1 for word in sentence: abtn = AdaptiveButton(text=word, font_size=30) # 直接取按钮自身的text赋值给目标Label,避免闭包问题 abtn.bind(on_press=lambda clicked_btn: setattr(self.meaning_label, 'text', clicked_btn.text)) box.add_widget(abtn) self.add_widget(box, insert_pos) class Meaningclass(Label): """显示单词文本的标签""" def __init__(self, **kwargs): super().__init__(**kwargs) self.font_size = 30 class MyApp(App): def build(self): self.icon = 'budspencer.png' Fl = FloatLayout() # 先创建Label实例 meaning = Meaningclass(pos_hint={'x': .5, 'y': .1}) # 把Label实例传给Boxlayout,供按钮回调更新 Box = Boxlayout(meaning_label=meaning, pos_hint={'x': .23, 'y': .58}) Fl.add_widget(Box) Fl.add_widget(meaning) return Fl if __name__ == "__main__": MyApp().run()
内容的提问来源于stack exchange,提问作者Delicious_pie
相关产品推荐
相关产品推荐

