Skip to content Skip to sidebar Skip to footer

Retrieve Data From Db & Set To Textinput Fields And Image Widget In Kivy For A Multiscreen App! Attributeerror

I'm learning kivy by cobbling together a small application to understand the behavior of different widgets. What works: The app accepts text and images as input & stores to th

Solution 1:

Problem - 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 - Empty

The problem was self.ids was empty.

Root Cause

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.

  1. At class ScreenOne(Screen):, var = ScreenTwo()
  2. At class SelectableButton(RecycleDataViewBehavior, Button):, var = ScreenTwo()
  3. In one.kv file, ScreenTwo:

Kv language » 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.

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.

Python Code

  1. Remove var = ScreenTwo() from class ScreenOne(Screen): and class SelectableButton(RecycleDataViewBehavior, Button): because you don't need to instantiate another objects of ScreenTwo() which are different from the one instantiated in kv file, one.kv.
  2. In populate_fields() method, replace self.ids.no.text with self.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.
  3. In filechoser() method, remove image_path = self.image_path and return image_path because self.image_path is a class attributes of class ScreenTwo().
  4. In save() method, replace self.ids.no.text and self.ids.name.text with self.user_no_text_input.text and self.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 file - one.kv

  1. Remove all references of id: screen_manager and manager: screen_manager because each screen has by default a property manager that gives you the instance of the ScreenManager used.

kv file - two.kv

  1. In class rule, <SelectableButton>: replace root.var.populate_fields(self) with app.root.screen_two.populate_fields(self)

Screen default property manager

Each screen has by default a property manager that gives you the instance of the ScreenManager used.

Accessing Widgets defined inside Kv lang in your python code

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.

Example

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


classManager(ScreenManager):
    screen_one = ObjectProperty(None)
    screen_two = ObjectProperty(None)


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

    defpopulate_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']

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

    deffilechooser(self):
        Tk().withdraw()
        self.image_path = askopenfilename(initialdir = "/",title = "Select file",filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
        self.image.source = self.image_path

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

    defget_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]

    defsave(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() #NEWclassScreenOne(Screen):
    passclassSelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
                                  RecycleGridLayout):
    ''' Adds selection and focus behaviour to the view. '''classSelectableButton(RecycleDataViewBehavior, Button):
    ''' Add selection support to the Button '''

    index = None
    selected = BooleanProperty(False)
    selectable = BooleanProperty(True)

    defrefresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        returnsuper(SelectableButton, self).refresh_view_attrs(rv, index, data)

    defon_touch_down(self, touch):
        ''' Add selection on touch down '''ifsuper(SelectableButton, self).on_touch_down(touch):
            returnTrueif self.collide_point(*touch.pos) and self.selectable:
            return self.parent.select_with_touch(self.index, touch)

    defapply_selection(self, rv, index, is_selected):
        ''' Respond to the selection of items in the view. '''
        self.selected = is_selected


classOneApp(App):
    defbuild(self):
        return Manager()


if __name__ == "__main__":
    OneApp().run()

one.kv

#:kivy 1.11.0#:include two.kv<Manager>:screen_one:screen_one_idscreen_two:screen_two_idScreenOne:id:screen_one_idname:'screen1'ScreenTwo:id:screen_two_idname:'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 selectioncanvas.before:Color:rgba:(.0,0.9,.1,.3)ifself.selectedelse(0,0,0,1)Rectangle:pos:self.possize:self.sizeon_press:app.root.screen_two.populate_fields(self)<ScreenTwo>:user_no_text_input:nouser_name_text_input:nameimage:imageAccordionItem:title:"INPUT FIELDS"GridLayout:rows:3BoxLayout:size_hint:.5,Noneheight:600pos_hint: {'center_x':1}
                padding:10spacing:3orientation:"vertical"Label:text:"Employee ID"size_hint:(.5,None)height:30TextInput:id:nosize_hint:(.5,None)height:30multiline:FalseLabel:text:"Employee NAME"size_hint:(.5,None)height:30TextInput:id:namesize_hint:(.5,None)height:30multiline:FalseLabel:text:"Employee PHOTO"size_hint:(.5,None)height:30Image:id:imageallow_stretch:Truekeep_ratio:TrueButton:text:"SELECT IMAGE"size_hint_y:Noneheight:self.parent.height*0.2on_release:root.filechooser()Button:id:save_btntext:"SAVE BUTTON"height:50on_press:root.save()AccordionItem:title:"RECYCLE VIEW"BoxLayout:orientation:"vertical"GridLayout:size_hint:1,Nonesize_hint_y:Noneheight:25cols:2Label:text:"Employee ID"Label:text:"Employee Name"# Display only the first two columns Employee ID and Employee Name NOT EmployeePhoto on the RecycleViewBoxLayout:RecycleView:viewclass:'SelectableButton'data:root.data_itemsSelectableRecycleGridLayout:cols:2default_size:None,dp(26)default_size_hint:1,Nonesize_hint_y:Noneheight:self.minimum_heightorientation:'vertical'multiselect:Truetouch_multiselect:TrueButton:text:"On Screen 2 >> Go to Screen 1"on_press:root.manager.current='screen1'

Output

Img01Img02

Post a Comment for "Retrieve Data From Db & Set To Textinput Fields And Image Widget In Kivy For A Multiscreen App! Attributeerror"