从db&中检索数据为多屏应用程序在Kivy中设置为textinput字段和图像小部件! AttributeError [英] Retrieve data from db & set to textinput fields and image widget in Kivy for a multiscreen app! AttributeError
问题描述
我正在通过组合一个小应用程序来了解不同的小部件的行为,以学习kivy.
I'm learning kivy by cobbling together a small application to understand the behavior of different widgets.
有效的方法:
该应用接受文本和图像作为输入&存储到数据库中,使用RecycleView在按钮上正确显示存储的数据.
The app accepts text and images as input & stores to the database, stored data is correctly displayed on buttons using RecycleView.
问题:
按下RecycleView上的按钮时,应用程序崩溃,并显示以下错误:AttributeError:'super'对象没有属性' getattr '
On pressing the buttons on the RecycleView the app crashes with the error: AttributeError: 'super' object has no attribute 'getattr'
我尝试过的事情:
我从此帖子中了解到,则初始化可能无法完成,并尝试使用kivy时钟进行调度,但这会引发新的错误AttributeError:"float"对象没有属性"index".
I understand from this post, that initialization may not be complete and tried scheduling with kivy clock but this throws a new error AttributeError:'float' object has no attribute 'index'.
预期的行为:
在按钮上单击可在其各自的小部件中设置选定的按钮数据(文本和图像值).我无法理解为什么在多屏环境中无法正常工作.
On button click set selected button data (text & image values) in their respective widgets. I have not been able to understand why this is not working in a multiscreen environment.
完整代码如下.
main.py
import sqlite3
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.accordion import Accordion
from kivy.clock import Clock
from tkinter.filedialog import askopenfilename
from tkinter import Tk
class Manager(ScreenManager):
screen_one = ObjectProperty(None)
screen_two = ObjectProperty(None)
class ScreenTwo(BoxLayout, Screen, Accordion):
data_items = ListProperty([])
def __init__(self, **kwargs):
super(ScreenTwo, self).__init__(**kwargs)
# Clock.schedule_once(self.populate_fields)
self.create_table()
self.get_table_column_headings()
self.get_users()
def populate_fields(self, instance): # NEW
columns = self.data_items[instance.index]['range']
self.ids.no.text = self.data_items[columns[0]]['text']
self.user_name_text_input.text = self.data_items[columns[1]]['text']
def get_table_column_headings(self):
connection = sqlite3.connect("demo.db")
with connection:
cursor = connection.cursor()
cursor.execute("PRAGMA table_info(Users)")
col_headings = cursor.fetchall()
self.total_col_headings = len(col_headings)
def filechooser(self):
Tk().withdraw()
self.image_path = askopenfilename(initialdir = "/",title = "Select file",filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
self.image.source = self.image_path
image_path = self.image_path
return image_path
def create_table(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
sql = """CREATE TABLE IF NOT EXISTS Employees(
EmpID integer PRIMARY KEY,
EmpName text NOT NULL,
EmpPhoto blob NOT NULL)"""
cursor.execute(sql)
connection.close()
def get_users(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
cursor.execute("SELECT * FROM Employees ORDER BY EmpID ASC")
rows = cursor.fetchall()
# create list with db column, db primary key, and db column range
data = []
low = 0
high = self.total_col_headings - 1
# Using database column range for populating the TextInput widgets with values from the row clicked/pressed.
self.data_items = []
for row in rows:
for col in row:
data.append([col, row[0], [low, high]])
low += self.total_col_headings
high += self.total_col_headings
# create data_items
self.data_items = [{'text': str(x[0]), 'Index': str(x[1]), 'range': x[2]} for x in data]
def save(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
EmpID = self.ids.no.text
EmpName = self.ids.name.text
image_path = self.image_path # -- > return value from fielchooser
EmpPhoto = open(image_path, "rb").read()
try:
save_sql="INSERT INTO Employees (EmpID, EmpName, EmpPhoto) VALUES (?,?,?)"
connection.execute(save_sql,(EmpID, EmpName, EmpPhoto))
connection.commit()
connection.close()
except sqlite3.IntegrityError as e:
print("Error: ",e)
self.get_users() #NEW
class ScreenOne(Screen):
var = ScreenTwo()
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Button '''
var = ScreenTwo()
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
class OneApp(App):
def build(self):
return Manager()
if __name__ =="__main__":
OneApp().run()
one.kv
#:kivy 1.10.0
#:include two.kv
<Manager>:
id: screen_manager
screen_one: screen_one_id # original name: our set name
screen_two: screen_two_id
ScreenOne:
id: screen_one_id # our set name
name: 'screen1'
manager: screen_manager # telling each screen who its manager is.
ScreenTwo:
id: screen_two_id # our set name
name: 'screen2'
manager: screen_manager
<ScreenOne>:
Button:
text: "On Screen 1 >> Go to Screen 2"
on_press: root.manager.current = 'screen2'
two.kv
#:kivy 1.10.0
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
on_press:
root.var.populate_fields(self)
<ScreenTwo>:
user_no_text_input: no
user_name_text_input: name
image: image
AccordionItem:
title: "INPUT FIELDS"
GridLayout:
rows:3
BoxLayout:
size_hint: .5, None
height: 600
pos_hint: {'center_x': 1}
padding: 10
spacing: 3
orientation: "vertical"
Label:
text: "Employee ID"
size_hint: (.5, None)
height: 30
TextInput:
id: no
size_hint: (.5, None)
height: 30
multiline: False
Label:
text: "Employee NAME"
size_hint: (.5, None)
height: 30
TextInput:
id: name
size_hint: (.5, None)
height: 30
multiline: False
Label:
text: "Employee PHOTO"
size_hint: (.5, None)
height: 30
Image:
id: image
allow_stretch: True
keep_ratio: True
Button:
text: "SELECT IMAGE"
size_hint_y: None
height: self.parent.height * 0.2
on_release: root.filechooser()
Button:
id: save_btn
text: "SAVE BUTTON"
height: 50
on_press: root.save()
AccordionItem:
title: "RECYCLE VIEW"
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 2
Label:
text: "Employee ID"
Label:
text: "Employee Name"
# Display only the first two columns Employee ID and Employee Name NOT EmployeePhoto on the RecycleView
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: root.data_items
SelectableRecycleGridLayout:
cols: 2
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
Button:
text: "On Screen 2 >> Go to Screen 1"
on_press: root.manager.current = 'screen1'
很长的帖子很抱歉,谢谢您的时间和关注.
Apologies for the really long post, I thank you for your time and attention.
推荐答案
问题-AttributeError
self.ids.no.text = self.data_items[columns[0]]['text']
File "kivy/properties.pyx", line 841, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
self.ids-空
问题是self.ids
为空.
ScreenTwo() 类的三个实例.如果应用id()
函数,它将显示三个不同的存储器地址/位置. self.ids
仅在解析kv文件时可用.因此,self.ids
仅在 one.kv 文件中实例化的实例中可用.
There were three instances of class ScreenTwo(). If you apply id()
function, it will show three different memory addresses/locations. self.ids
is only available when the kv file is parsed. Therefore, self.ids
is only available in the instance instantiated in one.kv file.
- 在
class ScreenOne(Screen):
,var = ScreenTwo()
- 在
class SelectableButton(RecycleDataViewBehavior, Button):
,var = ScreenTwo()
- 在 one.kv 文件中,
ScreenTwo:
- At
class ScreenOne(Screen):
,var = ScreenTwo()
- At
class SelectableButton(RecycleDataViewBehavior, Button):
,var = ScreenTwo()
- In one.kv file,
ScreenTwo:
当解析您的kv文件时,kivy会收集所有标有的小部件 id,并将其放在此 self.ids 字典类型属性中.
When your kv file is parsed, kivy collects all the widgets tagged with id’s and places them in this self.ids dictionary type property.
解决方案
在提供的示例中,我正在使用一个包含表Users
和列UserID
和UserName
的SQLite3数据库.有关详细信息,请参阅示例.
Solution
In the example provided, I am using a SQLite3 database containing table, Users
with columns, UserID
and UserName
. Please refer to the example for details.
- 从
class ScreenOne(Screen):
和class SelectableButton(RecycleDataViewBehavior, Button):
中删除var = ScreenTwo()
,因为您不需要实例化ScreenTwo()
的另一个对象,该对象不同于在kv文件one.kv
中实例化的对象. - 在
populate_fields()
方法中,将self.ids.no.text
替换为self.user_no_text_input.text
,因为在kv文件(two.kv
)中,已经存在一个ObjectProperty,已定义user_no_text_input
并将其连接到TextInput的IDno
,即user_no_text_input: no
- 在
filechoser()
方法中,删除image_path = self.image_path
和return image_path
,因为self.image_path是类ScreenTwo()
的类属性. - 在
save()
方法中,将self.ids.no.text
和self.ids.name.text
分别替换为self.user_no_text_input.text
和self.user_name_text_input.text
,因为它们是定义的,并且已连接到kv文件*加上,通常认为使用 ObjectProperty 是最佳实践".这将创建直接引用,提供更快的访问权限,并且更加明确.*
- Remove
var = ScreenTwo()
fromclass ScreenOne(Screen):
andclass SelectableButton(RecycleDataViewBehavior, Button):
because you don't need to instantiate another objects ofScreenTwo()
which are different from the one instantiated in kv file,one.kv
. - In
populate_fields()
method, replaceself.ids.no.text
withself.user_no_text_input.text
because in kv file (two.kv
), there is already an ObjectProperty,user_no_text_input
defined and hooked to TextInput's id,no
i.e.user_no_text_input: no
. - In
filechoser()
method, removeimage_path = self.image_path
andreturn image_path
because self.image_path is a class attributes of classScreenTwo()
. - In
save()
method, replaceself.ids.no.text
andself.ids.name.text
withself.user_no_text_input.text
andself.user_name_text_input.text
respectively because they are defined and hooked-up to the TextInputs in kv file,two.kv
*plus it is generally regarded as ‘best practice’ to use the ObjectProperty. This creates a direct reference, provides faster access and is more explicit.*
kv文件-one.kv
- 删除所有
id: screen_manager
和manager: screen_manager
的引用,因为默认情况下每个屏幕都有一个属性manager
,该属性为您提供所使用的 ScreenManager 的实例.
- Remove all references of
id: screen_manager
andmanager: screen_manager
because each screen has by default a propertymanager
that gives you the instance of the ScreenManager used.
kv文件-two.kv
- 在类规则中,
<SelectableButton>:
将root.var.populate_fields(self)
替换为app.root.screen_two.populate_fields(self)
- In class rule,
<SelectableButton>:
replaceroot.var.populate_fields(self)
withapp.root.screen_two.populate_fields(self)
每个屏幕默认都有一个属性管理器,可为您提供 使用的ScreenManager实例.
Each screen has by default a property manager that gives you the instance of the ScreenManager used.
在您的python代码中访问在Kv lang内部定义的小部件
尽管
self.ids
方法非常简洁,但通常 被视为使用 ObjectProperty 的最佳做法".这 创建直接参考,提供更快的访问权限以及更多 明确的.
Although the
self.ids
method is very concise, it is generally regarded as ‘best practice’ to use the ObjectProperty. This creates a direct reference, provides faster access and is more explicit.
示例
main.py
import sqlite3
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.properties import BooleanProperty, ListProperty, ObjectProperty
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.accordion import Accordion
from tkinter.filedialog import askopenfilename
from tkinter import Tk
class Manager(ScreenManager):
screen_one = ObjectProperty(None)
screen_two = ObjectProperty(None)
class ScreenTwo(BoxLayout, Screen, Accordion):
data_items = ListProperty([])
def __init__(self, **kwargs):
super(ScreenTwo, self).__init__(**kwargs)
self.create_table()
self.get_table_column_headings()
self.get_users()
def populate_fields(self, instance): # NEW
columns = self.data_items[instance.index]['range']
self.user_no_text_input.text = self.data_items[columns[0]]['text']
self.user_name_text_input.text = self.data_items[columns[1]]['text']
def get_table_column_headings(self):
connection = sqlite3.connect("demo.db")
with connection:
cursor = connection.cursor()
cursor.execute("PRAGMA table_info(Users)")
col_headings = cursor.fetchall()
self.total_col_headings = len(col_headings)
def filechooser(self):
Tk().withdraw()
self.image_path = askopenfilename(initialdir = "/",title = "Select file",filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
self.image.source = self.image_path
def create_table(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
sql = """CREATE TABLE IF NOT EXISTS Users(
UserID integer PRIMARY KEY,
UserName text NOT NULL)"""
cursor.execute(sql)
connection.close()
def get_users(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
cursor.execute("SELECT * FROM Users ORDER BY UserID ASC")
rows = cursor.fetchall()
# create list with db column, db primary key, and db column range
data = []
low = 0
high = self.total_col_headings - 1
# Using database column range for populating the TextInput widgets with values from the row clicked/pressed.
self.data_items = []
for row in rows:
for col in row:
data.append([col, row[0], [low, high]])
low += self.total_col_headings
high += self.total_col_headings
# create data_items
self.data_items = [{'text': str(x[0]), 'Index': str(x[1]), 'range': x[2]} for x in data]
def save(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
UserID = self.user_no_text_input.text
UserName = self.user_name_text_input.text
EmpPhoto = open(self.image_path, "rb").read()
try:
save_sql = "INSERT INTO Users (UserID, UserName) VALUES (?,?)"
connection.execute(save_sql, (UserID, UserName))
connection.commit()
connection.close()
except sqlite3.IntegrityError as e:
print("Error: ", e)
self.get_users() #NEW
class ScreenOne(Screen):
pass
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Button '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
class OneApp(App):
def build(self):
return Manager()
if __name__ == "__main__":
OneApp().run()
one.kv
#:kivy 1.11.0
#:include two.kv
<Manager>:
screen_one: screen_one_id
screen_two: screen_two_id
ScreenOne:
id: screen_one_id
name: 'screen1'
ScreenTwo:
id: screen_two_id
name: 'screen2'
<ScreenOne>:
Button:
text: "On Screen 1 >> Go to Screen 2"
on_press: root.manager.current = 'screen2'
two.kv
#:kivy 1.11.0
<SelectableButton>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (.0, 0.9, .1, .3) if self.selected else (0, 0, 0, 1)
Rectangle:
pos: self.pos
size: self.size
on_press:
app.root.screen_two.populate_fields(self)
<ScreenTwo>:
user_no_text_input: no
user_name_text_input: name
image: image
AccordionItem:
title: "INPUT FIELDS"
GridLayout:
rows:3
BoxLayout:
size_hint: .5, None
height: 600
pos_hint: {'center_x': 1}
padding: 10
spacing: 3
orientation: "vertical"
Label:
text: "Employee ID"
size_hint: (.5, None)
height: 30
TextInput:
id: no
size_hint: (.5, None)
height: 30
multiline: False
Label:
text: "Employee NAME"
size_hint: (.5, None)
height: 30
TextInput:
id: name
size_hint: (.5, None)
height: 30
multiline: False
Label:
text: "Employee PHOTO"
size_hint: (.5, None)
height: 30
Image:
id: image
allow_stretch: True
keep_ratio: True
Button:
text: "SELECT IMAGE"
size_hint_y: None
height: self.parent.height * 0.2
on_release: root.filechooser()
Button:
id: save_btn
text: "SAVE BUTTON"
height: 50
on_press: root.save()
AccordionItem:
title: "RECYCLE VIEW"
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 2
Label:
text: "Employee ID"
Label:
text: "Employee Name"
# Display only the first two columns Employee ID and Employee Name NOT EmployeePhoto on the RecycleView
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: root.data_items
SelectableRecycleGridLayout:
cols: 2
default_size: None, dp(26)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
Button:
text: "On Screen 2 >> Go to Screen 1"
on_press: root.manager.current = 'screen1'
输出
这篇关于从db&中检索数据为多屏应用程序在Kivy中设置为textinput字段和图像小部件! AttributeError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!