test.kv
:kivy 1.10.0
<CRUD>:
title: self.mode + " State"
size_hint: None, None
size: 350, 350
auto_dismiss: False
BoxLayout:
orientation: "vertical"
GridLayout:
cols: 2
Label:
text: root.label_rec_id
Label:
id: userid
text: root.col_data[0] # root.userid
Label:
text: "First Name"
TextInput:
id: fname
text: root.col_data[1] # root.fname
Label:
text: "Last Name"
TextInput:
id: lname
text: root.col_data[2] # root.lname
Button:
size_hint: 1, 0.4
text: "Save Changes"
on_release:
root.package_changes(fname.text, lname.text)
app.root.update_changes(root)
root.dismiss()
Button:
size_hint: 1, 0.4
text: "Cancel Changes"
on_release: root.dismiss()
when click on any row then edit form open.Can i use same form for add user.
At this time i click on add user then user.py file run and open a new form.how to use same form for both add update.
Yes, you can use the same form to add city. In the example, I added a button, a method add_record, some variable e.g. mode of type StringProperty, and INSERT SQL command. Please refer to the example for details.
Example
main.py
import kivy
kivy.require('1.10.0') # replace with your current kivy version !
import sqlite3 as lite
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty, NumericProperty
from kivy.lang import Builder
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from kivy.core.window import Window
Window.size = (500, 500)
MAX_TABLE_COLS = 3
con = lite.connect('company.db')
# con.text_factory = str
cur = con.cursor()
class CRUD(Popup):
"""CRUD - Create, Read, Update, Delete"""
label_id_text = ObjectProperty(None)
label_id_data = ObjectProperty(None)
mode = StringProperty("")
label_rec_id = StringProperty("UserID")
start_point = NumericProperty(0)
col_data = ListProperty(["", "", ""])
def __init__(self, obj, **kwargs):
super(CRUD, self).__init__(**kwargs)
self.mode = obj.mode
if obj.mode == "Add":
self.label_id_text.opacity = 0 # invisible
self.label_id_data.opacity = 0 # invisible
else:
self.label_id_text.opacity = 1 # visible
self.label_id_data.opacity = 1 # visible
self.start_point = obj.start_point
self.col_data[0] = obj.rv_data[obj.start_point]["text"]
self.col_data[1] = obj.rv_data[obj.start_point + 1]["text"]
self.col_data[2] = obj.rv_data[obj.start_point + 2]["text"]
def package_changes(self, fname, lname):
self.col_data[1] = fname
self.col_data[2] = lname
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)
rv_data = ObjectProperty(None)
start_point = NumericProperty(0)
mode = StringProperty("")
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
self.rv_data = rv.data
def on_press(self):
self.mode = "Update"
self.start_point = 0
end_point = MAX_TABLE_COLS
rows = len(self.rv_data) // MAX_TABLE_COLS
for row in range(rows):
if self.index in list(range(end_point)):
break
self.start_point += MAX_TABLE_COLS
end_point += MAX_TABLE_COLS
popup = CRUD(self)
popup.open()
class RV(BoxLayout):
rv_data = ListProperty([])
start_point = NumericProperty(0)
mode = StringProperty("")
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_users()
def get_users(self):
'''This result retrieve from database'''
self.rv_data = []
cur.execute("SELECT * FROM Users ORDER BY UserID ASC")
rows = cur.fetchall()
# create data_items
for row in rows:
for col in row:
self.rv_data.append(col)
def add_record(self):
self.mode = "Add"
popup = CRUD(self)
popup.open()
def update_changes(self, obj):
if obj.mode == "Add":
# insert record into Database Table
cur.execute("INSERT INTO Users VALUES(NULL, ?, ?)",
(obj.col_data[1], obj.col_data[2]))
else:
# update Database Table
cur.execute("UPDATE Users SET FirstName=?, LastName=? WHERE UserID=?",
(obj.col_data[1], obj.col_data[2], obj.col_data[0]))
con.commit()
self.get_users()
class ListUser(App):
title = "Users"
def build(self):
self.root = Builder.load_file('main.kv')
return RV()
if __name__ == '__main__':
ListUser().run()
main.kv
#:kivy 1.10.0
<CRUD>:
label_id_text: label_id_text
label_id_data: label_id_data
title: self.mode + " State"
size_hint: None, None
size: 350, 350
auto_dismiss: False
BoxLayout:
orientation: "vertical"
GridLayout:
cols: 2
Label:
id: label_id_text
text: "ID"
Label:
id: label_id_data
text: root.col_data[0] # root.userid
Label:
text: "First Name"
TextInput:
id: fname
text: root.col_data[1] # root.fname
Label:
text: "Last Name"
TextInput:
id: lname
text: root.col_data[2] # root.lname
Button:
size_hint: 1, 0.4
text: "Confirm " + root.mode
on_release:
root.package_changes(fname.text, lname.text)
app.root.update_changes(root)
root.dismiss()
Button:
size_hint: 1, 0.4
text: "Cancel " + root.mode
on_release: root.dismiss()
<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
<RV>:
BoxLayout:
orientation: "vertical"
Button:
size_hint: 1, 0.1
text: "Add Record"
on_press: root.add_record()
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 3
Label:
text: "ID"
Label:
text: "First Name"
Label:
text: "Last Name"
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in root.rv_data]
SelectableRecycleGridLayout:
cols: 3
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
Output
Related
So, I'm trying to create a list that pulls from a mysql database that displays an order # as a button and a new window expands to display the order numbers items/subitems with checkboxes to have checked when an item is selected. I have the window to pop up; however, nothing populates and my query prints out information/list. After all boxes are checked and a Finish button is clicked on, it will remove the order from the list to show it has been worked on. I found this on the forums; however, it's not pulling anything for me:
How to fetch data from database and show in table in kivy+python
Thank you!
main.py
import pymysql
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.popup import Popup
orderNum = "1"
class TextInputPopup(Popup):
obj = ObjectProperty(None)
obj_text = StringProperty("")
def __init__(self, obj, **kwargs):
super(TextInputPopup, self).__init__(**kwargs)
self.obj = obj
self.obj_text = obj.text
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
def on_press(self):
popup = TextInputPopup(self)
popup.open()
def update_changes(self, txt):
self.text = txt
class RV(BoxLayout):
data_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_order()
def get_order(self):
connection = pymysql.connect(host='localhost', user='root',passwd='password', db='Shop')
cursor = connection.cursor()
cursor.execute("SELECT itemName, ITEM FROM order_list WHERE orderID=%s", (orderNum,))
rows = cursor.fetchall()
# create data_items
for row in rows:
for col in row:
self.data_items.append(col)
class TestApp(App):
title = "Kivy RecycleView & pymysql Demo"
def build(self):
return RV()
if __name__ == "__main__":
TestApp().run()
kivy.kv
#:kivy 1.10.0
<TextInputPopup>:
title: "Popup"
size_hint: None, None
size: 400, 400
auto_dismiss: False
BoxLayout:
orientation: "vertical"
TextInput:
id: txtinput
text: root.obj_text
Button:
size_hint: 1, 0.2
text: "Finish"
on_release:
root.obj.update_changes(txtinput.text)
root.dismiss()
Button:
size_hint: 1, 0.2
text: "Cancel Changes"
on_release: root.dismiss()
<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
<RV>:
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 2
Label:
text: "Order Number"
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in 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
Here's my list that pulls information and uses a print function:
('Sub', 'Italian')
('Sub', 'Meatball')
('Drink', 'Apple Juice')
('Side', 'Salad')
('Soup', 'Chicken and rice')
What I have and What I'm Trying to Achieve
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 the database, stored data is correctly displayed on buttons using RecycleView.
Problem:
On pressing the buttons on the RecycleView the app crashes with the error: AttributeError: 'super' object has no attribute 'getattr'
What I've tried:
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'.
Expected behavior:
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.
The full code is as follows.
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.
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.
At class ScreenOne(Screen):, var = ScreenTwo()
At class SelectableButton(RecycleDataViewBehavior, Button):, var = ScreenTwo()
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
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.
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.
In filechoser() method, remove image_path = self.image_path and return image_path because self.image_path is a class attributes of class ScreenTwo().
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
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
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
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'
Output
I'm learning kivy by making small applications from kivy docs and other online resources. The current code has
two textinput fields (UserID, UserName) to store values in the db, using RecycleView the stored data is displayed
on buttons with the Kivy GUI.
On button press, I require corresponding data from the db to be set to the respective textinput fields. i.e. On
pressing button with UserID 1 , textinput field should display values of UserID and UserNames from that row.
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
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 RV(BoxLayout):
''' Creates Db conn, table, and saves data, retrives stored data and
displays in the RecycleView .'''
data_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.create_table()
self.get_users()
def create_table(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
sql = """CREATE TABLE IF NOT EXISTS Users(
UserID integer PRIMAY 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 data_items
for row in rows:
for col in row:
self.data_items.append(col)
def save(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
UserID = self.ids.no.text
UserName = self.ids.name.text
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)
class TestApp(App):
title = "Kivy RecycleView & SQLite3 Demo"
def build(self):
return RV()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
<TextInputPopup>:
title: "Popup"
size_hint: None, None
size: 400, 400
auto_dismiss: False
BoxLayout:
orientation: "vertical"
TextInput:
id: txtinput
text: root.obj_text
Button:
size_hint: 1, 0.2
text: "Save Changes"
on_release:
root.obj.update_changes(txtinput.text)
root.dismiss()
Button:
size_hint: 1, 0.2
text: "Cancel Changes"
on_release: root.dismiss()
<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
<RV>:
BoxLayout:
orientation: "vertical"
user_no_text_input: no
user_name_text_input: name
Label:
text: "USER NUMBER"
size_hint: (.5, None)
height: 30
TextInput:
id: no
size_hint: (.5, None)
height: 30
multiline: False
Label:
text: "USER NAME"
size_hint: (.5, None)
height: 30
TextInput:
id: name
size_hint: (.5, None)
height: 30
multiline: False
Button:
id: save_btn
text: "SAVE BUTTON"
height: 50
width: 100
on_press: root.save()
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 2
Label:
text: "User ID"
Label:
text: "User Name"
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in 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
Solution
The solution is as follow: Please refer to snippets, example, and output for details.
kv file
Add on_press event under class rule, <SelectableButton>:. When on_press event is fire, it will invoke root.on_press(self) method in the root class, RV, and pass an instance of the button, self.
Move the two ObjecProperty hooks (user_no_text_input: no, user_name_text_input: name) to the right location i.e. from under BoxLayout: to <RV>:.
Replace data: [{'text': str(x)} for x in root.data_items] with data: root.data_items
Snippet - kv
<SelectableButton>:
...
on_press:
app.root.on_press(self)
<RV>:
user_no_text_input: no
user_name_text_input: name
BoxLayout:
orientation: "vertical"
Label:
...
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: root.data_items
SelectableRecycleGridLayout:
Python Code
In class RV, do the following:
Add NumericProperty to import statement for kivy.properties.
Add ObjecProperty for user_no_text_input = ObjectProperty(None) and user_name_text_input = ObjectProperty(None)
Add NumericProperty for total_col_headings = NumericProperty(0)
Create a on_press() method for the on_press event.
Create a get_table_column_headings() method to get the total column headings in the table. This is used to create the column range.
Enhance get_users() method, to create a list with db column value, db primary key value, and db column range. The trick of using database column range is for populating the TextInput widgets with values from the row clicked/pressed.
Snippet - Python
from kivy.properties import BooleanProperty, ListProperty, NumericProperty, ObjectProperty
...
class RV(BoxLayout):
''' Creates Db conn, table, and saves data, retrives stored data and
displays in the RecycleView .'''
data_items = ListProperty([])
user_no_text_input = ObjectProperty(None)
user_name_text_input = ObjectProperty(None)
total_col_headings = NumericProperty(0)
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.create_table()
self.get_table_column_headings()
self.get_users()
def on_press(self, instance):
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:
# With the with keyword, the Python interpreter automatically releases the resources.
# It also provides error handling
cursor = connection.cursor()
cursor.execute("PRAGMA table_info(Users)")
col_headings = cursor.fetchall()
self.total_col_headings = len(col_headings)
...
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
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]
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, NumericProperty, ObjectProperty
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
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 RV(BoxLayout):
''' Creates Db conn, table, and saves data, retrives stored data and
displays in the RecycleView .'''
data_items = ListProperty([])
user_no_text_input = ObjectProperty(None)
user_name_text_input = ObjectProperty(None)
total_col_headings = NumericProperty(0)
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.create_table()
self.get_table_column_headings()
self.get_users()
def on_press(self, instance):
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:
# With the with keyword, the Python interpreter automatically releases the resources.
# It also provides error handling
cursor = connection.cursor()
cursor.execute("PRAGMA table_info(Users)")
col_headings = cursor.fetchall()
self.total_col_headings = len(col_headings)
def create_table(self):
connection = sqlite3.connect("demo.db")
cursor = connection.cursor()
sql = """CREATE TABLE IF NOT EXISTS Users(
UserID integer PRIMAY 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
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
try:
save_sql = "INSERT INTO Users (UserID, UserName) VALUES (?,?)"
cursor.execute(save_sql, (UserID, UserName))
connection.commit()
connection.close()
except sqlite3.IntegrityError as e:
print("Error: ", e)
class TestApp(App):
title = "Kivy RecycleView & SQLite3 Demo"
def build(self):
return RV()
if __name__ == "__main__":
TestApp().run()
test.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.on_press(self)
<RV>:
user_no_text_input: no
user_name_text_input: name
BoxLayout:
orientation: "vertical"
Label:
text: "USER NUMBER"
size_hint: (.5, None)
height: 30
TextInput:
id: no
size_hint: (.5, None)
height: 30
multiline: False
Label:
text: "USER NAME"
size_hint: (.5, None)
height: 30
TextInput:
id: name
size_hint: (.5, None)
height: 30
multiline: False
Button:
id: save_btn
text: "SAVE BUTTON"
height: 50
width: 100
on_press: root.save()
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 2
Label:
text: "User ID"
Label:
text: "User Name"
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
Output
I have written some code in Python (test.py) and kivy (test.kv).When i run test.py.then show Test Menu and user submenu.When i click on user then +Add button show.When click on +add then show Number TextBox Type.
I am using treeView in number textbox.when i click on select number then it gives error
File "kivy/_event.pyx", line 273, in kivy._event.EventDispatcher.init (kivy/_event.c:5375)
File "kivy/properties.pyx", line 478, in kivy.properties.Property.set (kivy/properties.c:5171)
File "kivy/properties.pyx", line 513, in kivy.properties.Property.set (kivy/properties.c:5882)
ValueError: TreeViewLabelAccountGroup.text accept only str
if i use rows = [(1, 'Male'), (2, 'Female'), (3, 'Dog')] instead of rows = [(1, 111), (2, 112), (3, 113)]
then its working fine.
I do not know where I'm making a mistake.
If i change text to int then its working but data not showing.
if parent is None:
tree_node = tree_view_account_group.add_node(TreeViewLabelAccountGroup(text=node['node_id'],
is_open=True))
else:
tree_node = tree_view_account_group.add_node(TreeViewLabelAccountGroup(text=node['node_id'],
is_open=True), parent)
test.py
import kivy
kivy.require('1.9.0') # replace with your current kivy version !
import sqlite3 as lite
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty, NumericProperty
from kivy.lang import Builder
from kivy.uix.dropdown import DropDown
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from kivy.core.window import Window
from kivy.uix.label import Label
Window.maximize()
from kivy.clock import Clock
from kivy.uix.treeview import TreeView, TreeViewLabel, TreeViewNode
from kivy.uix.image import AsyncImage
import os
import sys
#root.attributes("-toolwindow", 1)
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
def populate_tree_view_account_group(tree_view_account_group, parent, node):
if parent is None:
tree_node = tree_view_account_group.add_node(TreeViewLabelAccountGroup(text=node['node_id'],
is_open=True))
else:
tree_node = tree_view_account_group.add_node(TreeViewLabelAccountGroup(text=node['node_id'],
is_open=True), parent)
for child_node in node['children']:
populate_tree_view_account_group(tree_view_account_group, tree_node, child_node)
#this is retrieve from database
rows = [(1, 111), (2, 112), (3, 113)]
treeAccountGroup = []
for r in rows:
treeAccountGroup.append({'node_id': r[1], 'children': []})
class TreeviewAccountGroup(Popup):
treeviewAccount = ObjectProperty(None)
tv = ObjectProperty(None)
h = NumericProperty(0)
#ti = ObjectProperty()
popup = ObjectProperty()
def __init__(self, **kwargs):
super(TreeviewAccountGroup, self).__init__(**kwargs)
self.tv = TreeView(root_options=dict(text=""),
hide_root=False,
indent_level=4)
for branch in treeAccountGroup:
populate_tree_view_account_group(self.tv, None, branch)
#self.remove_widgets()
self.treeviewAccount.add_widget(self.tv)
Clock.schedule_once(self.update, 1)
def remove_widgets(self):
for child in [child for child in self.treeviewAccount.children]:
self.treeviewAccount.remove_widget(child)
def update(self, *args):
self.h = len([child for child in self.tv.children]) * 24
def filter(self, f):
self.treeviewAccount.clear_widgets()
self.tv = TreeView(root_options=dict(text=""),
hide_root=False,
indent_level=4)
new_tree = []
for n in treeAccountGroup:
if f.lower() in n['node_id'].lower():
new_tree.append(n)
for branch in new_tree:
populate_tree_view_account_group(self.tv, None, branch)
self.treeviewAccount.add_widget(self.tv)
class TreeViewLabelAccountGroup(Label, TreeViewNode):
pass
class AccountGroupPopup(Popup):
category_label = ObjectProperty(None)
category_text = ObjectProperty(None)
name_label = ObjectProperty(None)
name_txt = ObjectProperty(None)
popupGroupAccount = ObjectProperty(None)
popupEffect = ObjectProperty(None)
groupType = ObjectProperty(None)
mode = StringProperty("")
col_data = ListProperty(["?", "?", "?", "?","?"])
index = NumericProperty(0)
popup4 = ObjectProperty(None)
popup5 = ObjectProperty(None)
primary_check_button = BooleanProperty(False)
secondary_check_button = BooleanProperty(False)
def __init__(self, obj, **kwargs):
super(AccountGroupPopup, self).__init__(**kwargs)
self.mode = obj.mode
if obj.mode == "Add":
self.col_data[0] = ''
self.col_data[1] = ''
self.col_data[2] = ''
self.col_data[3] = 'Select Number'
def display_primary_treeview(self, instance):
if len(instance.text) > 0:
if self.popupGroupAccount is None:
self.popupGroupAccount = TreeviewAccountGroup()
self.popupGroupAccount.popup4 = self
self.popupGroupAccount.filter(instance.text)
self.popupGroupAccount.open()
def display_effect_type(self, instance):
if len(instance.text) > 0:
if self.popupEffect is None:
self.popupEffect = TreeviewEffectType()
self.popupEffect.popup5 = self
self.popupEffect.filter(instance.text)
self.popupEffect.open()
class SelectableButtonGroupAccount(RecycleDataViewBehavior, Button):
''' Add selection support to the Button '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
rv_data_account_group = ObjectProperty(None)
start_point = NumericProperty(0)
mode = StringProperty("Update")
def __init__(self, **kwargs):
super(SelectableButtonGroupAccount, self).__init__(**kwargs)
Clock.schedule_interval(self.update, .0005)
def update(self, *args):
self.text = self.rv_data_account_group[self.index][self.key]
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButtonGroupAccount, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButtonGroupAccount, 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
self.rv_data_account_group = rv.data
#print("selection changed to {0}".format(rv.data[1]))
def on_press(self):
popup = AccountGroupPopup(self)
popup.open()
class RVACCOUNTGROUP(BoxLayout):
def add_account_group(self):
self.mode = "Add"
popup = AccountGroupPopup(self)
popup.open()
class CustDrop(DropDown):
def __init__(self, **kwargs):
super(CustDrop, self).__init__(**kwargs)
self.select('')
class MainMenu(BoxLayout):
content_area = ObjectProperty()
rv = ObjectProperty(None)
dropdown = ObjectProperty(None)
def display_account_group(self):
self.dropdown.dismiss()
self.remove_widgets()
self.rvarea = RVACCOUNTGROUP()
self.content_area.add_widget(self.rvarea)
def remove_widgets(self):
self.content_area.clear_widgets()
def insert_update(self, obj):
print('Test')
class TacApp(App):
title = "FactEx"
def build(self):
self.root = Builder.load_file('test.kv')
return MainMenu()
if __name__ == '__main__':
TacApp().run()
test.kv
#:kivy 1.10.0
#:import CoreImage kivy.core.image.Image
#:import os os
<TreeviewAccountGroup>:
id: treeviewAccount
treeviewAccount: treeviewAccount
title: ""
pos_hint: {'x': .65, 'y': .3}
size_hint: .2,.4
#size: 800, 800
auto_dismiss: False
BoxLayout
orientation: "vertical"
ScrollView:
size_hint: 1, .9
BoxLayout:
size_hint_y: None
id: treeviewAccount
height: root.h
rooot: root
TextInput:
id: treeview
size_hint_y: .1
on_text: root.filter(self.text)
#BoxLayout:
#id: treeview
#on_press: root.select_node(self.text)
Button:
size_hint: 1, 0.1
text: "Close"
on_release: root.dismiss()
<TreeviewEffectType>:
#id: treeviewAccount
treeviewEffectType: treeviewEffectType
title: ""
pos_hint: {'x': .65, 'y': .3}
size_hint: .2,.4
#size: 800, 800
auto_dismiss: False
BoxLayout
orientation: "vertical"
ScrollView:
size_hint: 1, .9
BoxLayout:
size_hint_y: None
id: treeviewEffectType
height: root.h
rooot: root
TextInput:
id: treeview
size_hint_y: .1
on_text: root.filter(self.text)
#BoxLayout:
#id: treeview
#on_press: root.select_node(self.text)
Button:
size_hint: 1, 0.1
text: "Close"
on_release: root.dismiss()
<TreeViewLabelEffectType>:
height: 24
on_touch_down:
root.parent.parent.rooot.popup5.col_data[4] = self.text
#app.root.effect_type_txt.text = self.text
#root.parent.parent.rooot.popup5.popup.dismiss()
<TreeViewLabelAccountGroup>:
height: 24
on_touch_down:
root.parent.parent.rooot.popup4.col_data[3] = self.text
#app.root.effect_type_txt.text = self.text
#app.root.popupEffect.dismiss()
<AccountGroupPopup>:
title: ""
size_hint: None, None
size: 500, 350
auto_dismiss: False
BoxLayout:
orientation: "vertical"
GridLayout:
cols: 2
padding : 30, 0
row_default_height: '30dp'
size_hint: 1, .1
pos_hint: {'x': .1, 'y': .06}
GridLayout:
cols: 2
padding: 10, 10
spacing: 20, 20
#row_default_height: '30dp'
size_hint: 1, .7
pos_hint: {'x': 0, 'y':.65}
Label:
id:category_label
text: 'Number'
text_size: self.size
valign: 'middle'
TextInput:
id: category_text
text: root.col_data[3]
on_focus: root.display_primary_treeview(self)
Button:
text: 'Ok'
on_release:
#root.package_changes(name_txt.text,primary_group_txt.text,effect_type_txt.text)
app.root.insert_update(root)
root.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
<AccountGroup#RecycleView>:
viewclass: 'SelectableButtonGroupAccount'
SelectableRecycleGridLayout:
cols: 1
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
<RVACCOUNTGROUP>:
BoxLayout:
orientation: "vertical"
Button:
size_hint: .05, .03
text: "+Add"
on_press: root.add_account_group()
<DropdownButton#Button>:
border: (0, 16, 0, 16)
text_size: self.size
valign: "middle"
padding_x: 5
size_hint_y: None
height: '30dp'
#on_release: dropdown.select('')
#on_release: app.root.test
background_color: 90 , 90, 90, 90
color: 0, 0.517, 0.705, 1
<MenuButton#Button>:
text_size: self.size
valign: "middle"
padding_x: 5
size : (80,30)
size_hint : (None, None)
background_color: 90 , 90, 90, 90
background_normal: ''
color: 0, 0.517, 0.705, 1
border: (0, 10, 0, 0)
<MainMenu>:
content_area: content_area
dropdown: dropdown
BoxLayout:
orientation: 'vertical'
#spacing : 10
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
size_hint_y: 1
MenuButton:
id: btn
text: 'Test'
size : (60,30)
on_release: dropdown.open(self)
CustDrop:
id: dropdown
auto_width: False
width: 150
DropdownButton:
text: 'User'
size_hint_y: None
height: '32dp'
# on_release: dropdown3.open(self)
on_release: root.display_account_group()
BoxLayout:
id: content_area
size_hint_y: 30
Label:
size_hint_y: 1
Can someone help me?
You need to convert the node_id to str:
for r in rows:
treeAccountGroup.append({'node_id': str(r[1]), 'children': []})
menu.py
from kivy.app import App
from kivy.uix.dropdown import DropDown
from kivy.lang import Builder
class CustDrop(DropDown):
def __init__(self, **kwargs):
super(CustDrop, self).__init__( **kwargs)
self.select('')
kv_str = Builder.load_string('''
BoxLayout:
orientation: 'vertical'
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: (1,1,1)
size_hint_y:1
Button:
id: btn
text: 'user'
on_release: dropdown.open(self)
#size_hint_y: None
#height: '48dp'
CustDrop:
id: dropdown
Button:
text: 'List User'
size_hint_y: None
height: '48dp'
Label:
size_hint_x: 4
Label:
size_hint_y: 9
''')
class ExampleApp(App):
def build(self):
return kv_str
if __name__ =='__main__':
ExampleApp().run()
user.py
import kivy
kivy.require('1.9.0') # replace with your current kivy version !
import sqlite3 as lite
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty,NumericProperty
from kivy.lang import Builder
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from kivy.core.window import Window
Window.size = (500, 500)
#con = lite.connect('user.db')
#con.text_factory = str
#cur = con.cursor()
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
def on_press(self,*args):
popup = TextInputPopup(self)
popup.open()
def update_changes(self):
self.text = txt
class RV(BoxLayout):
data_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_users()
def get_users(self):
# cur.execute("SELECT * FROM `users` order by id asc")
# rows = cur.fetchall()
rows = [(1, 'Yash', 'Chopra'),(2, 'amit', 'Kumar')]
for row in rows:
for col in row:
self.data_items.append(col)
class ListUser(App):
title = "Users"
def build(self):
self.root = Builder.load_file('user.kv')
return RV()
if __name__ == '__main__':
ListUser().run()
user.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
<RV>:
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 3
Label:
text: "ID"
Label:
text: "First Name"
Label:
text: "Last Name"
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in root.data_items]
SelectableRecycleGridLayout:
cols: 3
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
I have two file menu.py and user.py. menu.py file show menu and user.py shows list of user..
when i run menu.py then shows user menu on top .When i click on user then 'list user' show(submenu of user).When i click on 'list user' then user list should show in current window.Menu will be on top and user-list show in same window(under menu).
To dsiplay the user list in the same window, combine both Python scripts and kv files into one. Please refer to the example below for details.
Example
mainmenu.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
import sqlite3 as lite
dbPath = "/home/iam/dev/SQLite/sampleDB/ChinookDB/"
#con = lite.connect('user.db')
con = lite.connect(dbPath + "chinook.db")
#con.text_factory = str
cur = con.cursor()
class MessageBox(Popup):
obj = ObjectProperty(None)
obj_text = StringProperty("")
def __init__(self, obj, **kwargs):
super(MessageBox, self).__init__(**kwargs)
self.obj = obj
self.obj_text = obj.text
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
def on_press(self):
popup = MessageBox(self)
popup.open()
def update_changes(self, txt):
self.text = txt
class RV(BoxLayout):
data_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.get_users()
def get_users(self):
cur.execute("SELECT CustomerId, FirstName, LastName FROM 'customers' order by CustomerId asc")
rows = cur.fetchall()
for row in rows:
for col in row:
self.data_items.append(col)
class CustDrop(DropDown):
def __init__(self, **kwargs):
super(CustDrop, self).__init__(**kwargs)
self.select('')
class MainMenu(BoxLayout):
users = ObjectProperty(None)
dropdown = ObjectProperty(None)
def display_users(self):
# rv = RV()
self.dropdown.dismiss()
self.users.add_widget(RV())
class MainMenuApp(App):
title = "Example"
def build(self):
return MainMenu()
if __name__ == '__main__':
MainMenuApp().run()
mainmenu.kv
#:kivy 1.10.0
<MessageBox>:
title: "Popup"
size_hint: None, None
size: 400, 400
auto_dismiss: False
BoxLayout:
orientation: "vertical"
TextInput:
id: txtinput
text: root.obj_text
Button:
size_hint: 1, 0.2
text: "Save Changes"
on_release:
root.obj.update_changes(txtinput.text)
root.dismiss()
Button:
size_hint: 1, 0.2
text: "Cancel Changes"
on_release: root.dismiss()
<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
<RV>:
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 3
Label:
text: "ID"
Label:
text: "First Name"
Label:
text: "Last Name"
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in root.data_items]
SelectableRecycleGridLayout:
cols: 3
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
<MainMenu>:
users: users
dropdown: dropdown
orientation: 'vertical'
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: (1,1,1)
size_hint_y:1
Button:
id: btn
text: 'user'
on_release: dropdown.open(self)
CustDrop:
id: dropdown
Button:
text: 'List User'
size_hint_y: None
height: '48dp'
on_release: root.display_users()
Label:
size_hint_x: 4
text: "label 1"
BoxLayout:
id: users
size_hint_y: 9
Output