I am struggling with referring to Labels on ScreenManager screens. I have a RecycleView and the class accumulates a total in the init. I would like to put this total on the screen outside the RecycleView. The id in the .kv file is t_pay. The total is accumulated in the Nq_rv class as self.total_bill. How do I take the total from this class and update the total field on the pay screen?
Note the total field at the bottom of this screenshot:
My main.py file is:
class Nq_rv(RecycleView):
def __init__(self, **kwargs):
super(Nq_rv, self).__init__(**kwargs)
bill_text = []
self.total_bill = 0.0
with open('bill.csv') as bill:
bill_reader = csv.reader(bill)
for row in bill_reader:
for item in range(len(row)):
if item < 2:
item_text = row[item]
else:
item_text = '{0:.2f}'.format(float(row[2]))
self.total_bill += float(row[2]) #total accumulation
text_row = {'text': item_text}
bill_text.append(text_row)
self.data = bill_text
#How do I update the t-pay field in the .kv file?
class Sm(ScreenManager):
total_pay = ObjectProperty() #I don't know if this is needed
def send_survey(self):
mypopup = MyPopup()
mypopup.show_popup('Survey', 'Survey sent!', 'OK!')
def pay(self):
mypopup = MyPopup()
mypopup.show_popup('Pay', 'Please put your card in the card reader and follow the prompts.', 'OK!')
def tip_slider_update(self):
self.ids.pay_screen.tip.text = '{0:.2f}'.format(float(self.ids.pay_screen.total_pay.text) * self.ids.pay_screen.tip_sldr.value)
def close_app(self):
App.get_running_app().stop()
class Pay_screen(Screen):
pass
class Survey_screen(Screen):
pass
class Finish_screen(Screen):
pass
class ImageButton(ButtonBehavior, Image):
pass
class Nq_button(Button):
pass
class MyPopup(Popup):
def show_popup(self, title_text, label_text, button_text):
mytext= label_text
content = BoxLayout(orientation="vertical")
content.add_widget(Label(text=mytext, font_size=20, text_size=(300, None)))
mybutton = Button(text="Ok!", size_hint=(1,.20), font_size=20)
content.add_widget(mybutton)
mypopup = Popup(content = content,
title = title_text,
auto_dismiss = False,
size_hint = (.5, .5))
mybutton.bind(on_press=mypopup.dismiss)
mypopup.open()
class nextqualApp(App):
icon = 'nextqual.png'
title = 'Pay / survey / join'
if __name__ == '__main__':
nextqualApp().run()
my .kv file is nextqual.kv:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
Sm:
id: sm
transition: FadeTransition()
Pay_screen:
id: pay_screen
manager: sm
Survey_screen:
id: survey_screen
manager: sm
<Nq_check_label#Label>:
markup: True
multiline: True
<Nq_check_items#Label>:
color: 0,0,0,1
<Nq_rv>:
viewclass: 'Nq_check_items'
RecycleGridLayout:
cols: 3
default_size: None, dp(20)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<Pay_screen>:
name: 'pay'
total_pay: t_pay
BoxLayout:
orientation: "vertical"
padding: 6
font_size: '24'
BoxLayout:
size_hint_y: None
height: "40dp"
Button:
text: "Pay your bill"
on_release: app.root.current = 'pay'
Button:
text: "Tell us how we did"
on_release: app.root.current = 'survey'
Button:
text: "I'm finished"
BoxLayout:
BoxLayout:
padding: 12
orientation: 'vertical'
BoxLayout:
height: "40dp"
size_hint_y: None
Label:
text: 'Table: 4'
Label:
text: 'Server: Julie'
Label:
text: 'Check # 58645'
BoxLayout:
canvas:
Color:
rgb: 255,255,255,255
Rectangle:
size: self.size
pos: self.pos
Nq_rv:
BoxLayout:
height: "30dp"
size_hint_y: None
Label:
text: 'Total check:'
size_hint_x: 75
#This is the field I want to update!
Label:
id: t_pay
text: '0.00'
halign: 'right'
size_hint_x: 25
<Survey_screen>:
name: "survey"
BoxLayout:
orientation: "vertical"
padding: 6
font_size: '24'
BoxLayout:
size_hint_y: None
height: "40dp"
Button:
text: "Pay your bill"
on_release: app.root.current = 'pay'
Button:
text: "Tell us how we did"
on_release: app.root.current = 'survey'
Button:
text: "I'm finished"
BoxLayout:
BoxLayout:
height: "100dp"
size_hint_y: None
Label:
size_hint_x: 40
Button:
size_hint_x: 20
text:"Send survey"
halign: "center"
on_press: app.root.send_survey()
Label:
size_hint_x: 40
embryo asked for some sample data. the file 'bill.csv' looks like this:
1,Seafood Sampler,15.99
1,Tea Smoked Duck,19.95
2,Shredded Duck with Ginger,22
1,Deli Rueben,9.95
1,Sam Adams,3
1,Cotswold Premium,4
1,Btl Pinot Noir,25
The idea of creating custom classes is to give you custom properties, for example Nq_rv is not only a RecycleView but a class that you can new properties, for example you can give the property total_bill that has the information of the total. That property will be accessible from the outside so you can make a binding with the text of t_pay:
*.py
class Nq_rv(RecycleView):
total_bill = NumericProperty(0.00) # <-- new property
def __init__(self, **kwargs):
super(Nq_rv, self).__init__(**kwargs)
self.load_data()
def load_data(self):
bill_text = []
total = 0.0
with open('bill.csv') as bill:
bill_reader = csv.reader(bill)
for row in bill_reader:
for item, val in enumerate(row):
if item < 2:
item_text = val
else:
item_text = '{0:.2f}'.format(float(val))
total += float(val) #total accumulation
text_row = {'text': item_text}
bill_text.append(text_row)
self.data = bill_text
self.total_bill = total # <-- update property
*.kv
BoxLayout:
canvas:
Color:
rgb: 255,255,255,255
Rectangle:
size: self.size
pos: self.pos
Nq_rv:
id: rv # <-- set id
BoxLayout:
height: "30dp"
size_hint_y: None
Label:
text: 'Total check:'
size_hint_x: 75
#This is the field I want to update!
Label:
id: t_pay
text: '{0:.2f}'.format(rv.total_bill) # <--- binding
halign: 'right'
size_hint_x: 25
Related
I am new to kivy ...I wanted make a to do list app....and wanted to add a name of the task from user and a button which can get a tick mark on press....
some how i got the thing to get a label on screen but i couldn't get it.
This main.py
class ListWidget(RecycleView):
def update(self):
self.data = [{'text': str(item)}for item in self.item]
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.item = []
class RootWidget(BoxLayout):
inputbutton = ObjectProperty(None).
inputcontent = ObjectProperty(None).
outputcontent = ObjectProperty(None).
def add_item(self):
if self.inputcontent.text != " ":
formatted = f'\n*{self.inputcontent.text}'
self.outputcontent.item.append(formatted)
self.outputcontent.update()
self.inputcontent.text = ""
class MyApp(App):
def build(self):
return RootWidget()
MyApp().run()
this is my.kv file
<RootWidget>
inputbutton: inputbutton
inputcontent: inputcontent
outputcontent: outputcontent
orientation: 'vertical'
BoxLayout:
orientation: 'vertical'
size_hint: 1, 0.25
Label:
text: 'TO-DO'
font_size: 32
size_hint: 1,0.3
BoxLayout:
orientation: 'horizontal'
Button:
id: inputbutton
size_hint: 0.25, 1
text: 'add'
on_press:root.add_item()
TextInput:
id: inputcontent
multiline: False
ListWidget:
id: outputcontent
viewclass: 'Label'
orientation: 'vertical'
RecycleBoxLayout:
default_size: None,dp(56)
default_size_hint: 0.4,None
size_hint_y: None
height:self.minimum_height
orientation: 'vertical'
this is the output
This is the output
You can use a custom viewclass that does what you want. Something like this:
class LabelAndButton(GridLayout):
text = StringProperty() # must have a text property (used in ListWidget.data)
Then in the kv you can use this class as the viewclass and define how it looks:
ListWidget:
id: outputcontent
viewclass: 'LabelAndButton' # use the new class
orientation: 'vertical'
RecycleBoxLayout:
default_size: None,dp(56)
default_size_hint: 0.4,None
size_hint_y: None
height:self.minimum_height
orientation: 'vertical'
<LabelAndButton>:
cols:2
Label:
text: root.text
Button:
text: root.text
A sample Kivy app populates Recycleview upon a button click.
However, when I copy the code from the class functions into the App.build to populate the Recycleview upon app loading, it doesn't do it. Is it not referencing the Recycleview correctly? Why is this not working?
I saw some answers here that used "self.ids." to find the Recycleview data, and also "App.get_running_app()." but it didn't work.
(Note: requires Kivy 2.0.0rc4 to compile)
from random import sample, randint
from string import ascii_lowercase
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
kv = """
<Row#RecycleKVIDsDataViewBehavior+BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
Label:
id: name
Label:
text: root.value
<Test>:
canvas:
Color:
rgba: 0.3, 0.3, 0.3, 1
Rectangle:
size: self.size
pos: self.pos
rv: rv
orientation: 'vertical'
GridLayout:
cols: 3
rows: 2
size_hint_y: None
height: dp(108)
padding: dp(8)
spacing: dp(16)
Button:
text: 'Populate list'
on_press: root.populate()
Button:
text: 'Sort list'
on_press: root.sort()
Button:
text: 'Clear list'
on_press: root.clear()
BoxLayout:
spacing: dp(8)
Button:
text: 'Insert new item'
on_press: root.insert(new_item_input.text)
TextInput:
id: new_item_input
size_hint_x: 0.6
hint_text: 'value'
padding: dp(10), dp(10), 0, 0
BoxLayout:
spacing: dp(8)
Button:
text: 'Update first item'
on_press: root.update(update_item_input.text)
TextInput:
id: update_item_input
size_hint_x: 0.6
hint_text: 'new value'
padding: dp(10), dp(10), 0, 0
Button:
text: 'Remove first item'
on_press: root.remove()
RecycleView:
id: rv
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
viewclass: 'Row'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(2)
"""
Builder.load_string(kv)
class Test(BoxLayout):
def populate(self):
self.rv.data = [
{'name.text': ''.join(sample(ascii_lowercase, 6)),
'value': str(randint(0, 2000))}
for x in range(50)]
def sort(self):
self.rv.data = sorted(self.rv.data, key=lambda x: x['name.text'])
def clear(self):
self.rv.data = []
def insert(self, value):
self.rv.data.insert(0, {
'name.text': value or 'default value', 'value': 'unknown'})
def update(self, value):
if self.rv.data:
self.rv.data[0]['name.text'] = value or 'default new value'
self.rv.refresh_from_data()
def remove(self):
if self.rv.data:
self.rv.data.pop(0)
class TestApp(App):
def build(self):
t = Test()
t.rv.data = [
{'name.text': ''.join(sample(ascii_lowercase, 6)),
'value': str(randint(0, 2000))}
for x in range(50)]
t.rv.refresh_from_data()
return Test()
if __name__ == '__main__':
TestApp().run()
One solution is to perform the Recycleview update from the Test class.
from random import sample, randint
from string import ascii_lowercase
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
kv = """
<Row#RecycleKVIDsDataViewBehavior+BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
Label:
id: name
Label:
text: root.value
<Test>:
canvas:
Color:
rgba: 0.3, 0.3, 0.3, 1
Rectangle:
size: self.size
pos: self.pos
rv: rv
orientation: 'vertical'
GridLayout:
cols: 3
rows: 2
size_hint_y: None
height: dp(108)
padding: dp(8)
spacing: dp(16)
Button:
text: 'Populate list'
on_press: root.populate()
Button:
text: 'Sort list'
on_press: root.sort()
Button:
text: 'Clear list'
on_press: root.clear()
BoxLayout:
spacing: dp(8)
Button:
text: 'Insert new item'
on_press: root.insert(new_item_input.text)
TextInput:
id: new_item_input
size_hint_x: 0.6
hint_text: 'value'
padding: dp(10), dp(10), 0, 0
BoxLayout:
spacing: dp(8)
Button:
text: 'Update first item'
on_press: root.update(update_item_input.text)
TextInput:
id: update_item_input
size_hint_x: 0.6
hint_text: 'new value'
padding: dp(10), dp(10), 0, 0
Button:
text: 'Remove first item'
on_press: root.remove()
RecycleView:
id: rv
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
viewclass: 'Row'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(2)
"""
Builder.load_string(kv)
class Test(BoxLayout):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.populate()
def populate(self):
self.rv.data = [
{'name.text': ''.join(sample(ascii_lowercase, 6)),
'value': str(randint(0, 2000))}
for x in range(50)]
def sort(self):
self.rv.data = sorted(self.rv.data, key=lambda x: x['name.text'])
def clear(self):
self.rv.data = []
def insert(self, value):
self.rv.data.insert(0, {
'name.text': value or 'default value', 'value': 'unknown'})
def update(self, value):
if self.rv.data:
self.rv.data[0]['name.text'] = value or 'default new value'
self.rv.refresh_from_data()
def remove(self):
if self.rv.data:
self.rv.data.pop(0)
class TestApp(App):
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
It seems to be a question of the hierarchy and Kivy design. There's no instance of the RecycleView getting passed to the top object App.
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()
I have two file demo.py and demo.kv.
Can anyone tell me how to call function from one class to another class?I want to call def calculate(self): from def on_text(self, text_input, value):.Now i am using code
def on_text(self, text_input, value):
App.get_running_app().User.calculate()
But it gives error AttributeError: 'Test' object has no attribute 'User'
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 add_more(self):
self.ids.rows.add_row()
def calculate(self):
rows = self.ids.rows
total = 0
for row in rows.children:
text = row.ids.number_input.text
total += int(text) if text != "" else 0 # validate if the entry is not empty
self.total_value.text = str(total)
class Row(BoxLayout):
col_data = ListProperty(["?", "?", "?", "?", "?"])
button_text = StringProperty("")
col_data3 = StringProperty("")
col_data4 = StringProperty("")
def __init__(self, **kwargs):
super(Row, self).__init__(**kwargs)
self.ids.number_input.bind(text=self.on_text)
def on_text(self, text_input, value):
print('Calling')
App.get_running_app().User.calculate()
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 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 Test(App):
def build(self):
self.root = Builder.load_file('demo.kv')
return MainMenu()
if __name__ == '__main__':
Test().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:
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
total_value:total_value
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"
padding : 10, 5
spacing: 10, 10
size: 200, 40
size_hint: None, None
Label:
size_hint_x: .7
text: "Total value"
TextInput:
id: total_value
on_focus:root.test()
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
One way to solve this problem without accessing the tree of kivy hierarchies is using global variables, but it is advisable not to abuse this type of variables since the errors generated by its misuse are difficult to trace.
[...]
class Row(BoxLayout):
[...]
def on_text(self, text_input, value):
print('Calling')
popup.calculate()
class rv(BoxLayout):
[...]
def add(self):
self.mode = "Add"
global popup
popup = User()
popup.open()
[...]
This may be a duplicate of this question: Im New on kivy Python classes
Firstly:
class Test(App):
def build(self):
self.root = Builder.load_file('demo.kv')
return MainMenu()
This is a bit unnecessary, you can instead do:
class demo(App):
def build(self):
return MainMenu()
App's search for a kv file with a lowercase name equal to the App's name.
Secondly:
<User>:
id: user
total_value:total_value
This is not how you use ids in the kivy Widgets. When you define a Kivy Widget, ie:
class KivyWidgetName(KivyLayout):
pass
<KivyWidgetName>:
You're creating a class. When you add your custom widget to a parent Widget, you are creating an Object of that KivyWidget Class. Sometimes however if you're making more than one, you need to give them ids so their parent can separate them from each other.
ie:
<RootWidget>:
KivyWidgetName:
id: first_instance
KivyWidgetName:
id: second_instance
So now RootWidget can access two different versions of the class, if it wants to access the first one, it can do:
self.ids.first_instance
You can also access them by the index number in which they appear, so this
can also be done by:
self.ids[1]
However explicit is typically better than implicit
Thirdly, you had the right idea to this:
App.get_running_app().User.calculate()
but it should actually be something like this:
App.get_running_app().root.ids.[INSERTID]
So in this case, you're getting the root widget of app (which is ), then you need to use the ids to get to the proper address.
For example, take this:
:
User:
id: 'user_one'
User:
id: 'user_two'
To access the user calculate function, you can do this:
App.get_running_app().root.ids.user_one.calculate
If you however have a number of children, you'll have to seek all of their ids until you find user:
For example:
<TestWidget>:
User:
id: 'user_one'
User:
id: 'user_two'
<RootWidget>:
TestWidget:
id: 'test_one'
TestWidget:
id: 'test_two'
So to get to user one of test two, you can do this:
App.get_running_app().root.ids.test_two.ids.user_one.calculate
You might need to re-arrange your rootwidget to be something else that just contains your main menu to help you with this but that decision is up to you ultimately.
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'