Kivy动态将按钮添加到gridlayout会出错

时间:2019-04-20 17:52:05

标签: python python-3.x kivy

我试图动态填充ScrollView内部的Gridlayout。但是,当我尝试添加Builder.load_string生成的按钮时,出现以下错误: kivy.uix.widget.WidgetException:无法添加,它已经有一个父级 我不确定自己在做什么错。 我可以通过构造函数在python中生成每个按钮,但是我不希望重写按钮的生成。

main.py

# IMSA Computational Science and Data Science Club: Brummet Client
# Written for ssh into IMSA SLURM cluster
# Written by: Arthur Lu, Jacob Levine
# Use at one's own risk

__author__ = ("Arthur Lu <alu1@imsa.edu>", "Jacob Levine <jlevine@imsa.edu>")

from kivy.config import Config
#Config.set('graphics', 'resizable', False)

from kivy.app import App

from kivy.properties import StringProperty, ObjectProperty

from kivy.core.window import Window

from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition, NoTransition
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.label import Label

from kivy.lang.builder import Builder

from kivy.clock import Clock

import csv
import paramiko
import time
#import os

def load_csv(filepath):
    with open(filepath, newline='') as csvfile:
        file_array = list(csv.reader(csvfile))
        csvfile.close() 
    return file_array

class Client(Screen):

    def on_pre_enter(self, *args):
        Window.size = (1280, 720)
        Window.top = 100
        Window.left = 100

    def client(self, ssh, sftp):

        self.ssh = ssh
        self.sftp = sftp

        self.sftp.chdir('brummet_projects')

        Clock.schedule_interval(self.auto, 1)

    def auto(self, dt):

        projects = self.sftp.listdir('.')

        for file in projects:

            if file[0] == ".":

                projects.remove(file)

        list_view = self.ids.list_files

        """
        Button:
            background_color: 0,0,0,0
            Image:
                source:'data\customui\client_file_bar.png'
                x: self.parent.x
                y: self.parent.y
                width: self.parent.width
                height: self.parent.height
                allow_stretch: True
                keep_ratio: False

            Image:
                source:'data\customui\python.png'
                y: self.parent.y + 10
                x: - self.parent.width/2 + 25
                width: self.parent.width - 20
                height: self.parent.height - 20

            Label:
                size_hint:(0.9, 1)
                text: "hello there"
                y: self.parent.y
                x: self.parent.x + self.parent.width*0.05
                width: self.parent.width
                height: self.parent.height
                text_size: self.size
                font_size: self.height - 30
                halign: 'left'
                valign: 'middle'

            Label:
                size_hint:(0.9, 1)
                text: "no u"
                y: self.parent.y
                x: self.parent.x + self.parent.width*0.7
                width: self.parent.width
                height: self.parent.height
                text_size: self.size
                font_size: self.height - 30
                halign: 'left'
                valign: 'middle'

            Label:
                size_hint:(0.9, 1)
                text: "dong big dumb"
                y: self.parent.y
                x: self.parent.x + self.parent.width*0.8
                width: self.parent.width
                height: self.parent.height
                text_size: self.size
                font_size: self.height - 30
                halign: 'left'
                valign: 'middle'
        """

        template = Builder.load_string("""
Button:
    background_color: 0,0,0,0
    Image:
        source:'data\customui\client_file_bar.png'
        x: self.parent.x
        y: self.parent.y
        width: self.parent.width
        height: self.parent.height
        allow_stretch: True
        keep_ratio: False

    Image:
        source:'data\customui\python.png'
        y: self.parent.y + 10
        x: - self.parent.width/2 + 25
        width: self.parent.width - 20
        height: self.parent.height - 20

    Label:
        size_hint:(0.9, 1)
        text: "hello there"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.05
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

    Label:
        size_hint:(0.9, 1)
        text: "no u"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.7
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

    Label:
        size_hint:(0.9, 1)
        text: "dong big dumb"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.8
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'
            """)

        print(projects)

        for file in projects:
            list_view.add_widget(template)

class Connect(Screen):
    def on_pre_enter(self, *args):
        Window.size = (600, 300)

    def routine(self, host, port, username, password):

        ssh = None
        sftp = None

        #print(username, password)
        self.ids.status.text = "connecting"

        try:
            self.ids.status.text = "attempting to connect to " + host + ":" + str(port)
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(host, port, username, password)

            transport = paramiko.Transport((host, port))
            transport.connect(username = username, password = password)
            sftp = paramiko.SFTPClient.from_transport(transport)

            self.ids.status.text = "connected to " + host + ":" + str(port)

            Clock.schedule_once(self.continue_to_client, 0.1)
            self.manager.get_screen('client').client(ssh, sftp)


        except Exception as e:
            if sftp is not None:
                sftp.close()
            if ssh is not None:
                ssh.close()

            self.ids.status.text = "connection failed: " + str(e)
            Clock.schedule_once(self.return_to_login, 4)
            #self.manager.current = 'login'

    def return_to_login(self, *args):
        self.manager.transition = SlideTransition(direction = "right")
        self.manager.current = 'login'
            #time.sleep(5)

    def continue_to_client(self, *args):

        self.manager.transition = NoTransition()
        self.manager.current = 'client'

class Login(Screen):

    def on_pre_enter(self, *args):
        Window.size = (600, 300)

    def do_login(self, loginText, passwordText, hostText, portText):
        app = App.get_running_app()

        if hostText == "":
            hostText = "titanrobotics.ddns.net"
        if portText == "":
            portText = "60022"

        host = hostText
        port = int(portText)

        username = loginText
        password = passwordText

        self.manager.transition = SlideTransition(direction = "left")
        self.manager.current = "connect"

        self.manager.get_screen('connect').routine(host, port, username, password)

    def resetForm(self):
        self.ids['login'].text = ""
        self.ids['password'].text = ""


manager = ScreenManager()

class BrummetApp(App):

    username = StringProperty(None)
    password = StringProperty(None)

    title = 'Brummet Client v ' + load_csv("data/meta")[0][1]

    def check_resize(self, instance, x, y):
        # resize X
        #screenName = manager.current
        #print(screenName)
        if manager.current != "client":

            target_x = 600
            target_y = 300


            if x >  target_x:
                Window.size = (target_x, Window.size[1])

            if y > target_y:
                Window.size = (Window.size[0], target_y)

            if x <  target_x:
                Window.size = (target_x, Window.size[1])

            if y < target_y:
                Window.size = (Window.size[0], target_y)

        if manager.current == "client":

            target_x = 1280
            target_y = 720

            if x <  target_x:
                Window.size = (target_x, Window.size[1])

            if y < target_y:
                Window.size = (Window.size[0], target_y)

    def build(self):

        manager.add_widget(Login(name = 'login'))
        manager.add_widget(Connect(name = 'connect'))
        manager.add_widget(Client(name = 'client'))

        Window.bind(on_resize=self.check_resize)

        return manager

if __name__ == '__main__':
    BrummetApp().run()

brummet.kv

<Login>:
    BoxLayout:
        id: login_layout
        orientation: 'vertical'
        padding: [10,10,10,10]
        spacing: 10

        BoxLayout:
            spacing: 10
            orientation:'vertical'

            Label:
                id: title
                text: 'Brummet Client'
                halign: 'center'
                valign: 'middle'
                font_size: 24

            Label:
                text: 'Please log in with IMSA SLURM credentials'
                halign: 'center'
                valign: 'middle'
                font_size: 20

        BoxLayout:
            orientation: 'horizontal'
            Label:
                size_hint: (0.15, 1)
                text: 'Username'
                font_size: 18
                halign: 'left'

            TextInput:
                size_hint: (0.7, 1)
                id: username
                multiline: False
                font_size: 18
                write_tab: False

        BoxLayout:
            orientation: 'horizontal'
            Label:
                size_hint: (0.15, 1)
                text: 'Password'
                halign: 'left'
                font_size: 18

            TextInput:
                size_hint: (0.7, 1)
                id: password
                multiline: False
                password: True
                font_size: 18
                write_tab: False

        BoxLayout:
            orientation: 'horizontal'
            Label:
                size_hint: (0.15, 1)
                text: 'Host'
                halign: 'left'
                font_size: 18

            TextInput:
                size_hint: (0.7, 1)
                hint_text: 'slurm.imsa.edu'
                id: host
                multiline: False
                font_size: 18
                write_tab: False

        BoxLayout:
            orientation: 'horizontal'
            Label:
                size_hint: (0.15, 1)
                text: 'Port'
                halign: 'left'
                font_size: 18

            TextInput:
                size_hint: (0.7, 1)
                input_type: 'number'
                input_filter: 'int'
                hint_text: '22'
                id: port
                multiline: False
                font_size: 18
                write_tab: False

        Button:
            text: 'Log In'
            font_size: 24
            id: submit
            on_press:
                root.do_login(username.text, password.text, host.text, port.text)

<Connect>:
    BoxLayout:
        orientation: 'vertical'
        padding: [0,100,0,100]
        spacing: 0

        Label:
            text:'Logging In'
            font_size: 24
            halign: 'center'
            valign: 'middle'

        Label:
            id: status
            test:''
            font_size: 12
            halign: 'center'
            valign: 'middle'
            text_size: self.size
            size_hint: 1,1
            shorten: True

<Client>:
    BoxLayout:
        orientation: 'horizontal'
        padding: [5, 5, 5, 5]
        spacing: 5
        GridLayout:
            size_hint: (0.2, 1)
            row_default_height: self.height / 10
            row_force_default: True
            cols: 1
            Button:
            Button:
        BoxLayout:
            orientation: 'vertical'
            Button:
                x: 0
                y: 0
                size: (self.parent.width, 50)
                size_hint:(None, None)
                background_color: 0,0,0,0

                Label:
                    size_hint:(0.9, 1)
                    text: "My Projects"
                    y: self.parent.y
                    x: self.parent.x + self.parent.width*0
                    width: self.parent.width
                    height: self.parent.height
                    text_size: self.size
                    font_size: self.height - 10
                    halign: 'left'
                    valign: 'middle'

            Button:
                x: 0
                y: 0
                size: (self.parent.width, 50)
                size_hint:(None, None)
                background_color: 0,0,0,0

                Label:
                    size_hint:(0.9, 1)
                    text: "Project Name"
                    y: self.parent.y
                    x: self.parent.x + self.parent.width*0
                    width: self.parent.width
                    height: self.parent.height
                    text_size: self.size
                    font_size: self.height - 30
                    halign: 'left'
                    valign: 'middle'

                Label:
                    size_hint:(0.9, 1)
                    text: "Type"
                    y: self.parent.y
                    x: self.parent.x + self.parent.width*0.7
                    width: self.parent.width
                    height: self.parent.height
                    text_size: self.size
                    font_size: self.height - 30
                    halign: 'left'
                    valign: 'middle'

                Label:
                    size_hint:(0.9, 1)
                    text: "Date Modified"
                    y: self.parent.y
                    x: self.parent.x + self.parent.width*0.8
                    width: self.parent.width
                    height: self.parent.height
                    text_size: self.size
                    font_size: self.height - 30
                    halign: 'left'
                    valign: 'middle'

            ScrollView:
                GridLayout:
                    id:list_files
                    orientation: "vertical"
                    size_hint_y: None
                    height: self.minimum_height
                    row_default_height:50
                    cols:1

                    Button:
                        background_color: 0,0,0,0
                        Image:
                            source:'data\customui\client_file_bar.png'
                            x: self.parent.x
                            y: self.parent.y
                            width: self.parent.width
                            height: self.parent.height
                            allow_stretch: True
                            keep_ratio: False

                        Image:
                            source:'data\customui\python.png'
                            y: self.parent.y + 10
                            x: - self.parent.width/2 + 25
                            width: self.parent.width - 20
                            height: self.parent.height - 20

                        Label:
                            size_hint:(0.9, 1)
                            text: "hello there"
                            y: self.parent.y
                            x: self.parent.x + self.parent.width*0.05
                            width: self.parent.width
                            height: self.parent.height
                            text_size: self.size
                            font_size: self.height - 30
                            halign: 'left'
                            valign: 'middle'

                        Label:
                            size_hint:(0.9, 1)
                            text: "no u"
                            y: self.parent.y
                            x: self.parent.x + self.parent.width*0.7
                            width: self.parent.width
                            height: self.parent.height
                            text_size: self.size
                            font_size: self.height - 30
                            halign: 'left'
                            valign: 'middle'

                        Label:
                            size_hint:(0.9, 1)
                            text: "dong big dumb"
                            y: self.parent.y
                            x: self.parent.x + self.parent.width*0.8
                            width: self.parent.width
                            height: self.parent.height
                            text_size: self.size
                            font_size: self.height - 30
                            halign: 'left'
                            valign: 'middle'

以下代码会发生错误:

template = Builder.load_string("""
Button:
    background_color: 0,0,0,0
    Image:
        source:'data\customui\client_file_bar.png'
        x: self.parent.x
        y: self.parent.y
        width: self.parent.width
        height: self.parent.height
        allow_stretch: True
        keep_ratio: False

    Image:
        source:'data\customui\python.png'
        y: self.parent.y + 10
        x: - self.parent.width/2 + 25
        width: self.parent.width - 20
        height: self.parent.height - 20

    Label:
        size_hint:(0.9, 1)
        text: "hello there"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.05
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

    Label:
        size_hint:(0.9, 1)
        text: "no u"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.7
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

    Label:
        size_hint:(0.9, 1)
        text: "dong big dumb"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.8
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'
            """)

        print(projects)

        for file in projects:
            list_view.add_widget(template)      

上面的代码应在ScrollView中生成一些按钮,但是会产生以下错误: kivy.uix.widget.WidgetException:无法添加,它已经具有父级

1 个答案:

答案 0 :(得分:0)

根本原因-添加相同的实例,模板

for loop中,实例的第一个add_widget()运行正常。但是从template开始的同一实例的第二add_widget()抛出了错误。因为template已经有父母了。

解决方案-添加新实例,模板

有两种解决方法。

方法1-创建一个新的kv文件 template.kv

创建一个新的kv文件template.kv,并在template循环中添加此对象的新实例。

template.kv

for

main.py

Button:
    background_color: 0,0,0,0
    Image:
        source: 'data\customui\client_file_bar.png'
        x: self.parent.x
        y: self.parent.y
        width: self.parent.width
        height: self.parent.height
        allow_stretch: True
        keep_ratio: False

    Image:
        source: 'data\customui\python.png'
        y: self.parent.y + 10
        x: - self.parent.width/2 + 25
        width: self.parent.width - 20
        height: self.parent.height - 20

    Label:
        size_hint:(0.9, 1)
        text: "hello there"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.05
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

    Label:
        size_hint:(0.9, 1)
        text: "no u"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.7
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

    Label:
        size_hint:(0.9, 1)
        text: "dong big dumb"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.8
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

方法2-添加新类

在kv文件中创建类规则,并在class Client(Screen): ... def auto(self, dt): projects = self.sftp.listdir('.') for file in projects: if file[0] == ".": projects.remove(file) list_view = self.ids.list_files print(projects) for file in projects: list_view.add_widget(Builder.load_file('template.kv')) 循环中添加此对象的新实例。

摘要-kv文件

for

代码段-main.py

<FileTemplate>:
    background_color: 0,0,0,0
    Image:
        source: 'data\customui\client_file_bar.png'
        x: self.parent.x
        y: self.parent.y
        width: self.parent.width
        height: self.parent.height
        allow_stretch: True
        keep_ratio: False

    Image:
        source: 'data\customui\python.png'
        y: self.parent.y + 10
        x: - self.parent.width/2 + 25
        width: self.parent.width - 20
        height: self.parent.height - 20

    Label:
        size_hint:(0.9, 1)
        text: "hello there"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.05
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

    Label:
        size_hint:(0.9, 1)
        text: "no u"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.7
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'

    Label:
        size_hint:(0.9, 1)
        text: "dong big dumb"
        y: self.parent.y
        x: self.parent.x + self.parent.width*0.8
        width: self.parent.width
        height: self.parent.height
        text_size: self.size
        font_size: self.height - 30
        halign: 'left'
        valign: 'middle'
...

<Client>:
    ...
            ScrollView:
                GridLayout:
                    id:list_files
                    orientation: "vertical"
                    size_hint_y: None
                    height: self.minimum_height
                    row_default_height:50
                    cols:1