Python/Kivy : not working properly vertical scrollbar in dynamic row - python

I have two files demo.py and demo.kv
I have a button +Add More which add row dynamic.I am trying to add vertical scrollbar in dynamic row using ScrollView:.But its not working properly.
its mean when i add row in scrollview that row having extra space in scrollview i want add rows without any spacing.
ScrollView:
BoxLayout:
orientation: "horizontal"
size_hint_y: None
height: 500
Rows:
id: rows
demo.py
import kivy
from kivy.uix.screenmanager import Screen
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 StringProperty
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (500, 400)
class user(Screen):
def add_more(self):
self.ids.rows.add_row()
class Row(BoxLayout):
button_text = StringProperty("")
class Rows(BoxLayout):
orientation = "vertical"
row_count = 0
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)))
class Test(App):
def build(self):
self.root = Builder.load_file('demo.kv')
return self.root
if __name__ == '__main__':
Test().run()
demo.kv
<Button#Button>:
font_size: 15
font_name: 'Verdana'
<Label#Label>:
font_size: 15
font_name: 'Verdana'
<TextInput#TextInput>:
font_size: 15
font_name: 'Verdana'
padding_y: 3
<Row>:
GridLayout:
cols: 2
row_force_default: True
row_default_height: 40
Button:
text: root.button_text
size_hint_x: None
top: 200
Button:
text: 'World 1'
width: 300
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: "Test 1"
text_size: self.size
valign: 'bottom'
halign: 'center'
Label:
size_hint_x: .8
text: "Test 2"
text_size: self.size
valign: 'bottom'
halign: 'center'
ScrollView:
BoxLayout:
orientation: "horizontal"
size_hint_y: None
height: 500
Rows:
id: rows
BoxLayout:
orientation: "horizontal"
size_hint_x: .2
size_hint_y: .2
Button:
text: "+Add More"
on_press: root.add_more()
BoxLayout:
orientation: "horizontal"
padding : 10, 5
spacing: 10, 10
size_hint: .5, .35
pos_hint: {'x': .25, 'y':.25}
Button:
text: 'Ok'
Button:
text: 'Cancel'
any help would be greatly appreciated.

If I have understood what you want, you have too many nested Layouts, that are unnecessary. Rows should be the main layout of your ScrollView.
On the other hand, Rows should always have the lowest possible height to contain their widgets (minimun_height property), not a fixed size.
Demo.py:
from kivy.uix.screenmanager import Screen
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 StringProperty
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (500, 400)
class User(Screen):
def add_more(self):
self.ids.rows.add_row()
class Row(BoxLayout):
button_text = StringProperty("")
class Rows(BoxLayout):
row_count = 0
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)))
class Test(App):
def build(self):
self.root = Builder.load_file('Demo.kv')
return self.root
if __name__ == '__main__':
Test().run()
Demo.kv:
<Button#Button>:
font_size: 15
font_name: 'Verdana'
<Label#Label>:
font_size: 15
font_name: 'Verdana'
<TextInput#TextInput>:
font_size: 15
font_name: 'Verdana'
padding_y: 3
<Row>:
size_hint_y: None
height: self.minimum_height
height: 40
Button:
text: root.button_text
size_hint_x: None
top: 200
Button:
text: 'World 1'
width: 300
<Rows>:
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
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: "Test 1"
text_size: self.size
valign: 'bottom'
halign: 'center'
Label:
size_hint_x: .8
text: "Test 2"
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()
BoxLayout:
orientation: "horizontal"
padding : 10, 5
spacing: 10, 10
size_hint: .5, .35
pos_hint: {'x': .25, 'y':.25}
Button:
text: 'Ok'
Button:
text: 'Cancel'

Related

How do I add a rectangle graphic behind all my buttons and labels?

I would like to add a rounded rectangle behind each row of widgets containing 3 buttons and two labels all horizontally layout. Each group contents are added dynamically through the input_text and '+' button on the bottom. I have all the major parts working but I can't get the rounded rectangle graphic.
Here is what I've got so far:
I was hoping to create something like this:
Please let me know where I went wrong and how to fix it I'm pretty new in Kivy so I'm just learning.
Thanks.
class Trackers(Screen):
storage = {}
path = ''
def on_pre_enter(self):
self.path = App.get_running_app().user_data_dir + '/'
self.loadData()
for tracker, num in self.storage.items():
self.ids.track.add_widget(Tracker(text=tracker, number=num, data=self.storage))
def addWidget(self):
text_input = self.ids.text_input.text
num = '0'
if text_input not in self.storage.keys():
self.ids.track.add_widget(Tracker(text=text_input, number=num, data=self.storage))
self.storage[text_input] = '0'
self.ids.text_input.text = ''
self.saveData()
class Tracker(BoxLayout):
def __init__(self, text='', number='', data={}, **kwargs):
super().__init__(**kwargs)
self.ids.label.text = text
self.ids.count_add.text = number
class Pess(App):
def build(self):
Config.set('graphics', 'width', '600')
Config.set('graphics', 'height', '800')
from kivy.core.window import Window
Window.clearcolor = get_color_from_hex('#262829')
return ScreenGenerator()
##### --> .kv file
<Trackers>:
BoxLayout:
orientation: 'vertical'
ActionBar:
height: 45
size_hint_y: None
background_image: ''
background_color: rgba('#0B3242')
ActionView:
ActionPrevious:
title: '[b]TRACKERS[/b]'
font_size: 21
color: rgba('#AFB7BA')
markup: True
on_release: app.root.current = 'menu'
ActionButton:
text: 'SEND'
color: rgba('#AFB7BA')
on_release: root.send()
ScrollView:
BoxLayout:
id: track
orientation: 'vertical'
font_size: 15
size_hint_y: None
height: self.minimum_height
BoxLayout:
size_hint_y: None
height: 45
TextInput:
id: text_input
hint_text: 'Add Trackers'
multiline: False
Button:
text: '+'
size_hint_x: None
width: 60
on_release: root.addWidget()
background_color: rgba('#1D7332')
<Tracker>:
count_add: count_add
size_hint_y: None
height: 45
padding: 4
Button:
text: '[b]X[/b]'
markup: True
size_hint_x: None
width: 60
on_release: app.root.get_screen('track').delete_storage(root)
Label:
id: label
font_size: 20
Label:
id: count_add
font_size: 20
text: '0'
Button:
text: '[b]-[/b]'
markup: True
size_hint_x: None
width: 60
on_release: app.root.get_screen('track').subtract_num(root)
Button:
text: '[b]+[/b]'
markup: True
size_hint_x: None
width: 60
on_release: app.root.get_screen('track').add_num(root)
In your 'kv' file, you can add graphics to the Canvas of your Tracker like this:
<Tracker>:
count_add: count_add
size_hint_y: None
height: 45
padding: 20, 4, 20, 4 # to keep widgets a bit away from the sides
canvas.before: # use before to keep this under any widgets
Color:
rgba: 1, 0, 0, 1 # any color you want
Rectangle:
pos: self.pos[0] + self.height/2, self.pos[1]
size: self.size[0] - self.height, self.height
Ellipse:
pos: self.pos[0], self.pos[1]
size: self.height, self.height
Ellipse:
pos: self.pos[0] + self.width - self.height, self.pos[1]
size: self.height, self.height
You might want to add some spacing to your track id BoxLayout so that the Tracker widgets don't appear connected.
Here is the entire version of your code that I ran. There are a few lines commented out, since you did not provide all the code:
from kivy.config import Config
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import get_color_from_hex
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager
class Trackers(Screen):
storage = {}
path = ''
def on_pre_enter(self):
self.path = App.get_running_app().user_data_dir + '/'
#self.loadData()
for tracker, num in self.storage.items():
self.ids.track.add_widget(Tracker(text=tracker, number=num, data=self.storage))
def addWidget(self):
text_input = self.ids.text_input.text
num = '0'
if text_input not in self.storage.keys():
self.ids.track.add_widget(Tracker(text=text_input, number=num, data=self.storage))
self.storage[text_input] = '0'
self.ids.text_input.text = ''
#self.saveData()
class Tracker(BoxLayout):
def __init__(self, text='', number='', data={}, **kwargs):
super().__init__(**kwargs)
self.ids.label.text = text
self.ids.count_add.text = number
Builder.load_string('''
<Trackers>:
BoxLayout:
orientation: 'vertical'
ActionBar:
height: 45
size_hint_y: None
background_image: ''
background_color: rgba('#0B3242')
ActionView:
ActionPrevious:
title: '[b]TRACKERS[/b]'
font_size: 21
color: rgba('#AFB7BA')
markup: True
on_release: app.root.current = 'menu'
ActionButton:
text: 'SEND'
color: rgba('#AFB7BA')
on_release: root.send()
ScrollView:
BoxLayout:
id: track
orientation: 'vertical'
spacing: 5
font_size: 15
size_hint_y: None
height: self.minimum_height
BoxLayout:
size_hint_y: None
height: 45
TextInput:
id: text_input
hint_text: 'Add Trackers'
multiline: False
Button:
text: '+'
size_hint_x: None
width: 60
on_release: root.addWidget()
background_color: rgba('#1D7332')
<Tracker>:
count_add: count_add
size_hint_y: None
height: 45
padding: 20, 4, 20, 4
canvas.before:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos[0] + self.height/2, self.pos[1]
size: self.size[0] - self.height, self.height
Ellipse:
pos: self.pos[0], self.pos[1]
size: self.height, self.height
Ellipse:
pos: self.pos[0] + self.width - self.height, self.pos[1]
size: self.height, self.height
Button:
text: '[b]X[/b]'
markup: True
size_hint_x: None
width: 60
on_release: app.root.get_screen('track').delete_storage(root)
Label:
id: label
font_size: 20
Label:
id: count_add
font_size: 20
text: '0'
Button:
text: '[b]-[/b]'
markup: True
size_hint_x: None
width: 60
on_release: app.root.get_screen('track').subtract_num(root)
Button:
text: '[b]+[/b]'
markup: True
size_hint_x: None
width: 60
on_release: app.root.get_screen('track').add_num(root)
''')
class Pess(App):
def build(self):
Config.set('graphics', 'width', '600')
Config.set('graphics', 'height', '800')
from kivy.core.window import Window
Window.clearcolor = get_color_from_hex('#262829')
# Don't have `ScreenGenerator` so just set up `ScreenManager`
sm = ScreenManager()
sm.add_widget(Trackers(name='trackers'))
return sm
#return ScreenGenerator()
Pess().run()

Using jsonstore in Kivy

I created a GUI based off this question while trying to teach myself how to use jsonstore. I don't have the reputation points to add a comment so I'm asking my question here. I think I have the basic idea down but for some reason I can't save the data to a json file. I get the following error:
AttributeError: 'NoneType' object has no attribute 'text'
I've tried following the documentation but I can't see anywhere where it would explain what I'm doing wrong.
main.py
from kivy.storage.jsonstore import JsonStore
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty
Window.clearcolor = (0.02745098, 0.074509804, 0.121568627, 1)
Window.size = (2000, 900)
class TitleScreen(Screen):
pass
class MainScreen(Screen):
pass
class CreateProfile(Screen):
First = ObjectProperty()
Middle = ObjectProperty()
Last = ObjectProperty()
def __init__(self, **kwargs):
super(CreateProfile, self).__init__(**kwargs)
self.store = JsonStore("bco.json")
self.load()
def save(self):
self.store.put('profile', first = self.label.text)
self.store.put('profile', middle = self.label.text)
self.store.put('profile', last = self.label.text)
def load(self):
try:
self.Last.text = self.store.get('profile')['score']
except KeyError:
pass
class CreatePacket(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("customwidget.kv")
class CustomWidgetApp(App):
def build(self):
return presentation
if __name__ == "__main__":
CustomWidgetApp().run()
customwidget.kv
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import hex kivy.utils.get_color_from_hex
#: import FocusBehaviors kivy.uix.behaviors.focus
ScreenManagement:
transition: FadeTransition()
TitleScreen:
MainScreen:
CreateProfile:
CreatePacket:
<MainLabel#Label>:
font_size:50
bold: True
size_hint_x: 1
size_hint_y: 1.85
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
<SubLabel#Label>:
font_size: 35
bold: True
halign: "center"
size_hint_x: 1
size_hint_y: 1.5
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
<OtherLabel#Label>:
font_size: 12
bold: True
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
text_size: self.size
<Button#Button>:
font_size: 20
bold: True
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
background_color: 0.02745098, 0.074509804, 0.121568627, .01
canvas.before:
Color:
rgba: 0.396078431, 0.803921569, 0.807843137, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
on_press: self.background_color = (0.396078431, 0.803921569, 0.807843137, 1)
on_release: self.background_color = (0.02745098, 0.074509804, 0.121568627, .01)
# TODO: Create a focus behavior to "Tab" between widgets
<TextInput#TextInput>:
font_size: 12
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
padding_x: 10
padding_y: 10
focus_next: None
focus_previous: None
unfocus_on_touch: True
background_color: 0.02745098, 0.074509804, 0.121568627, .01
canvas.before:
Color:
rgba: 0.396078431, 0.803921569, 0.807843137, 1
Line:
width: 1
rectangle: self.x, self.y, self.width, self.height
<TitleScreen>:
id: "title"
FloatLayout:
MainLabel:
text: "Easy Button"
size_hint_x: 1
size_hint_y: 1.25
SubLabel:
text: 'Test'
size_hint_x: 1
size_hint_y: 1
Button:
text: "Click Here To Continue"
on_release: app.root.current = "main"
size_hint: (.75, .15)
pos_hint: {'x': .12, 'y': .2}
<MainScreen>:
name: "main"
MainLabel:
text: "Home Page"
BoxLayout:
Button:
on_release: app.root.current = "create_profile"
text: "Create Profile"
size_hint: (.5, .15)
Button:
on_release: app.root.current = "create_packet"
text: "Create Packet"
size_hint: (.5, .15)
<CreateProfile>:
name: "create_profile"
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
MainLabel:
text: "Create Profile"
size_hint: (1, .15)
BoxLayout:
size_hint: (.95, .2)
pos_hint: {'x': 0, 'y': .85}
spacing: 10
padding: 10
halign: "left"
OtherLabel:
text: "First"
OtherLabel:
text: "Middle"
OtherLabel:
text: "Last"
BoxLayout:
size_hint: (.95, .07)
pos_hint: {'x': 0, 'y': .8}
spacing: 20
padding: 10
halign: "right"
text_size: self.size
TextInput:
id: 'First'
TextInput:
id: "Middle"
TextInput:
id: 'Last'
BoxLayout:
Button:
on_release: app.root.current = "main"
text: "back Home"
size_hint: (.5, .15)
Button:
on_release: root.save()
text: "Save Profile"
size_hint: (.5, .15)
<CreatePacket>:
name: "create_packet"
MainLabel:
text: "Select Packet"
FloatLayout:
Button:
on_release: app.root.current = "main"
text: "back Home"
size_hint: (1, .15)
Your code has several problems but the main one is that you do not understand how to expose any .kv widget to .py, one of the simplest ways is to use an ObjectProperty as you try to do but that property is not linked to the widget, I prefer to do the creation in the .kv for its simplicity.
On the other hand I recommend you avoid the abuse of try-except since it hides errors, the best thing is to verify.
Another error is that you are overwriting the value of profile in the .json, the idea is to save everything in one.
Considering the above, the solution is:
*.py
from kivy.app import App
from kivy.storage.jsonstore import JsonStore
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.core.window import Window
from kivy.clock import Clock
Window.clearcolor = (0.02745098, 0.074509804, 0.121568627, 1)
Window.size = (2000, 900)
class TitleScreen(Screen):
pass
class MainScreen(Screen):
pass
class CreateProfile(Screen):
def __init__(self, **kwargs):
super(CreateProfile, self).__init__(**kwargs)
self.store = JsonStore("bco.json")
Clock.schedule_once(lambda *args: self.load())
def save(self):
self.store.put('profile',
first = self.first.text,
middle = self.middle.text,
last = self.last.text)
def load(self):
if self.store.exists('profile'):
profile = self.store.get('profile')
v = [("first", self.first), ("middle", self.middle), ("last", self.last)]
for key, ti in v:
val = profile.get(key)
if val:
ti.text = val
class CreatePacket(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("customwidget.kv")
class CustomWidgetApp(App):
def build(self):
return presentation
if __name__ == "__main__":
CustomWidgetApp().run()
*.kv
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import hex kivy.utils.get_color_from_hex
#: import FocusBehaviors kivy.uix.behaviors.focus
ScreenManagement:
transition: FadeTransition()
TitleScreen:
MainScreen:
CreateProfile:
CreatePacket:
<MainLabel#Label>:
font_size:50
bold: True
size_hint_x: 1
size_hint_y: 1.85
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
<SubLabel#Label>:
font_size: 35
bold: True
halign: "center"
size_hint_x: 1
size_hint_y: 1.5
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
<OtherLabel#Label>:
font_size: 12
bold: True
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
text_size: self.size
<Button#Button>:
font_size: 20
bold: True
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
background_color: 0.02745098, 0.074509804, 0.121568627, .01
canvas.before:
Color:
rgba: 0.396078431, 0.803921569, 0.807843137, 1
Line:
width: 2
rectangle: self.x, self.y, self.width, self.height
on_press: self.background_color = (0.396078431, 0.803921569, 0.807843137, 1)
on_release: self.background_color = (0.02745098, 0.074509804, 0.121568627, .01)
# TODO: Create a focus behavior to "Tab" between widgets
<TextInput#TextInput>:
font_size: 12
color: 0.396078431, 0.803921569, 0.807843137, 1
font_name: '/home/jarren/PycharmProjects/BCO_Form_Filler/practice/pirulen rg.ttf'
padding_x: 10
padding_y: 10
focus_next: None
focus_previous: None
unfocus_on_touch: True
background_color: 0.02745098, 0.074509804, 0.121568627, .01
canvas.before:
Color:
rgba: 0.396078431, 0.803921569, 0.807843137, 1
Line:
width: 1
rectangle: self.x, self.y, self.width, self.height
<TitleScreen>:
id: "title"
FloatLayout:
MainLabel:
text: "Easy Button"
size_hint_x: 1
size_hint_y: 1.25
SubLabel:
text: 'Test'
size_hint_x: 1
size_hint_y: 1
Button:
text: "Click Here To Continue"
on_release: app.root.current = "main"
size_hint: (.75, .15)
pos_hint: {'x': .12, 'y': .2}
<MainScreen>:
name: "main"
MainLabel:
text: "Home Page"
BoxLayout:
Button:
on_release: app.root.current = "create_profile"
text: "Create Profile"
size_hint: (.5, .15)
Button:
on_release: app.root.current = "create_packet"
text: "Create Packet"
size_hint: (.5, .15)
<CreateProfile>:
name: "create_profile"
first: first
middle: middle
last: last
AnchorLayout:
anchor_x: 'center'
anchor_y: 'top'
MainLabel:
text: "Create Profile"
size_hint: (1, .15)
BoxLayout:
size_hint: (.95, .2)
pos_hint: {'x': 0, 'y': .85}
spacing: 10
padding: 10
halign: "left"
OtherLabel:
text: "First"
OtherLabel:
text: "Middle"
OtherLabel:
text: "Last"
BoxLayout:
size_hint: (.95, .07)
pos_hint: {'x': 0, 'y': .8}
spacing: 20
padding: 10
halign: "right"
text_size: self.size
TextInput:
id: first
TextInput:
id: middle
TextInput:
id: last
BoxLayout:
Button:
on_release: app.root.current = "main"
text: "back Home"
size_hint: (.5, .15)
Button:
on_release: root.save()
text: "Save Profile"
size_hint: (.5, .15)
<CreatePacket>:
name: "create_packet"
MainLabel:
text: "Select Packet"
FloatLayout:
Button:
on_release: app.root.current = "main"
text: "back Home"
size_hint: (1, .15)
I found a work around that still allows me to append information into a JSON by using TinyDB. Here is the updated code:
from kivy.app import App
from kivy.storage.jsonstore import JsonStore
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.core.window import Window
from tinydb import TinyDB, Query
from kivy.uix.listview import ListItemButton
Window.clearcolor = (0.02745098, 0.074509804, 0.121568627, 1)
Window.size = (2000, 900)
db = TinyDB('bcodb.json')
class ProfileListButton(ListItemButton):
pass
class TitleScreen(Screen):
pass
class MainScreen(Screen):
pass
class CreateProfile(Screen):
def __init__(self, **kwargs):
super(CreateProfile, self).__init__(**kwargs)
self.store = JsonStore("bcodb.json")
def save(self):
db.insert({'first': self.first.text, 'middle': self.middle.text, 'last': self.last.text})
def update(self):
db.update({'first': self.first.text, 'middle': self.middle.text, 'last': self.last.text})
class CreatePacket(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("customwidget2.kv")
class CustomWidgetApp(App):
def build(self):
return presentation
if __name__ == "__main__":
CustomWidgetApp().run()
It's pretty easy to figure out with a simple database-like program I'm trying to develop. The .kv file is the same.

Python/Kivy : How to put dynamic label widget and value

I have two file test.py and test.kv .
i run test.py then shows show button.
When i click on show button then def abc call.Can someone tell me how to show array in dynamic label and value(Item1=5000.Item2=1000).
Item1 5000
Item2 1000
I am using array
arr = ({'Item1': 5000},{'Item2': 1000})
test.py
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty, NumericProperty
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (600, 600)
class Invoice(Screen):
def __init__(self, **kwargs):
super(Invoice, self).__init__(**kwargs)
def abc(self):
#fetching from database
arr = ({'Item1': 5000},{'Item2': 1000})
print(arr)
class Test(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
if __name__ == '__main__':
Test().run()
test.kv
<Button#Button>:
font_size: 15
font_name: 'Verdana'
size_hint_y:None
height: 30
<Label#Label>:
font_size: 15
font_name: 'Verdana'
size_hint_y:None
height: 30
Invoice:
BoxLayout:
orientation: "vertical"
padding : 15, 15
BoxLayout:
orientation: "vertical"
padding : 5, 5
size_hint: .6, None
pos_hint: {'x': .18,}
BoxLayout:
orientation: "horizontal"
padding : 5, 5
spacing: 10, 10
size: 800, 40
size_hint: 1, None
Button:
text: "Show"
size_hint_x: .05
spacing_x: 30
on_press:root.abc()
BoxLayout:
orientation: "horizontal"
size_hint: 1, 1
BoxLayout:
orientation: "vertical"
size_hint: .5, 1
padding : 0, 15
spacing: 10, 10
size: 500, 30
Button:
text: "Invoice"
text_size: self.size
halign: 'center'
valign: 'middle'
GridLayout:
cols: 2
#orientation: "horizontal"
padding : 5, 0
spacing: 10, 0
#size: 500, 30
size_hint: 1, 1
pos: self.pos
size: self.size
Label:
size_hint_x: .35
text: "Item1"
text_size: self.size
halign: 'left'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Label:
size_hint_x: .15
text: "5000"
text_size: self.size
halign: 'right'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
In your abc() method you can create labels and add them to your layout. In order to do that, I made a few change to your code. I added an id to your GridLayout and changed your custom label class to MyLabel and added it to the py file, so that I could create them in Python. Here is the modified Python file:
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (600, 600)
class MyLabel(Label):
pass
class Invoice(Screen):
def __init__(self, **kwargs):
super(Invoice, self).__init__(**kwargs)
def abc(self):
#fetching from database
arr = ({'Item1': 5000},{'Item2': 1000})
layout = self.ids['invoices']
for invoice in arr:
for key,val in invoice.items():
lab1 = MyLabel(text=str(key),size_hint_x=.35, halign='left' )
lab2 = MyLabel(text=str(val),size_hint_x=.15, halign='right' )
layout.add_widget(lab1)
layout.add_widget(lab2)
class Test(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
And changes to the kv file included changing Label to MyLabel, moving as much as possible to the MyLabel class, and removing your example labels:
<Button#Button>:
font_size: 15
font_name: 'Verdana'
size_hint_y:None
height: 30
<MyLabel>:
font_size: 15
font_name: 'Verdana'
size_hint_y:None
height: 30
text_size: self.size
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Invoice:
BoxLayout:
orientation: "vertical"
padding : 15, 15
BoxLayout:
orientation: "vertical"
padding : 5, 5
size_hint: .6, None
pos_hint: {'x': .18,}
BoxLayout:
orientation: "horizontal"
padding : 5, 5
spacing: 10, 10
size: 800, 40
size_hint: 1, None
Button:
text: "Show"
size_hint_x: .05
spacing_x: 30
on_press:root.abc()
BoxLayout:
orientation: "horizontal"
size_hint: 1, 1
BoxLayout:
orientation: "vertical"
size_hint: .5, 1
padding : 0, 15
spacing: 10, 10
size: 500, 30
Button:
text: "Invoice"
text_size: self.size
halign: 'center'
valign: 'middle'
GridLayout:
id: invoices
cols: 2
#orientation: "horizontal"
padding : 5, 0
spacing: 10, 0
#size: 500, 30
size_hint: 1, 1
pos: self.pos
size: self.size
Although the option to iterate over the data and generate the widget dynamically is an option, the truth is that it is unbeatable in the long term. If you have structured information it is appropriate to use a design pattern and kivy offers to use a RecycleView for these cases, this implements the MVC pattern, so we just need to pass the data and establish a view where an appropriate adapter can be provided.
In your case it is enough to design a widget that is what is shown in each row:
<Item#GridLayout>:
cols: 2
text: "" # new property
value: 0 # new property
padding : 5, 0
spacing: 10, 0
Label:
size_hint_x: .35
text: root.text
halign: 'left'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Label:
size_hint_x: .15
text: str(root.value)
halign: 'right'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
And then replace the GridLayout with the RecycleView:
RecycleView:
id: rv
viewclass: 'Item'
RecycleBoxLayout:
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
And in the event of the button assign the data, in this case you must convert your data to a list of dictionaries where the fields will be the text and value attribute of Item:
def convert_data(data):
l = []
for item in data:
for key, value in item.items():
l.append({'text': key, 'value': value})
return l
class Invoice(Screen):
def abc(self):
#fetching from database
arr = ({'Item1': 5000},{'Item2': 1000})
# convert to [{'text': 'Item1', 'value': 5000}, {'text': 'Item2', 'value': 1000}]
self.rv.data = convert_data(arr)
Complete Code:
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
def convert_data(data):
l = []
for item in data:
for key, value in item.items():
l.append({'text': key, 'value': value})
return l
class Invoice(Screen):
def abc(self):
#fetching from database
arr = ({'Item1': 5000},{'Item2': 1000})
# convert to [{'text': 'Item1', 'value': 5000}, {'text': 'Item2', 'value': 1000}]
self.rv.data = convert_data(arr)
class MyApp(App):
def build(self):
return Builder.load_file('test.kv')
if __name__ == '__main__':
MyApp().run()
test.kv
<Button#Button>:
font_size: 15
size_hint_y:None
height: 30
<Label#Label>:
font_size: 15
size_hint_y:None
height: 30
<Item#GridLayout>:
cols: 2
text: ""
value: 0
padding : 5, 0
spacing: 10, 0
Label:
size_hint_x: .35
text: root.text
halign: 'left'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Label:
size_hint_x: .15
text: str(root.value)
halign: 'right'
valign: 'middle'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
Invoice:
rv: rv
BoxLayout:
orientation: "vertical"
padding : 15, 15
BoxLayout:
orientation: "vertical"
padding : 5, 5
size_hint: .6, None
pos_hint: {'x': .18,}
BoxLayout:
orientation: "horizontal"
padding : 5, 5
spacing: 10, 10
size: 800, 40
size_hint: 1, None
Button:
text: "Show"
size_hint_x: .05
spacing_x: 30
on_press:root.abc()
BoxLayout:
orientation: "horizontal"
size_hint: 1, 1
BoxLayout:
orientation: "vertical"
size_hint: .5, 1
padding : 0, 15
spacing: 10, 10
size: 500, 30
Button:
text: "Invoice"
text_size: self.size
halign: 'center'
valign: 'middle'
BoxLayout:
RecycleView:
id: rv
viewclass: 'Item'
RecycleBoxLayout:
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'

Python : call function from another class in python

Can anyont tell me how to call method from another class?
when i click on ok button then i call def insert(self): method.I am using code
def insert(self):
#insert data after call add_expense() method
RowsExpense().add_expense()
after insert data i call RowsExpense().add_expense().its calling because print('llllll') shows in console.But self.add_widget(r) not working because row not showing on screen.
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()
def add_more2(self):
self.ids.rowsExpense.add_expense()
class ExtraPopup(Popup):
mode = StringProperty("")
def __init__(self, obj, **kwargs):
super(ExtraPopup, self).__init__(**kwargs)
def add_extra(self):
self.ids.rowsExtra.add_row()
def insert(self):
#insert data after call add_expense() method
RowsExpense().add_expense()
class RowExtra(BoxLayout):
col_data = ListProperty(["?", "?", "?", "?", "?", "?", "?", "?"])
button_text = StringProperty("")
mode = StringProperty("")
def __init__(self, **kwargs):
super(RowExtra, self).__init__(**kwargs)
self.col_data[0] = ''
self.col_data[1] = ''
self.col_data[2] = ''
class RowsExtra(BoxLayout):
#orientation = "vertical"
row_count = 0
button_text = StringProperty("")
textName = StringProperty("")
def __init__(self, **kwargs):
super(RowsExtra, self).__init__(**kwargs)
def add_row(self):
for x in range(0, 3):
self.row_count += 1
r = RowExtra(button_text=str(self.row_count))
self.add_widget(r)
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)
def add_seller_expenses(self):
self.mode = "Add"
popup = ExtraPopup(self)
popup.ids.rowsExtra.textName = self.ids.name.text
popup.ids.rowsExtra.add_row()
popup.open()
class Rows(BoxLayout):
row_count = 0
def __init__(self, **kwargs):
super(Rows, self).__init__(**kwargs)
self.add_more()
def add_more(self):
self.row_count += 1
self.add_widget(Row(button_text=str(self.row_count)))
class RowExpense(BoxLayout):
col_data = ListProperty(["?", "?", "?"])
button_text = StringProperty("")
def __init__(self, **kwargs):
super(RowExpense, self).__init__(**kwargs)
self.col_data[0] = ' '
class RowsExpense(BoxLayout):
row_count = 0
def __init__(self, **kwargs):
super(RowsExpense, self).__init__(**kwargs)
def add_expense(self):
print('llllll')
for x in range(0, 3):
self.row_count += 1
r = RowExpense(button_text=str(self.row_count))
self.add_widget(r)
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'
Button:
text: "Button"
size_hint_x: None
top: 200
on_press: root.add_seller_expenses()
<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()
BoxLayout:
orientation: "horizontal"
size_hint: 1, 1
BoxLayout:
orientation: "vertical"
padding: 10, 5
size_hint: .5, 1
BoxLayout:
orientation: "horizontal"
spacing: 10, 10
size: 600, 50
size_hint: 1, None
Label:
text: "SN"
text_size: self.size
size_hint_x: .3
halign: "center"
ScrollView:
RowsExpense:
id: rowsExpense
BoxLayout:
orientation: "horizontal"
size_hint_x: .2
size_hint_y: .2
Button:
text: "+Add More"
on_press: root.add_more2()
<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"
<ExtraPopup>:
title: " Extra"
title_size: 20
title_font: "Verdana"
size_hint: None, None
size: 400, 400
auto_dismiss: False
BoxLayout:
orientation: "vertical"
padding : 10, 5
spacing: 10, 10
BoxLayout:
orientation: "horizontal"
spacing: 10, 10
size: 550, 30
size_hint: 1, None
Label:
size_hint_x: .3
text: "SN"
text_size: self.size
valign: 'bottom'
halign: 'center'
Label:
size_hint_x: .7
text: "Name"
text_size: self.size
valign: 'bottom'
halign: 'center'
ScrollView:
RowsExtra:
id: rowsExtra
BoxLayout:
orientation: "horizontal"
size_hint_x: .3
size_hint: .15, .1
Button:
text: "+Add More"
valign: 'bottom'
on_press: root.add_extra()
BoxLayout:
orientation: "horizontal"
padding : 10, 5
spacing: 10, 10
size_hint: .5, .2
pos_hint: {'x': .25, 'y':.25}
Button:
text: 'Ok'
id: ok_text
on_release:
root.insert()
root.dismiss()
Button:
text: 'Cancel'
#size_hint_x: .5
on_release: root.dismiss()
<RowExtra>:
size_hint_y: None
height: self.minimum_height
height: 30
Button:
text: root.button_text
size_hint_x: .3
#top: 200
TextInput:
text: root.col_data[1]
size_hint_x: .7
<RowsExtra>:
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
<RowsExpense>:
size_hint_y: None
height: self.minimum_height
orientation: "vertical"
<RowExpense>:
size_hint_y: None
height: self.minimum_height
height: 30
TextInput:
text: root.col_data[0]
multiline: False
<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
You're calling class method while You should call instance method instead.
You need to store reference to Your RowsExpense instance in Your root class and run Your method through that reference. Then You can either pass that reference to the class through which You want to run the method as an argument in it's init function or use plain old:
App.get_running_app().root.whateverYouwanttodohere()

Python : How to add row dynamic

I am new to python/Kivy.
I have two files test.py and test.ky.
Now I am using two static row with serial number 1 and 2.
Can anyone tell me?
How to add row dynamic when click on '+add more' button.Now 2 row shows which are static row with serial number increment. I want add row dynamic 1 to 10 with serial number increment.
test.py
import kivy
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
Window.size = (450, 525)
class display(Screen):
def add_more(self):
print('test')
class test(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
if __name__ == '__main__':
test().run()
test.kv
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'
BoxLayout:
orientation: "horizontal"
spacing: 0, 5
Button:
text: '1'
size_hint_x: .2
TextInput:
size_hint_x: .8
BoxLayout:
orientation: "horizontal"
spacing: 0, 5
Button:
text: '2'
size_hint_x: .2
TextInput:
size_hint_x: .8
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.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
Can someone help me?
You can make a custom class for Row and Rows, then have a method adding rows.
I modified your example a bit. Try this:
from kivy.uix.screenmanager import Screen
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 StringProperty
Window.size = (450, 525)
class display(Screen):
def add_more(self):
self.ids.rows.add_row()
class Row(BoxLayout):
button_text = StringProperty("")
class Rows(BoxLayout):
orientation = "vertical"
row_count = 0
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)))
class test(App):
def build(self):
self.root = Builder.load_string(KV)
return self.root
KV = """
<Row>:
orientation: "horizontal"
spacing: 0, 5
Button:
text: root.button_text
size_hint_x: .2
TextInput:
size_hint_x: .8
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.dismiss()
Button:
text: 'Cancel'
on_release: root.dismiss()
"""
test().run()

Categories

Resources