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': []})
Related
I haven't been able to figure out how to do update the data from a change in the text input on the UI. I've been trying to base my code on the following: https://github.com/kivy/kivy/issues/5318
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
import re
string_to_build_recycleview = '''
<rowTest#BoxLayout>:
#canvas:
# Rectangle:
# size: self.size
# pos: self.pos
name_text: 'Someone Else'
stroke_text: 'Butterfly'
classification_text: '3'
time_text: '9:99.99'
BoxLayout:
orientation: 'horizontal'
Label:
id: name
text: root.name_text
Label:
id: stroke
text: root.stroke_text
ClassificationTI:
id: classification
padding: (8, 1, 2, 1)
halign: 'center'
size: (20,20)
multiline: 'False'
input_filter: 'int'
text: root.classification_text
on_text: root.classification_text = self.text
TimeTI:
padding: (8, 1, 2, 1)
halign: 'center'
size: (20,20)
multiline: 'False'
text: root.time_text
#on_text: root.time_text = self.text
<RecycleViewTEST#RecycleView>:
id: myListToTest
scroll_type: ['bars', 'content']
#scroll_wheel_distance: dp(114)
bar_width: dp(10)
viewclass: 'rowTest'
RecycleBoxLayout:
default_size: None, dp(20)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(2)
'''
class ClassificationTI(TextInput):
def insert_text(self,substr,from_undo=False):
if substr.isnumeric():
return super(ClassificationTI, self).insert_text(substr,from_undo=from_undo)
return super(ClassificationTI, self)
class TimeTI(TextInput):
def insert_text(self,substr,from_undo=False):
if substr.isnumeric() or substr == ":" or substr == ".":
return super(TimeTI, self).insert_text(substr,from_undo=from_undo)
return super(TimeTI, self)
class RecycleViewTEST(RecycleView):
def __init__(self, **kwargs):
super(RecycleViewTEST, self).__init__(**kwargs)
Builder.load_string(string_to_build_recycleview)
self.initial_populate()
def on_enter(self, *args):
self.get_real_data()
def initial_populate(self):
data = (('TheFirstOne', 'free', '9', '1:01.60'),('TheSecondOne', 'free', '9', '1:01.60'))
self.data = [{'name_text':n,'stroke_text':s,'classification_text':c,'time_text':t} for n,s,c,t in data]
print(self.data)
def validate_classification(self,classification):
if classification.isnumeric() and int(classification) > 14 and int(classification) < 1:
return "Not Valid"
return classification
def validate_time(self,time):
if re.match(r"([0-9]?[0-9]:)?([0-5][0-9])\.([0-9][0-9])", time) is None:
return "Not Valid"
return time
def do_stuff(self):
for i in range(len(self.data)):
print(self.data[i])
self.data[i]['classification_text'] = self.validate_classification(self.data[i]['classification_text'])
self.data[i]['time_text'] = self.validate_time(self.data[i]['time_text'])
print(self.data[i])
self.refresh_from_data()
string_to_build_the_form = '''
<TheForm>:
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
Label:
size_hint: (None, None)
size: (200, 27)
text: '[u]Name[/u]'
markup: True
Label:
size_hint: (None, None)
size: (200, 27)
text: '[u]Stroke[/u]'
markup: True
Label:
size_hint: (None, None)
size: (190, 27)
text: '[u]Class[/u]'
markup: True
Label:
size_hint: (None, None)
size: (200, 27)
text: '[u]Time (mm:ss.hh)[/u]'
markup: True
BoxLayout:
size_hint: (1,None)
RecycleViewTEST:
id: myListToTest
BoxLayout:
orientation: 'horizontal'
Button:
size_hint: (None,None)
size: (150,50)
text: "Validate"
on_release: root.validate()
Button:
id: save_recalc
size_hint: (None,None)
size: (150,50)
text: "Save & Recalculate"
disabled: True
'''
class TheForm(BoxLayout):
def __init__(self, **kwargs):
super(TheForm, self).__init__(**kwargs)
Builder.load_string(string_to_build_the_form)
def validate(self, *args):
self.ids.myListToTest.do_stuff()
self.ids.save_recalc.disabled = False
class TheTEST(App):
def build(self):
sm = TheForm()
return sm
if __name__ == '__main__':
TheTEST().run()
This is after I change the values text input boxes. The before refers to validating the classification and time then changing the data. After executing validate(), the textinput text returns to the original numbers:
BEFORE: {'name_text': 'TheFirstOne', 'stroke_text': 'free', 'classification_text': '9', 'time_text': '1:01.60'}
AFTER: {'name_text': 'TheFirstOne', 'stroke_text': 'free', 'classification_text': '9', 'time_text': '1:01.60'}
BEFORE: {'name_text': 'TheSecondOne', 'stroke_text': 'free', 'classification_text': '9', 'time_text': '1:01.60'}
AFTER: {'name_text': 'TheSecondOne', 'stroke_text': 'free', 'classification_text': '9', 'time_text': '1:01.60'}
What am I missing here? Thanks for the help!
I got an answer from the Kivy forums on discord. The key is to pass a index to the row widget so they can change the data for their row. I think this should work for any complex widget in a recycleview object. Hope this helps someone.
from kivy.app import App
from kivy.properties import ObjectProperty, NumericProperty, ListProperty
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.textinput import TextInput
from kivy.lang.builder import Builder
KV = """
<RecycleItem>:
on_text: if root.owner != None: self.owner.data[self.index]['text'] = self.text
RecycleView:
data: app.data
viewclass: 'RecycleItem'
RecycleBoxLayout:
spacing: 10
default_size: None, dp(80)
default_size_hint: 1, None
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
"""
class RecycleItem(RecycleDataViewBehavior,TextInput):
owner = ObjectProperty()
index = NumericProperty(0)
def refresh_view_attrs(self, rv, index, data):
self.index = index
print("INDEXXXXXX: ",self.index)
return super(RecycleItem, self).refresh_view_attrs(rv, index, data)
class Test(App):
data = ListProperty()
def build(self):
self.data = [{"text": "Label "+str(x), 'owner': self} for x in range(20)]
return Builder.load_string(KV)
Test().run()
I have two file demo.py and demo.kv.
when i run demo.py after click on menu then shows +Add more button.When i click on +Add more button then three row add dynamic because i am using loop there.Every time add three row dynamic
But can anyone tell me when i add new row then how to remove previous three row?
Every time should be show only 3 new row and previous row should be delete.I am using code
def add_more(self):
self.remove_widget(Row())
for x in range(0, 3):
self.row_count += 1
self.add_widget(Row(button_text=str(self.row_count)))
demo.py
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty, NumericProperty
from kivy.uix.popup import Popup
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (500, 400)
class User(Popup):
total_value = ObjectProperty(None)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
def add_more(self):
self.ids.rows.add_more()
class Row(BoxLayout):
col_data = ListProperty(["?", "?", "?", "?", "?"])
name = ObjectProperty(None)
button_text = StringProperty("")
col_data3 = StringProperty("")
col_data4 = StringProperty("")
def __init__(self, **kwargs):
super(Row, self).__init__(**kwargs)
class Rows(BoxLayout):
row_count = 0
def __init__(self, **kwargs):
super(Rows, self).__init__(**kwargs)
self.add_more()
def add_more(self):
self.remove_widget(Row())
for x in range(0, 3):
self.row_count += 1
self.add_widget(Row(button_text=str(self.row_count)))
class rv(BoxLayout):
data_items = ListProperty([])
mode = StringProperty("")
def __init__(self, **kwargs):
super(rv, self).__init__(**kwargs)
def add(self):
self.mode = "Add"
popup = User()
popup.open()
class MainMenu(BoxLayout):
content_area = ObjectProperty()
def display(self):
self.rv = rv()
self.content_area.add_widget(self.rv)
class demo(App):
def build(self):
return MainMenu()
if __name__ == '__main__':
demo().run()
demo.kv
<Row>:
size_hint_y: None
height: self.minimum_height
height: 40
Button:
text: root.button_text
size_hint_x: None
top: 200
TextInput:
id : name
text: root.col_data3
width: 300
TextInput:
id: number_input
text: root.col_data4
width: 300
input_filter: 'int'
<Rows>:
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
<User>:
id: user
BoxLayout:
orientation: "vertical"
padding : 20, 5
BoxLayout:
orientation: "horizontal"
#padding : 10, 10
spacing: 10, 10
size: 450, 40
size_hint: None, None
Label:
size_hint_x: .2
text: "Number"
text_size: self.size
valign: 'bottom'
halign: 'center'
Label:
size_hint_x: .4
text: "name"
text_size: self.size
valign: 'bottom'
halign: 'center'
Label:
size_hint_x: .4
text: "Value"
text_size: self.size
valign: 'bottom'
halign: 'center'
ScrollView:
Rows:
id: rows
BoxLayout:
orientation: "horizontal"
size_hint_x: .2
size_hint_y: .2
Button:
text: "+Add More"
on_press: root.add_more()
<rv>:
BoxLayout:
orientation: "vertical"
Button:
size_hint: .25, .03
text: "+Add"
on_press: root.add()
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 3
BoxLayout:
orientation: "vertical"
<MenuButton#Button>:
text_size: self.size
valign: "middle"
padding_x: 5
size : (100, 40)
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
BoxLayout:
orientation: 'vertical'
spacing : 10
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
size_hint_y: 2
MenuButton:
text: 'Menu'
size : (50, 12)
on_release: root.display()
BoxLayout:
id: content_area
size_hint_y: 30
remove_widget must receive as argument the instance of the child widget to be removed. Since you can get widget's children using its children attribute, to remove the previous three rows you can do the following:
def add_more(self):
if self.children:
for child in self.children[:3]:
self.remove_widget(child)
for x in range(0, 3):
self.row_count += 1
self.add_widget(Row(button_text=str(self.row_count)))
However, it's simpler to use clear_widgets method:
def add_more(self):
self.clear_widgets(self.children[:3])
for x in range(0, 3):
self.row_count += 1
self.add_widget(Row(button_text=str(self.row_count)))
Since you actually delete all the rows in the BoxLayout, you can do:
def add_more(self):
self.clear_widgets()
for x in range(0, 3):
self.row_count += 1
self.add_widget(Row(button_text=str(self.row_count)))
Edit:
To reset the index simply disregard self.row_count and use the value returned by range:
def add_more(self):
self.clear_widgets()
for x in range(1, 4):
self.add_widget(Row(button_text=str(x)))
I have two files test1.py and test1.kv. There are a +Add more button.When click on +Add More button then row add dynamic.
I added treeView on TextInput.But now i am confused that how to put selected treeView value(self.text) in current TextBox because every TextBox added dynamic so i don't know how to set selected treeView value in current TextBox
test1.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.popup import Popup
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.treeview import TreeView, TreeViewLabel, TreeViewNode
from kivy.uix.screenmanager import Screen
Window.size = (450, 525)
class TreeviewNumber(Popup):
treeview = ObjectProperty(None)
tv = ObjectProperty(None)
h = NumericProperty(0)
def __init__(self, **kwargs):
super(TreeviewNumber, self).__init__(**kwargs)
self.tv = TreeView(root_options=dict(text=""),
hide_root=False,
indent_level=4)
rows = [('One'), ('Two'), ('Three')]
tree = []
for r in rows:
tree.append({'node_id': r, 'children': []})
for branch in tree:
populate_tree_view(self.tv, None, branch)
#self.remove_widgets()
self.treeview.add_widget(self.tv)
Clock.schedule_once(self.update, 1)
def remove_widgets(self):
for child in [child for child in self.treeview.children]:
self.treeview.remove_widget(child)
def update(self, *args):
self.h = len([child for child in self.tv.children]) * 24
def populate_tree_view(tree_view_city, parent, node):
if parent is None:
tree_node = tree_view_city.add_node(TreeViewLabel(text=node['node_id'],
is_open=True))
else:
tree_node = tree_view_city.add_node(TreeViewLabel(text=node['node_id'],
is_open=True), parent)
for child_node in node['children']:
populate_tree_view(tree_view_city, tree_node, child_node)
class TreeViewLabel(Label, TreeViewNode):
pass
class display(Screen):
def add_more(self):
self.ids.rows.add_row()
def insert_value(self):
#print(Row().id)
w = Row()
print(w.ids.state.text)
class Row(BoxLayout):
button_text = StringProperty("")
state = ObjectProperty('')
popupNumber = ObjectProperty(None)
popup_number = ObjectProperty(None)
def display_number(self,instance):
if self.popupNumber is None:
# if self.popupAccountCity is None:
self.popupNumber = TreeviewNumber()
self.popupNumber.popup_number = self
self.popupNumber.open()
class Rows(BoxLayout):
orientation = "vertical"
row_count = 0
button_text = StringProperty("")
def __init__(self, **kwargs):
super(Rows, self).__init__(**kwargs)
self.add_row()
def add_row(self):
self.row_count += 1
self.add_widget(Row(button_text=str(self.row_count),id=str("test"+str(self.row_count))))
class test(App):
def build(self):
self.root = Builder.load_file('test1.kv')
return self.root
if __name__ == '__main__':
test().run()
test1.kv
<Row>:
state : state
orientation: "horizontal"
spacing: 0, 5
Button:
text: root.button_text
size_hint_x: .2
TextInput:
#text : root.id
size_hint_x: .8
id : state
on_focus: root.display_number(self)
display:
BoxLayout:
orientation: "vertical"
padding : 20, 20
BoxLayout:
orientation: "horizontal"
Button:
size_hint_x: .2
text: "+Add More"
valign: 'bottom'
on_press: root.add_more()
BoxLayout:
orientation: "horizontal"
Label:
size_hint_x: .2
text: "SN"
valign: 'bottom'
Label:
size_hint_x: .8
text: "Value"
valign: 'bottom'
Rows:
id: rows
BoxLayout:
orientation: "horizontal"
padding : 10, 0
spacing: 10, 10
size_hint: .5, .7
pos_hint: {'x': .25, 'y':.25}
Button:
text: 'Ok'
on_release:
root.insert_value()
Button:
text: 'Cancel'
on_release: root.dismiss()
<TreeviewNumber>:
treeview: treeview
title: "Select Number"
title_size: 17
title_font: "Verdana"
#pos_hint: {'x': .62, 'y': .55}
size_hint: .5, .5
auto_dismiss: False
BoxLayout
orientation: "vertical"
ScrollView:
size_hint: 1, .9
BoxLayout:
size_hint_y: None
id: treeview
#height: root.h
#rooot: root
GridLayout:
cols : 2
row_default_height: '20dp'
size_hint: .5, 0.2
pos_hint: {'x': .25, 'y': 1}
Button:
text: 'Ok'
on_release: root.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
<TreeViewLabel>:
text_size: self.size
height: 24
on_touch_down:
print(self.text)
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
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