Kivy динамически добавляя кнопки в gridlayout, выдает ошибку - PullRequest
0 голосов
/ 20 апреля 2019

Я пытаюсь заполнить Gridlayout внутри ScrollView динамически. Однако, когда я пытаюсь добавить кнопки, сгенерированные 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 голосов
/ 21 апреля 2019

Основная причина - добавление того же экземпляра, шаблон

В for loop первый add_widget() экземпляра template работал нормально. Но второй add_widget() того же экземпляра template on-wards выбрасывает ошибку. Потому что у template уже есть родитель.

Решение - добавление нового экземпляра, шаблон

Существует два решения проблемы.

Метод 1 - Создать новый файл kv, template.kv

Создайте новый файл kv, template.kv и добавьте новый экземпляр этого объекта в цикл for.

template.kv

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'

main.py

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

Метод 2 - Добавление нового класса

Создайте правило класса в файле kv и добавьте новый экземпляр этого объекта в цикл for.

Фрагменты - файл kv

<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

Фрагменты - main.py

class FileTemplate(Button):
    pass


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(FileTemplate())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...