我试图动态填充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:无法添加,它已经具有父级
答案 0 :(得分:0)
在for loop
中,实例的第一个add_widget()
运行正常。但是从template
开始的同一实例的第二add_widget()
抛出了错误。因为template
已经有父母了。
有两种解决方法。
创建一个新的kv文件template.kv,并在template
循环中添加此对象的新实例。
for
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'
在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'))
循环中添加此对象的新实例。
for
<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