Kivy中BoxLayout添加ScrollView失败:‘已存在父控件’报错求助
解决Kivy中ScrollView嵌套BoxLayout时的WidgetException错误
你遇到的这个WidgetException是Kivy里很常见的问题——同一个控件不能被添加到多个父容器中。我们先拆解错误原因,再一步步修复你的代码:
错误核心原因
在你的Songs类__init__方法里,你先把self.song_select_layout添加到了ScrollView:
root = ScrollView() root.add_widget(self.song_select_layout) self.add_widget(root)
但之后又执行了:
self.add_widget(self.song_select_layout)
这就导致同一个BoxLayout被同时挂载到了ScrollView和Screen两个父控件上,违反了Kivy的控件树规则,所以抛出了错误。
另外还有一个语法错误:你把super的调用写成了函数定义形式,这会导致Screen的初始化不完整:
# 错误写法 def super(Songs, self).__init__(**kwargs) # 正确写法 super(Songs, self).__init__(**kwargs)
额外优化点
为了让ScrollView真正能滚动,需要给你的BoxLayout设置size_hint_y=None,并让它的高度自适应内容(通过bind方法关联height和minimum_height),否则BoxLayout会被拉伸到ScrollView的可视高度,不会触发滚动。
修正后的完整代码
import os from kivy.app import App from kivy.uix.screenmanager import ScreenManager, Screen from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.label import Label from kivy.uix.scrollview import ScrollView class Mainscreen(Screen): def __init__(self, **kwargs): super(Mainscreen,self).__init__(**kwargs) self.layoutMs = BoxLayout(orientation='vertical') self.songButton = Button(text='Songs') self.pressed=False self.layoutMs.add_widget(Label(text='Welcome To CarsonMusic!')) self.songButton.bind(on_press=self.switch_to_songs) self.layoutMs.add_widget(self.songButton) self.add_widget(self.layoutMs) def switch_to_songs(self, *args): self.manager.current='song_screen' class Songs(Screen): titleP = '' current_artist='' def __init__(self, **kwargs): # 修复super调用的语法错误 super(Songs, self).__init__(**kwargs) self.Lsong = [] self.Lartist = [] self.LsongFil = [] self.filebase= os.getcwd() self.filebaseM = (str(self.filebase)+"/Music/") print (self.filebaseM) self.listfil = os.listdir(self.filebaseM) self.listfil = [phrase.replace('.mp3', '') for phrase in self.listfil] self.Lsong=self.listfil # 给BoxLayout设置size_hint_y=None,并绑定高度自适应 self.song_select_layout = BoxLayout(orientation='vertical', size_hint_y=None) self.song_select_layout.bind(minimum_height=self.song_select_layout.setter('height')) self.song_select_layout.buttons = [] self.back_button=Button(text='Back') self.back_button.bind(on_press=self.back_to_main) self.song_select_layout.add_widget(self.back_button) self.np_text=Label(text='Song Playing Now Is') self.np_song=Label() self.np_Atext=Label(text='By') self.np_artist=Label() root = ScrollView() root.add_widget(self.song_select_layout) self.add_widget(root) self.curr_art = "Default" for song in self.Lsong: button = Button(text=song, font_size=20, color=(0, 0, 0, 1), size_hint_y=None, height=40) button.bind(on_press=self.songPlay) self.song_select_layout.buttons.append(button) self.song_select_layout.add_widget(button) # 移除重复添加BoxLayout到Screen的代码 # self.add_widget(self.song_select_layout) self.song_select_layout.add_widget(self.np_text) self.song_select_layout.add_widget(self.np_song) self.song_select_layout.add_widget(self.np_Atext) self.song_select_layout.add_widget(self.np_artist) def back_to_main(self, *args): self.manager.current='main_screen' def songPlay(self, *args): # 这里可以补充你的播放逻辑 pass class CarsonMusic(App): def build(self): sm = ScreenManager() main_screen = Mainscreen(name='main_screen') song_screen = Songs(name='song_screen') sm.add_widget(main_screen) sm.add_widget(song_screen) return sm if __name__ == "__main__": CarsonMusic().run()
关键修改说明
- 修复了
super调用的语法错误,确保Screen正确初始化 - 删除了重复添加
self.song_select_layout到Screen的代码,避免控件多父容器的问题 - 给
BoxLayout设置size_hint_y=None,并绑定minimum_height到自身height,让内容高度自适应,触发ScrollView的滚动 - 给动态创建的按钮也设置了
size_hint_y=None和固定高度,保证每个按钮的高度一致,更美观
内容的提问来源于stack exchange,提问作者CarsonH26
相关产品推荐
相关产品推荐

