You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()

关键修改说明

  1. 修复了super调用的语法错误,确保Screen正确初始化
  2. 删除了重复添加self.song_select_layout到Screen的代码,避免控件多父容器的问题
  3. 给BoxLayout设置size_hint_y=None,并绑定minimum_height到自身height,让内容高度自适应,触发ScrollView的滚动
  4. 给动态创建的按钮也设置了size_hint_y=None和固定高度,保证每个按钮的高度一致,更美观

内容的提问来源于stack exchange,提问作者CarsonH26

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:44:52