So i am having trouble trying to set my button text from another class.
if you look at the function set_people, i use PosScreen.people = name to change the name, but the button text wont change. So i figured i have to acccess it via ids but im stuck on how to do it from another class. Here is my code:
test.py
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
pos_dialog = None
class PosScreen(Screen):
people = StringProperty("")
def __init__(self, **kwargs):
super(PosScreen, self).__init__(**kwargs)
def on_pre_enter(self, *args):
self.people = "No one"
def close_dialog(self, *obj):
global pos_dialog
pos_dialog.dismiss()
pos_dialog = None
def open_people_dialog(self):
global pos_dialog
if not pos_dialog:
pos_dialog = MDDialog(
title="Choose customer",
type="custom",
content_cls=DialogContentPosAddPeople(),
buttons=[
MDFlatButton(
text="Close",
theme_text_color="Custom",
on_release=self.close_dialog
)
]
)
pos_dialog.open()
class DialogContentPosAddPeople(BoxLayout):
def __init__(self, **kwargs):
super(DialogContentPosAddPeople, self).__init__(**kwargs)
self.set_list_people()
def set_list_people(self, text="", search=False):
peeps = ["Brian", "AJ", "Nick", "Kevin", "Howie"]
def add_name_item(name):
self.ids.rv.data.append(
{
"viewclass": "OneLineListItem",
"text": name,
"on_release": lambda x=name: self.set_people(x),
}
)
self.ids.rv.data = []
for name in peeps:
if search:
if text.lower() in name.lower():
add_name_item(name)
else:
add_name_item(name)
def set_people(self, name):
global pos_dialog
PosScreen.people = name # it changes the variable but button text doesn't change
PosScreen.ids.people_button.text = name # Not accessible NoneType
pos_dialog.dismiss()
pos_dialog = None
return
class testApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "Teal"
sm = ScreenManager()
sm.add_widget(PosScreen(name="pos"))
return sm
if __name__ == "__main__":
testApp().run()
test.kv
PosScreen:
<PosScreen>:
name: "pos"
people_button: people_button
canvas.before:
Color:
rgba: 72/255, 72/255, 72/255, 1
Rectangle:
pos: self.pos
size: self.size
MDBoxLayout:
orientation: "vertical"
RelativeLayout:
orientation: "vertical"
MDRectangleFlatIconButton:
id: people_button
icon: "account-circle-outline"
text: root.people
pos_hint:{'x': 0.05, 'center_y': 0.9}
md_bg_color: 0, 0, 0, 1
on_release: root.open_people_dialog()
<DialogContentPosAddPeople>:
rv: rv
orientation: "vertical"
spacing: dp(10)
padding: dp(20)
size_hint_y: None
height: "240dp"
MDBoxLayout:
adaptive_height: True
MDIconButton:
icon: 'magnify'
MDTextField:
id: search_field
hint_text: 'Search name'
on_text: root.set_list_people(self.text, True)
RecycleView:
id: rv
key_viewclass: 'viewclass'
key_size: 'height'
RecycleBoxLayout:
padding: dp(10)
default_size: None, dp(48)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Expected result: When I click one of the names, it will close the dialog and change the button's text to the name that i picked
Can someone help me shed some light on how can I achieve this?
So i solved it by passing the screen object when calling the second class.
class PosScreen(Screen):
...
def open_people_dialog(self):
global pos_dialog
if not pos_dialog:
pos_dialog = MDDialog(
...
content_cls=DialogContentPosAddPeople(self), # Added self here
...
)
]
)
pos_dialog.open()
and in the called class:
class DialogContentPosAddPeople(BoxLayout):
def __init__(self, master, **kwargs): # Added master
super(DialogContentPosAddPeople, self).__init__(**kwargs)
self.master = master
def set_people(self, name):
self.master.people = name #Referencing the master
self.master.ids.people_button.text = name
Related
I have a music app written with kivymd that scans your default download directory and extracts information about your audio files. (Basically retrieving the IDV3 tags and other things)
I only have 20 audio files at max on my desktop which shouldn't be a problem but I decided to test my program with many audio (like 120) files and the results were horrible.
The interface was so slow that it barely worked. How am I supposed to display large set of widgets in kivy without causing such catastrophic performance degradation?
Also, how can I make the loading of the widgets asynchronous so that they don't take up a lot of time on startup as well?
A minimal reproducible example of my code:
import os
from kivy.lang import Builder
from kivy.uix.behaviors import ButtonBehavior
from kivy.properties import ObjectProperty
from kivymd.uix.behaviors import RectangularRippleBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.app import MDApp
class Song:
"""Class for extracting information about an audio file."""
def __init__(self, path: str):
self._path = path
self._title = "My Song"
self._artist = "Unknown artist"
# the album cover is going to a texture retrieved from the file itself
# but in this case, I will refer to a file.
self._album_cover = "Default-Album-Cover.jpg"
#property
def path(self):
return self._path
#property
def title(self):
return self._title
#property
def artist(self):
return self._artist
#property
def album_cover(self):
return self._album_cover
class SongCard(ButtonBehavior, RectangularRippleBehavior, MDBoxLayout):
"""Custom widget for creating cards."""
song_obj = ObjectProperty(Song("dummy song"), rebind=True)
class MainApp(MDApp):
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
self.theme_cls.theme_style = "Dark"
self.kv = Builder.load_string('''
#:kivy 2.0.0
<SongCard>:
orientation: "vertical"
size_hint_y: None
height: "300dp"
radius: ("10dp",)
canvas.before:
Color:
rgba: app.theme_cls.bg_dark
RoundedRectangle:
pos: self.pos
size: self.size
radius: root.radius
FitImage:
source: root.song_obj.album_cover
MDLabel:
text: root.song_obj.title
adaptive_height: True
MDLabel:
text: root.song_obj.artist
adaptive_height: True
ScrollView:
do_scroll_x: False
MDGridLayout:
id: song_grid
cols: 2
adaptive_height: True
spacing: "10dp"
padding: "10dp"
''')
def build(self):
return self.kv
def on_start(self):
for _ in range(25):
for audio_file in os.listdir(os.path.join(os.path.expanduser('~'), "Downloads")):
if audio_file.endswith(".mp3"):
song_obj = Song(audio_file)
self.kv.ids.song_grid.add_widget(
SongCard(
song_obj=song_obj
)
)
if __name__ == '__main__':
MainApp().run()
You can use the RecycleView class to manage the amount of widgets and help your program to be able to accept a big amount of widgets, the ScrollView is great but recycleview manages data as dictionary and then you can use two BoxLayout class under a BoxLayout widget to have the GridLayout class functionality, I tested this with 213 elements and it works with a lot of widgets and good performance:
import os
from kivy.lang import Builder
from kivy.uix.behaviors import ButtonBehavior
from kivy.properties import ObjectProperty, StringProperty
from kivymd.uix.behaviors import RectangularRippleBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.app import MDApp
from kivy.uix.recyclegridlayout import RecycleGridLayout
class Song:
"""Class for extracting information about an audio file."""
def __init__(self, path: str):
self._path = path
self._title = "My Song"
self._artist = "Unknown artist"
# the album cover is going to a texture retrieved from the file itself
# but in this case, I will refer to a file.
self._album_cover = "Default-Album-Cover.jpg"
## #property
## def path(self):
## return self._path
##
## #property
## def title(self):
## return self._title
##
## #property
## def artist(self):
## return self._artist
##
## #property
## def album_cover(self):
## return self._album_cover
class SongCard(MDBoxLayout):
"""Custom widget for creating cards."""
#song_obj = ObjectProperty(Song("dummy song"), rebind=True)
song_obj = StringProperty()
class B2(ButtonBehavior, RectangularRippleBehavior, MDBoxLayout):
"""Custom widget for creating cards."""
pass
class MainApp(MDApp):
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
self.theme_cls.theme_style = "Dark"
self.kv = Builder.load_string('''
#:kivy 2.0.0
<SongCard>:
orientation: "vertical"
size_hint_y: None
height: "300dp"
#radius: ("10dp",)
BoxLayout:
padding: dp(10)
spacing: dp(5)
B2:
padding: dp(10)
canvas.before:
Color:
rgba: [0,1,0,.2]
RoundedRectangle:
pos: self.pos
size: self.size
radius: [30,]
orientation: "vertical"
spacing: dp(5)
FitImage:
size_hint: 1,1
source: "Help5/cc.png" #root.song_obj.album_cover
MDLabel:
text: root.song_obj[:40] #.title
adaptive_height: True
MDLabel:
text: " - jbsidis" #.artist
adaptive_height: True
B2:
padding: dp(10)
canvas.before:
Color:
rgba: [1,0,0,.2]
RoundedRectangle:
pos: self.pos
size: self.size
radius: [30,]
orientation: "vertical"
spacing: dp(5)
FitImage:
source: "Help5/cc.png" #root.song_obj.album_cover
MDLabel:
text: root.song_obj[:40] #.title
adaptive_height: True
MDLabel:
text: " - jbsidis" #.artist
adaptive_height: True
Screen:
BoxLayout:
orientation: "vertical"
#do_scroll_x: False
RecycleSupportGridLayoutjbsidis:
id: song_grid
key_viewclass: 'viewclass'
key_size: 'height'
RecycleBoxLayout:
padding: dp(10)
default_size: None, dp(248)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<RecycleSupportGridLayoutjbsidis#RecycleView>:
''')
def build(self):
return self.kv
def on_start(self):
#for _ in range(25):
for audio_file in os.listdir("/media/jbsidis/Android/Q/D/Anthony Robbins/"): #os.path.join(os.path.expanduser('~'), "Downloads")
#if audio_file.endswith(".mp3"):
#song_obj = Song(audio_file)
self.kv.ids.song_grid.data.append(
{
"viewclass": "SongCard",
"song_obj": audio_file
}
)
## {
## "viewclass": "CustomOneLineIconListItem",
## "icon": name_icon,
## "text": name_icon,
## "on_release": lambda:Clipboard.copy(name_icon),
## }
## self.kv.ids.song_grid.add_widget(
## SongCard(
## song_obj=song_obj
## )
## )
if __name__ == '__main__':
MainApp().run()
Pictures:
I want to display a table in one of the screen and the displayed data in the table should come from the loan database, loan table.
I have created the database and also there is no error which is being shown. The table is created but there is no data displayed on it.Its empty table.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
import json
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty
from kivy.uix.recyclegridlayout import RecycleGridLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.popup import Popup
from loandatabase import Database
database = Database()
Builder.load_file('design.kv')
class MainScreen(Screen):
def goto_admin_page(self):
self.manager.current = "adminfirst_screen"
def goto_agent_page(self):
pass
def goto_customer_page(self):
pass
class AdminScreenFirst(Screen):
def go_to_adminsecond(self,uname,pword):
with open("users.json") as file:
users =json.load(file)
if uname in users and users[uname]['password'] == pword:
self.manager.current = "adminsecond_screen"
else:
self.ids.login_wrong.text = "Invalid Credentials.Please Contact the administrator"
class AdminScreenSecond(Screen):
def go_to_adminpending(self):
self.manager.current = "adminPending_screen"
def go_to_adminapproved(self):
self.manager.current = "adminsecond_screen"
def go_to_adminrejected(self):
self.manager.current = "adminsecond_screen"
class AdminPendingScreen(Screen):
def display(self):
self.manager.current = "rv_screen"
class RV(Screen):
data_items = ListProperty([])
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
for row in database.view():
for col in row:
self.data_items.append(col)
class TextInputPopup(Popup):
obj = ObjectProperty(None)
obj_text = StringProperty("")
def __init__(self, obj, **kwargs):
super(TextInputPopup, self).__init__(**kwargs)
self.obj = obj
self.obj_text = obj.text
class SelectableRecycleGridLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleGridLayout):
''' Adds selection and focus behaviour to the view. '''
class SelectableButton(RecycleDataViewBehavior, Button):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableButton, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableButton, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
def on_press(self):
popup = TextInputPopup(self)
popup.open()
def update_changes(self, txt):
self.text = txt
class RootWidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == "__main__":
MainApp().run()
my kivy code :
<MainScreen>:
GridLayout:
cols:1
GridLayout:
cols:1
padding:15,15
spacing:20,20
Label:
text:"Login"
font_size:"20sp"
Button:
text:"Admin"
on_press : root.goto_admin_page()
Button:
text:"Agent"
on_press : root.goto_agent_page()
Button:
text: "Customer"
on_press:root.goto_customer_page()
<AdminScreenFirst>:
GridLayout:
cols:1
padding:15,15
spacing:20,20
Label:
text:"Admin Login"
font_size:"20sp"
TextInput:
id:username
hint_text:"Username"
TextInput:
id:password
password:True
hint_text:"Password"
RelativeLayout:
Button:
text:"Login"
on_press : root.go_to_adminsecond(root.ids.username.text,root.ids.password.text)
size_hint :0.3,0.5
pos_hint: {'center_x' : 0.5 , 'center_y' : 0.6}
Label:
id:login_wrong
text:""
<AdminScreenSecond>:
GridLayout:
cols:1
padding:15,15
spacing:20,20
Label:
text:"Loan"
font_size:"20sp"
Button:
text:"Pending Request"
on_press:root.go_to_adminpending()
Button:
text:"Approved"
on_press: root.go_to_adminapproved()
Button:
text:"Rejected"
on_press: root.go_to_adminrejected()
<AdminPendingScreen>:
GridLayout:
cols:1
padding:15,15
spacing:20,20
Label:
text:"Loans"
Button:
text:"Display"
on_press: root.display()
Label:
id:display
text:""
<TextInputPopup>:
title: "Popup"
size_hint: None, None
size: 400, 400
auto_dismiss: False
BoxLayout:
orientation: "vertical"
TextInput:
id: txtinput
text: root.obj_text
Button:
size_hint: 1, 0.2
text: "Save Changes"
on_release:
root.obj.update_changes(txtinput.text)
root.dismiss()
Button:
size_hint: 1, 0.2
text: "Cancel Changes"
on_release: root.dismiss()
<RV>:
BoxLayout:
orientation: "vertical"
GridLayout:
size_hint: 1, None
size_hint_y: None
height: 25
cols: 11
Label:
text: "LoanID"
Label:
text: "Name"
Label:
text:"Tenure"
Label:
text:"Balance"
Label:
text:"LoanType"
Label:
text:"InterestType"
Label:
text:"Interest % p.a."
Label:
text:"Security"
Label:
text:"Total"
Label:
text:"EMI"
Label:
text:"Request"
BoxLayout:
RecycleView:
viewclass: 'SelectableButton'
data: [{'text': str(x)} for x in root.data_items]
SelectableRecycleGridLayout:
cols: 11
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
<RootWidget>:
MainScreen:
name:"mainfirst_screen"
AdminScreenFirst:
name:"adminfirst_screen"
AdminScreenSecond:
name:"adminsecond_screen"
AdminPendingScreen:
name:"adminPending_screen"
RV:
name:"rv_screen"
my database code:
import sqlite3
class Database:
def __init__(self):
self.conn = sqlite3.connect("loandatabase.db")
self.cur = self.conn.cursor()
self.cur.execute("CREATE TABLE IF NOT EXISTS loan (id INTEGER PRIMARY KEY, customer_name TEXT, tenure integer, balance integer, loantype TEXT, interesttype TEXT,interest integer,security TEXT,totalpayment integer,emi integer,instruction TEXT)")
self.conn.commit()
def insert(self,cname,tenure,blance,ltype,itype,interest,security,tpay,emi,instr):
self.cur.execute("INSERT INTO loan VALUES (NULL,?,?,?,?,?,?,?,?,?,?)",(cname,tenure,blance,ltype,itype,interest,security,tpay,emi,instr))
self.conn.commit()
def view(self):
self.cur.execute("SELECT * FROM loan")
rows = self.cur.fetchall()
return rows
def __del__(self):
self.conn.close()
[![enter image description here][1]][1]
I would request you to help me with it. Thank You in Advance
I am using Kivy,Python,sqlite3
This is displayed:
[1]: https://i.stack.imgur.com/fkUvS.png
Try this:
class MainApp(App):
title = 'Title of the app'
def build(self):
sm = ScreenManager('''You can add transition here''')
sm.add_widget(MainScreen(name='main'))
sm.add_widget(AdminScreenFirst(name='admin1'))
sm.add_widget(AdminScreenSecond(name='admin2'))
sm.add_widget(AdminPendingScreen(name='pending'))
sm.add_widget(RV(name='rv'))
return sm
In a previous question, I asked how to have a row of text inputs which is dinamically added on press of a button, all from a py script.
I am now attempting at moving all my layout code to a kv file. While this was pretty straightforward for the widgets which appear on screen by default, I am not really sure of how to define the dinamically added text inputs from the kv file.
My solution, at present, is to create the 'default' widgets in the kv file, and to add the other ones from the py script through the addIngredient method. Below a minimal working version.
The kv file:
WMan:
AddWindow:
<AddWindow>:
name: 'add'
ingsGrid: ingsGrid
ing1: ing1
quant1: quant1
addIng: addIng
saveButton: saveButton
StackLayout:
id: ingsGrid
size_hint: .9, None
height: self.minimum_height
orientation: 'lr-tb'
spacing: '5sp', '5sp'
TextInput:
id: ing1
multiline: False
size_hint: .65, None
height: self.minimum_height
TextInput:
id: quant1
multiline: False
size_hint: .25, None
height: self.minimum_height
Button:
id: addIng
text: "+"
size_hint: .1, None
height: ing1.height
on_release: root.addIngredient(self)
Button:
id: saveButton
text: "Save"
size_hint: .3, None
on_release:
root.saveRec(self)
The py script reads:
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.stacklayout import StackLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class AddWindow(Screen):
def __init__(self, **kwargs):
super(AddWindow, self).__init__(**kwargs)
recipeName = ObjectProperty(None)
ingsGrid = ObjectProperty(None)
ing1 = ObjectProperty(None)
quant1 = ObjectProperty(None)
self.i = 1
self.ingsList = {}
self.ingsList[0] = ing1
self.quants = {}
self.quants[0] = quant1
def addIngredient(self, instance):
tmp_children_list = self.ingsGrid.children[:]
self.ingsGrid.clear_widgets()
# range(start,stop[, step])
for index in range(len(tmp_children_list)-1, -1, -1):
# the last item, then last-1, etc
child = tmp_children_list[index]
# add the last first (new ones will be added on top)
self.ingsGrid.add_widget(child)
# if child is the pressed button
if child == instance:
self.ing = TextInput(
size_hint=(.65, None),
height='30sp')
self.ingsGrid.add_widget(self.ing)
self.ingsList[self.i] = self.ing
self.quant = TextInput(
size_hint=(0.25, None),
height='30sp')
self.ingsGrid.add_widget(self.quant)
self.quants[self.i] = self.quant
self.i += 1
self.addNext = Button(
text="+",
size_hint=(0.1, None),
height='30sp')
self.addNext.bind(on_press=self.addIngredient)
self.ingsGrid.add_widget(self.addNext)
def saveRec(self, instance): # grab all inputs and send to SQLite db
print(self.ingsList)
print(self.ingsList[0].text)
print(self.ingsList[1].text)
class WMan(ScreenManager):
pass
kv = Builder.load_file("test.kv")
class TestApp(App):
def build(self):
return kv
if __name__ == "__main__":
TestApp().run()
My problem here is twofold: first, while this way of dinamically adding rows works as it should, it is to me a bit messy to have half of the layout defined on the kv file, and the other half defined in the py script. So my first question is:
1. Is there a way to move the entire layout to the kv file?
Second questions is:
2. How do I access the content of textInput 'ing1' (the one created in the kv file)?
when I run print(self.ingsList), I get:
{0: <ObjectProperty name=>, 1: <kivy.uix.textinput.TextInput object at 0x000002077FB89C88>}
So while I can easily do print(self.ingsList[1].text), running print(self.ingsList[0].text) will give error:
AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'text'
Here is a modified version of your code that does what I think you want:
from kivy.app import App
from kivy.properties import ObjectProperty, NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class AddWindow(Screen):
def __init__(self, **kwargs):
super(AddWindow, self).__init__(**kwargs)
recipeName = ObjectProperty(None)
ingsGrid = ObjectProperty(None)
ing1 = ObjectProperty(None)
quant1 = ObjectProperty(None)
self.i = 1
self.ingsList = {}
self.ingsList[0] = ing1
self.quants = {}
self.quants[0] = quant1
def saveRec(self, instance): # grab all inputs and send to SQLite db
for child in self.ids.ingsGrid.children:
if isinstance(child, OneRow):
print('ingedient name:', child.ids.ing1.text)
print('quantity:', child.ids.quant1.text)
print('')
class WMan(ScreenManager):
pass
class OneRow(BoxLayout):
inst_count = NumericProperty(0)
count = 0
def __init__(self, **kwargs):
OneRow.count += 1
self.inst_count = OneRow.count
super(OneRow, self).__init__(**kwargs)
def get_index(self):
par = self.parent
if par is None:
return None
index = 0
for index in range(len(par.children) - 1, -1, -1):
child = par.children[index]
if child == self:
return index
kv_str = '''
#:import Factory kivy.factory.Factory
WMan:
AddWindow:
id: add
#:set row_height 30
<OneRow>:
orientation: 'horizontal'
size_hint_y: None
height: row_height
TextInput:
id: ing1
multiline: False
size_hint: .65, None
height: row_height
TextInput:
id: quant1
multiline: False
text: str(root.inst_count)
size_hint: .25, None
height: row_height
Button:
id: addIng
text: "+"
size_hint: .1, None
height: row_height
on_release: app.root.ids.add.ids.ingsGrid.add_widget(Factory.OneRow(), index=root.get_index())
<AddWindow>:
name: 'add'
ingsGrid: ingsGrid
saveButton: saveButton
StackLayout:
id: ingsGrid
size_hint: .9, None
height: self.minimum_height
orientation: 'lr-tb'
spacing: '5sp', '5sp'
OneRow:
Button:
id: saveButton
text: "Save"
size_hint: .3, None
on_release:
root.saveRec(self)
'''
# kv = Builder.load_file("test.kv")
kv = Builder.load_string(kv_str)
class TestApp(App):
def build(self):
return kv
if __name__ == "__main__":
TestApp().run()
I used Builder.load_string() instead of load_file() just for my own convenience.
I have created a class named OneRow that holds a single row from the ingredients list, and the + Button now just adds an instance of that class. The get_index() method of that class is only used to position new ingredients below the row with the Button that was pressed. And the other code in that class is just to add some identifying info. If those things are not important to you, you can eliminate the OneRow class definition from the python, and just replace <OneRow>: in the kv with <OneRow#BoxLayout>: and where the OneRow is added you can just set index=1.
I'm trying to update one widget in through a button in another view at Kivy.
Kivy code:
<MainScreen>:
relatorios : relatorios
despesas : despesas
GridLayout:
size: root.width, root.height
cols : 1
Button:
id: relatorios
text: 'Relatorios'
on_press: root.change_screen(rep = 'Report')
Button:
id: despesas
text: 'Despesa'
on_press: root.change_screen(rep = 'Expend')
<ReportScreen>:
GridLayout:
size: root.width, root.height
cols : 1
Button:
id: semanal
text: 'Semanal'
Button:
id: mensal
text: 'Mensal'
Button:
id: anual
text: 'Anual'
Button:
id: voltar
text: 'Voltar'
on_press: root.change_screen(rep = 'Main')
<ExpendScreen>:
kind_selec1 : kind_select
GridLayout:
size: root.width, root.height
cols : 2
Label:
id: kind
text: 'Tipo de Despesa'
Button:
id: kind_select
text: 'Selecionar'
on_press: root.change_screen(rep = 'Kind')
Label:
id: way
text: 'Meio de Pagamento'
Button:
id: selec_way
text: 'Selecionar'
on_press: root.change_screen(rep = 'Way')
Label:
id: parcelas
text: 'Quantidade de Parcelas'
TextInput:
id: qtdy
Label:
id: value
text: 'Valor Total'
TextInput:
id: qtdy
Button:
id: back
text: 'Voltar'
on_press: root.change_screen(rep = 'Main')
Button:
id: submit
text: 'Registrar'
on_press: root.change_name()
<DropDown1>:
cols : 1
orientation: 'vertical'
<DropDown2>:
cols : 1
orientation: 'vertical'
Python code:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from functools import partial
from functions import Expense
from kivy.properties import ObjectProperty
import kivy
import time
class MainScreen(Widget):
relatorios = ObjectProperty(None)
despesas = ObjectProperty(None)
def change_screen(self,rep):
app_finan_con.screen_manager.current = rep
return
class ReportScreen(Widget):
def change_screen(self,rep):
app_finan_con.screen_manager.current = rep
return
class ExpendScreen(Widget):
kind_selec1 = ObjectProperty()
def __init__(self, **kwargs):
super(ExpendScreen, self).__init__(**kwargs)
def change_screen(self,rep):
app_finan_con.screen_manager.current = rep
def change_name(self):
self.kind_selec1.text= 'porra'
class Dropdown1(BoxLayout):
def __init__(self, **kwargs):
super(Dropdown1, self).__init__(**kwargs)
list = ['Alimentação', 'Transporte', 'Entretenimento','Moradia','Doação']
for item in list:
self.but = Button(text=item)
self.add_widget(self.but)
self.but.bind(on_release=partial(self.It,arg = item))
def It(self,instance,arg):
app_finan_con.screen_manager.current = 'Expend'
ExpendScreen.change_name(ExpendScreen)
class Dropdown2(BoxLayout):
def __init__(self, **kwargs):
super(Dropdown2, self).__init__(**kwargs)
list = ['Crédito', 'Débito', 'Dinheiro']
for item in list:
self.but = Button(text=item)
self.add_widget(self.but)
self.but.bind(on_release=partial(self.It, arg=item))
def It(self,instance,arg):
app_finan_con.screen_manager.current = 'Expend'
class MyApp(App):
def build(self):
self.screen_manager = ScreenManager()
self.mainscreen = MainScreen()
screen = Screen(name='Main')
screen.add_widget(self.mainscreen)
self.screen_manager.add_widget(screen)
self.reportscreen = ReportScreen()
screen = Screen(name='Report')
screen.add_widget(self.reportscreen)
self.screen_manager.add_widget(screen)
self.expendscreen = ExpendScreen()
screen = Screen(name='Expend')
screen.add_widget(self.expendscreen)
self.screen_manager.add_widget(screen)
self.dropdown1 = Dropdown1()
screen = Screen(name='Kind')
screen.add_widget(self.dropdown1)
self.screen_manager.add_widget(screen)
self.dropdown2 = Dropdown2()
screen = Screen(name='Way')
screen.add_widget(self.dropdown2)
self.screen_manager.add_widget(screen)
return self.screen_manager
def change_screen():
app_finan_con.screen_manager.current ='Report'
return
if __name__ == '__main__':
app_finan_con = MyApp()
app_finan_con.run()
The idea is to use screen Dropdown1 to modify screen ExpendScreen through this code:
class Dropdown1(BoxLayout):
def __init__(self, **kwargs):
super(Dropdown1, self).__init__(**kwargs)
list = ['Alimentação', 'Transporte', 'Entretenimento','Moradia','Doação']
for item in list:
self.but = Button(text=item)
self.add_widget(self.but)
self.but.bind(on_release=partial(self.It,arg = item)) (this line activates It method)
def It(self,instance,arg):
app_finan_con.screen_manager.current = 'Expend'
ExpendScreen.change_name(ExpendScreen) (this line activates method in ExpendScreen):
class ExpendScreen(Widget):
kind_selec1 = ObjectProperty()
def __init__(self, **kwargs):
super(ExpendScreen, self).__init__(**kwargs)
def change_screen(self,rep):
app_finan_con.screen_manager.current = rep
def change_name(self):
self.kind_selec1.text= 'porra'
but I get the following error: AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'text'
but when I call the same method from a button in the same screen (ExpendScreen) it works as expected, kivy code:
Button:
id: submit
text: 'Registrar'
on_press: root.change_name()
Image examples:
Can anyone help me?
Your line of code:
ExpendScreen.change_name(ExpendScreen)
is calling an instance method of ExpendScreen without an instance. That won't work. You need to get the instance of ExpendScreen that is part of your gui, and call its change_name method. I haven't tested this, but I think replacing the above line with:
app_finan_con.expendscreen.change_name()
will do what you want.
My target is like
I am choosing an option from the recycle view list from button 1, after choosing the option, then I am choosing from button 2,now the display must not show the choosed value in button 1(previously choosed option must be removed from recycle view 2). More over the button text must be updated to the choosed value text.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager,Screen,SlideTransition,FadeTransition
from kivy.uix.checkbox import CheckBox
from kivy.properties import ObjectProperty, NumericProperty, BooleanProperty,ListProperty,StringProperty
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.base import runTouchApp
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior
alph = ['a','b','c','d','e','f','g','h','i']
val1_chsd = None
val2_chsd = None
class Screen3(Screen):
#pass
labeltext = StringProperty('My selection')
def buttontype(self,types):
global buttonval
print("type called :",types)
if types is "button1":
buttonval = "selection1"
print ('value set 1',buttonval)
elif types is "button2":
buttonval = "selection2"
print ('value set 2',buttonval)
def setvalueselected(self,sel_value):
print("Selected value is",sel_value)
global val1_chsd
global val2_chsd
if buttonval is "selection1":
val1_chsd = sel_value
val1_name = sel_value
print("choosed no. 1",val1_name)
if buttonval is "selection2":
val2_chsd = sel_value
val2_name = sel_value
print("choosed no. 2",val2_name)
def printselected(self):
print("abcdef",val1_chsd,val2_chsd)
if val1_chsd != None and val2_chsd != None:
selected = val1_chsd + '\n' + val2_chsd
print ("choosed : ",selected)
self.labeltext = selected
else:
print ("Choose all values")
class Screen4(Screen):
list = ListProperty([])
class Screen5(Screen):
list = ListProperty([])
class ScreenManagement(ScreenManager):
pass
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class RVval1(RecycleView):
def __init__(self, **kwargs):
super(RVval1, self).__init__(**kwargs)
#print('removedval:', value2_chsd)
self.data = [{'text': str(x)} for x in alph]
class RVval2(RecycleView):
def __init__(self, **kwargs):
super(RVval2, self).__init__(**kwargs)
print('removedval:', val1_chsd)
self.data = [{'text': str(x)} for x in alph]
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
sc3 = Screen3()
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return Truej
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
if is_selected:
print("selection changed to {0}".format(rv.data[index]))
valueselected = format(rv.data[index])
valueselected = valueselected.replace("{'text': '","")
valueselected = valueselected.replace("'}","")
print("valueselected : ",valueselected)
self.sc3.setvalueselected(valueselected)
else:
print("selection removed for {0}".format(rv.data[index]))
presentation = Builder.load_file('dfive2.kv')
class DfiveZApp(App):
def build(self):
return presentation
if __name__ == '__main__':
DfiveZApp().run()
Kivy file
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
ScreenManagement:
id:screenmgr
Screen3:
id: screen_3
name : "screen3"
manager: screenmgr
Screen4:
id: rv_screen_val1
name : "screen4"
Screen5:
id: rv_screen_val2
name : "screen5"
<Screen3>:
BoxLayout:
orientation: "vertical"
BoxLayout:
orientation: "vertical"
#size_hint_y: 1
GridLayout:
cols: 2
size_hint_y: 2
Label:
text:"Value1"
Button:
id: val1_name
text: 'val1'
on_press:
root.buttontype('button1')
app.root.transition = SlideTransition(direction='left')
app.root.current = 'screen4'
Label:
text:"Value2"
Button:
id: val2_name
text: 'val2'
on_press:
root.buttontype('button2')
app.root.transition = SlideTransition(direction='left')
app.root.current = 'screen5'
BoxLayout:
Label:
text:root.labeltext
Button:
text: 'Submit'
on_press:
root.printselected()
#app.root.transition = SlideTransition(direction='left')
<SelectableLabel>:
# 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
<RVval1>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
touch_multiselect: False
<RVval2>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
touch_multiselect: False
<Screen4>:
BoxLayout:
list: rv_list_val1
orientation: "vertical"
RVval1:
id: rv_list_val1
Button:
text: 'Previous screen'
size_hint: None, None
size: 150, 50
#on_press: root.SetText()
on_release:
#root.manager.current = root.manager.previous()
app.root.transition = SlideTransition(direction='right')
app.root.current = 'screen3'
<Screen5>:
BoxLayout:
list: rv_list_val2
orientation: "vertical"
RVval2:
id: rv_list_val2
Button:
text: 'Previous screen'
size_hint: None, None
size: 150, 50
#on_press: root.SetText()
on_release:
#root.manager.current = root.manager.previous()
app.root.transition = SlideTransition(direction='right')
app.root.current = 'screen3'
You can create a method to reload the data on the second step and set a string property on the view.
.py file
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager,Screen,SlideTransition,FadeTransition
from kivy.uix.checkbox import CheckBox
from kivy.properties import ObjectProperty, NumericProperty, BooleanProperty,ListProperty,StringProperty
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.base import runTouchApp
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior
alph = ['a','b','c','d','e','f','g','h','i']
sel_values1 = []
val1_chsd = None
val2_chsd = None
class Screen3(Screen):
#pass
labeltext = StringProperty('My selection')
val1 = StringProperty('val1')
val2 = StringProperty('val2')
def buttontype(self,types):
global buttonval
print("type called :",types)
if types is "button1":
buttonval = "selection1"
print ('value set 1',buttonval)
elif types is "button2":
buttonval = "selection2"
print ('value set 2',buttonval)
def setvalueselected(self,sel_value):
print("Selected value is",sel_value)
global val1_chsd
global val2_chsd
if buttonval is "selection1":
val1_chsd = sel_value
val1_name = sel_value
print("choosed no. 1",val1_name)
self.val1 = val1_name
if buttonval is "selection2":
val2_chsd = sel_value
val2_name = sel_value
print("choosed no. 2",val2_name)
self.val2 = val2_name
def printselected(self):
print("abcdef",val1_chsd,val2_chsd)
if val1_chsd != None and val2_chsd != None:
selected = val1_chsd + '\n' + val2_chsd
print ("choosed : ",selected)
self.labeltext = selected
else:
print ("Choose all values")
class Screen4(Screen):
list = ListProperty([])
class Screen5(Screen):
list = ListProperty([])
class ScreenManagement(ScreenManager):
pass
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout):
''' Adds selection and focus behaviour to the view. '''
class RVval1(RecycleView):
def __init__(self, **kwargs):
super(RVval1, self).__init__(**kwargs)
#print('removedval:', value2_chsd)
self.data = [{'text': str(x)} for x in alph]
class RVval2(RecycleView):
def __init__(self, **kwargs):
super(RVval2, self).__init__(**kwargs)
print('removedval:', val1_chsd)
self.data = [{'text': str(x)} for x in alph]
def reload_data(self):
self.data = [{'text': str(x)} for x in alph if x not in sel_values1]
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
sc3 = Screen3()
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return Truej
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
if is_selected:
print("selection changed to {0}".format(rv.data[index]))
valueselected = format(rv.data[index])
valueselected = valueselected.replace("{'text': '","")
valueselected = valueselected.replace("'}","")
print("valueselected : ",valueselected)
print(hasattr(rv, "RVval1"))
if (rv.name == "RVval1"):
sel_values1.append(valueselected)
app = App.get_running_app()
app.root.ids['screen_3'].setvalueselected(valueselected)
app.root.ids['rv_screen_val2'].ids['rv_list_val2'].reload_data()
else:
print("selection removed for {0}".format(rv.data[index-1]))
presentation = Builder.load_file('dfive2.kv')
class DfiveZApp(App):
def build(self):
return presentation
if __name__ == '__main__':
DfiveZApp().run()
.kv file
#:import SlideTransition kivy.uix.screenmanager.SlideTransition
ScreenManagement:
id:screenmgr
Screen3:
id: screen_3
name : "screen3"
manager: screenmgr
Screen4:
id: rv_screen_val1
name : "screen4"
Screen5:
id: rv_screen_val2
name : "screen5"
<Screen3>:
BoxLayout:
orientation: "vertical"
BoxLayout:
orientation: "vertical"
#size_hint_y: 1
GridLayout:
cols: 2
size_hint_y: 2
Label:
text:"Value1"
Button:
id: val1_name
text: root.val1
on_press:
root.buttontype('button1')
app.root.transition = SlideTransition(direction='left')
app.root.current = 'screen4'
Label:
text:"Value2"
Button:
id: val2_name
text: root.val2
on_press:
root.buttontype('button2')
app.root.transition = SlideTransition(direction='left')
app.root.current = 'screen5'
BoxLayout:
Label:
text:root.labeltext
Button:
text: 'Submit'
on_press:
root.printselected()
#app.root.transition = SlideTransition(direction='left')
<SelectableLabel>:
# 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
<RVval1>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
touch_multiselect: False
<RVval2>:
viewclass: 'SelectableLabel'
SelectableRecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: False
touch_multiselect: False
<Screen4>:
BoxLayout:
list: rv_list_val1
orientation: "vertical"
RVval1:
id: rv_list_val1
name: 'RVval1'
Button:
text: 'Previous screen'
size_hint: None, None
size: 150, 50
#on_press: root.SetText()
on_release:
#root.manager.current = root.manager.previous()
app.root.transition = SlideTransition(direction='right')
app.root.current = 'screen3'
<Screen5>:
BoxLayout:
list: rv_list_val2
orientation: "vertical"
RVval2:
id: rv_list_val2
name: 'RVval2'
Button:
text: 'Previous screen'
size_hint: None, None
size: 150, 50
#on_press: root.SetText()
on_release:
#root.manager.current = root.manager.previous()
app.root.transition = SlideTransition(direction='right')
app.root.current = 'screen3'