I wrote a simple code to create a timer, and for this I added two buttons: Start and Stop. The idea is that when one of the buttons is enabled the other isn't, and viceversa.
The code below works, but I want to get the same behaviour using the kv file.
def update_time(self, instance):
timer = Clock.schedule_interval(self.timer, 1.0 / 1.0)
instance.disabled = True
self.stop_button.disabled = False
def stop_updating(self, instance):
Clock.unschedule(self.timer)
instance.disabled = True
self.timer_button.disabled = False
If I try to use the code snippet below, the script throws an error because it is lacking one argument (instance).
Button:
text: 'Start'
on_press: root.update_time()
I know the "disabled" property in kv file exists, but I don't know how (or whether) to use it to modify the two buttons I want to be modified, onclick.
Any idea how to go about it?
All script code
class LandingScreen(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def go_to_timer(self):
clock_app.screen_manager.current = 'Timer'
class TimerScreen(GridLayout):
current_time = StringProperty()
timer_start = ObjectProperty(Button)
timer_stop = ObjectProperty(Button)
tot_sec = 0
def __init__(self, **kwargs): # Widgets without .kv file
super().__init__(**kwargs)
self.rows = 4
self.current_time = '00:00'
def update_time(self, instance):
timer = Clock.schedule_interval(self.timer, 1.0 / 1.0)
instance.disabled = True
# self.stop_button.disabled = False
self.timer_stop.disabled = False
def stop_updating(self, instance):
Clock.unschedule(self.timer)
instance.disabled = True
# self.timer_button.disabled = False
self.timer_start.disabled = False
def timer(self, instance):
# Code
class ClockApp(App):
def build(self):
self.screen_manager = ScreenManager()
# Landing screen
self.landing_screen = LandingScreen()
screen = Screen(name='Landing')
screen.add_widget(self.landing_screen)
self.screen_manager.add_widget(screen)
# Timer screen
self.timer_screen = TimerScreen()
screen = Screen(name='Timer')
screen.add_widget(self.timer_screen)
self.screen_manager.add_widget(screen)
return self.screen_manager
def go_to_landing(self):
clock_app.screen_manager.current = 'Landing'
if __name__ == '__main__':
clock_app = ClockApp()
clock_app.run()
kv file
<TimerScreen>:
rows: 5
Label:
font_size: 60
text: root.current_time
GridLayout:
cols: 3
Label:
text: ''
Button:
id: 'timer_start'
text: 'Start'
on_press: root.update_time(self)
Label:
text: ''
Label:
text: ''
Button:
id: 'timer_stop'
text: 'Stop'
on_press: root.stop_updating(self)
disabled: True
Label:
text: ''
Label:
text: ''
Label:
text: ''
Label:
text: ''
AnchorLayout:
anchor_x: 'left'
Button:
text: 'Back'
width: root.width / 7
size_hint_x: None
on_press: app.go_to_landing()
<LandingScreen>:
rows: 7
Label:
font_size: 50
text: 'Clock types'
canvas.before:
Color:
rgba: 1, 0, 0, 1
GridLayout:
cols:3
Label:
text: ''
Button:
text: 'Timer'
on_press: root.go_to_timer()
Label:
text: ''
Label:
text: ''
Button:
text: 'Countdown'
on_press: root.go_to_countdown()
Label:
text: ''
Label:
text: ''
Label:
text: ''
I think your code is mostly working. Just a few notes on the use of the ids:
You should not use a string literal as an id. Instead of id: 'timer_start', use id: timer_start. If you use a string literal, you will encounter problems if you try to reference it elsewhere in the kv (it will be interpreted as a string, not an id).
When declaring an ObjectProperty in a class that will be defined by an id in the kv, use ObjectProperty(None) in the declaration in the class.
Use declarations in the kv to match up with the declarations in the class.
Applying the above to your python code:
class TimerScreen(GridLayout):
current_time = StringProperty()
timer_start = ObjectProperty(None)
timer_stop = ObjectProperty(None)
tot_sec = 0
and in the corresponding kv:
<TimerScreen>:
timer_start: timer_start
timer_stop: timer_stop
rows: 5
and the ids should be changed to:
Button:
id: timer_start
text: 'Start'
on_press: root.update_time(self)
and:
Button:
id: timer_stop
text: 'Stop'
on_press: root.stop_updating(self)
disabled: True
The typical declaration of an ObjectProperty in kv like:
timer_start: timer_start
can be confusing, but that is how it is usually done. I prefer to use a different name for the ObjectProperty, to make it clearer. The first name in the above is the name of the ObjectProperty, and the second is the id. They just happen to have the same name.
Related
I have an problem with the Kivy-Libaray. I' m currently writing the front end, for a ShoppinglistApp. I have an main menu, where you can select what you want to do(add List/ Shop) from where you can get to the Shop selection.
In the shop selection Menu are 4 Shops.
all Items(Produkt) are stored in a list.
If you select the Shop, then for each Item(from this Shop) a Button should be created.
I tired this solution:
from kivy.app import App
from kivy.metrics import dp
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.stacklayout import StackLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
# the rest
class Produkt:
def __init__(self, name, amount, market):
self.name = name
self.amount = amount
self.market = market
eklist = []
for i in range(0, 100):
nn = "Banane" + str(i)
eklist.append(Produkt(nn, 0, "Baumarkt"))
# Define Screens
class FirstWindow(Screen):
pass
class SecondWindow(Screen):
pass
class ThirdWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
"""Anfang der Chose"""
class Box(BoxLayout):
pass
# Gvar is a class I created for the purpose of transporting variable(s). Especially the selected shop.
class Gvar:
def __init__(self, var1):
self.var1 = var1
def get(self):
return self.var1
gvar = Gvar("default")
# SetBox ist the Class for changing the variable through the shop selection menu.
class SetBox(BoxLayout):
def set(self, market):
gvar.var1 = market
print("Set")
print(gvar.var1)
class BLE(BoxLayout):
pass
# This is the Menus which makes Problems.
class SLE(StackLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
for p in eklist:
# In theory, this if request should always be triggerd. In praxis, I made the else Request to debug.
# for Text normally would appear p.name, but I changed it for debugging. Text shown(gvar.var1) is "default".
if p.market == str(gvar.get()):
b = Button(text=str(gvar.get()), size_hint=(None, None), color="blue", size=(dp(200), dp(100)),
on_press=lambda *args: self.test1(*args, str(p.name)))
b.prod = p
self.add_widget(b)
else:
b = Button(text=str(gvar.get()), size_hint=(None, None), color="green", size=(dp(200), dp(100)),
on_press=lambda *args: self.test1(*args, str(p.name)))
b.prod = p
self.add_widget(b)
market = None
# test 1 is a placeholder for a Backend funktion
# Here I print the Produkt and gvar.var1, to debug. Here it get for gvar.var1 the in the GUI selected Value.
def test1(self, b, i):
print("Initialize test2, please wait...")
print(b.prod.name, b.prod.amount, b.prod.market, gvar.var1)
"""Ende der Chose"""
kv = Builder.load_file('einkaufsliste.kv')
class EinkaufsApp(App):
def build(self):
return kv
if __name__ == '__main__':
EinkaufsApp().run()
+
Here is the Kivy file.
WindowManager:
FirstWindow:
SecondWindow:
ThirdWindow:
<FirstWindow>:
name: "first"
Box:
orientation: "vertical"
size :root.width, root.height
Label:
text: "Hauptmenü"
size_hint: 1, .2
font_size: 32
Button:
text: "Einkaufen"
font_size: 32
background_normal: ' '
background_color: "#666e00"
on_release:
app.root.current = "third"
root.manager.transition.direction = "left"
Button:
text: "Einkaufsliste updaten"
font_size: 32
background_normal: ' '
background_color: "#660000"
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
<SecondWindow>:
name: "second"
BLE:
id: BLE
orientation: "vertical"
Button:
text: "to first"
size_hint: 1, .1
font_size: 24
on_release:
app.root.current = "first"
root.manager.transition.direction = "right"
TextInput:
multiline: False
size_hint: 1, .1
SVE:
<ThirdWindow>:
name: "third"
SetBox:
orientation: "vertical"
size :root.width, root.height
id:SetBox
Box:
orientation: "horizontal"
size_hint: 1, .4
Button:
text: "to first"
size_hint: .2, 1
font_size: 24
on_release:
app.root.current = "first"
root.manager.transition.direction = "right"
Label:
text: "Markt auswählen"
size_hint: .8, 1
font_size: 32
Button:
text: "Supermarkt"
font_size: 32
background_normal: ' '
background_color: "#a1b2e3"
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
on_press: SetBox.set("Supermarkt")
Button:
text: "Drogerie"
font_size: 32
background_normal: ' '
background_color: "#c7bbc9"
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
on_press: SetBox.set("Drogerie")
Button:
text: "Baumarkt"
font_size: 32
background_normal: ' '
background_color: "#b4eeb4"
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
on_press: SetBox.set("Baumarkt")
Button:
text: "Elektrogerätefachhandel"
font_size: 32
background_normal: ' '
background_color: "#f4c2c2"
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
on_press: SetBox.set("Elektrogerätefachhandel")
<SVE#ScrollView>:
SLE:
id: Scrollwin
size_hint: 1, None
height: self.minimum_height
<SLE>:
# orientation: "rl-bt"
# spacing: "1dp", "1dp"
</code></pre>
I think the Problem is, that kivy creates the whole GUI before I can change the variable.
Is there a way to reload the Screen or do I have to make for each market/shop another Screen?
It's possible to do what you want. You have to add the items to the corresponidng layout. I'll give you an example with a project I did some time ago:
class ExmapleApp(App):
def build(self):
#Create GridLayout where you will add and remove widgets
self.gridRooms = gridRooms = GridLayout(cols=10, padding=10, spacing=10,
row_force_default=True, row_default_height=50, size_hint_y=None)
gridRooms.bind(minimum_height=gridRooms.setter('height'))
# Generate 10 Rooms items
for i in range(10):
labl = NewLabel() #Instace of NewLabel
labl.num = str(i+1) #Number of room
labl.roomName = "Room "+str(labl.num) # Room name
labl.filename = '12916_sweet_trip_mm_kwik_mod_01.wav'
roomsList.append(labl) #This add the room to a list for further reference
gridRooms.add_widget(labl) #This add the rooms to a grid
Here's an image of how it looks so far:
Finally, you can add widgets to that GridLayout:
#Create the object you want to Add
labl = NewLabel() #Instace of NewLabel
labl.num = str(11) #Number of room
labl.roomName = "Room "+str(labl.num) # Room name
labl.filename = '12916_sweet_trip_mm_kwik_mod_01.wav'
roomsList.append(labl) #This add the room to a list for further reference
#This add the widgets 3 secs after the program starts
Clock.schedule_once(lambda x: gridRooms.add_widget(roomsList[10]), 3) #Add room 11
You can even have a callback to create a new widget everytime you press a button.
To remove a widget just:
gridRooms.remove_widget(roomsList[10]) #Removes Room 11
I am currently working on creating a pretty basic fitness app in Python using Kivy. It's been going pretty smoothly but I recently ran into a problem while trying to implement a dropdown menu into one of my screens. I am trying to put 3 different dropdown menus into one screen which will return certain values that I will later use for the main function of the app (which will be to generate a daily fitness routine). The problem is, each dropdown menu has the same option. For example, two of the dropdown menus I want to use are 'time availability'(30 mins, 60 mins...120 mins) and 'Fitness Level' (scale of 1-3). But each dropdown menu ends up having the same contents as whichever one I could first (such as time availability 30 mins, 60mins...120 mins and then the same for the contents inside of fitness level.)
Does anyone have any suggestions for how I can keep each dropdown menu unique within the same screen? The code for my 2 files (one .py and one .kv) are attached. In the code I have below, I removed the contents of the dropdown menus so they are basically empty buttons.
.py file:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.base import runTouchApp
import webbrowser
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import ObjectProperty
######################################################################
class KivyTutorRoot(BoxLayout):
def __init__(self, **kwargs):
super(KivyTutorRoot, self).__init__(**kwargs)
#list of previous screens
self.screen_list = []
def changeScreen(self, next_screen):
operations = "Get Fit, Create User".split(',')
question = None
#if screen is not already in the list of previous screens...
if self.ids.kivy_screen_manager.current not in self.screen_list:
self.screen_list.append(self.ids.kivy_screen_manager.current)
if next_screen == 'about this app':
self.ids.kivy_screen_manager.current = "about_screen"
elif next_screen == 'get fit':
self.ids.kivy_screen_manager.current = "getFitScreen"
def onBackBtn(self):
#check if there are any screens to go back to
if self.screen_list:
#if there are screens we can go back to. Then go back to that screen
self.ids.kivy_screen_manager.current = self.screen_list.pop()
#the pop() will return the last item from the list, aka the last screen we visited
#say we don't want to close
return True
#no more screens to go back to, so we close
return False
###############################################################################
#dropdown menu classes here:
class CustomDropDownTime(DropDown):
pass
class CustomDropDownGym(DropDown):
pass
##############################################################################
#This will be a screen for all of the fitness functions
class getFitScreen(Screen):
top_layout = ObjectProperty(None)
dd_btn = ObjectProperty(None)
top_layout2 = ObjectProperty(None)
dd_btn2 = ObjectProperty(None)
def __init__(self,*args,**kwargs):
super(getFitScreen, self).__init__(*args, **kwargs)
#everything undere this is new code from stackover flow and it works for one. Stops working at GYM
self.drop_down = CustomDropDownTime()
dropdown = DropDown()
#time availability dropdown
time = ['15-30mins', '30-60mins', '60-90mins','90-120mins']
for times in time:
btn = Button(text='%r' %times, size_hint_y=None, height=30)
btn.bind(on_release=lambda btn: dropdown.select(btn.text))
dropdown.add_widget(btn)
mainbutton = Button(text='Time Available', size_hint=(1, 1))
mainbutton.bind(on_release=dropdown.open)
dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
###############################################################################
#This will be a screen for the charts
class graphScreen(Screen):
def __init__(self,**kwargs):
super(graphScreen, self).__init__(**kwargs)
################################################################################
class KivyTutorApp(App):
def __init__(self, **kwargs):
super(KivyTutorApp, self).__init__(**kwargs)
Window.bind(on_keyboard=self.onBackBtn)
def onBackBtn(self, window,key,*args):
#if user presses back button
#27 is the numerical code for back button
if key == 27:
return self.root.onBackBtn()
def build(self):
return KivyTutorRoot()
# this next part is so that we can 'get text' from this .py file when running from our .kv file
def getText(self):
# you need markup: True to use references like these
return ("Hey there! \nThis App was built using "
"[b][ref=kivy]kivy[/ref][/b]\n"
"Feel free to look at the source code "
"[b][ref=sour"
"ce]here[/ref][/b].\n"
"This app is under the [b][ref=mit]MIT License[/ref][/b]\n"
"Me: [b][ref=website]#kevin_adrian95[/ref][/b]")
# this next part is going to make the actual references
def on_ref_press(self, instance, ref):
dict = {
"source": "https://github.com/gopar/Kivy-Tutor",
# youre going to want to change this to your own github when you finish.
"website": "https://www.instagram.com/kevin_adrian95/",
"kivy": "https://kivy.org/#home",
"mit": "https://github.com/gopar/kivy-Tutor/blob/master/LICENSE"
}
webbrowser.open(dict[ref])
KivyTutorApp().run()
.kv file:
<WrappedLabel#Label>:
size_hint_y: None
height: self.texture_size[1]+(self.texture_size[1]/2)
markup: True
<CustomDropDownTime>:
Button:
text: '15-30 mins'
size_hint_y: None
height: 44
on_release: root.select('15-30mins')
Button:
text: '30-60 mins'
size_hint_y: None
height: 44
on_release: root.select('30-60min')
Button:
text: '60-90 mins'
size_hint_y: None
height: 44
on_release: root.select('60-90mins')
Button:
text: '90-120 mins'
size_hint_y: None
height: 44
on_release: root.select('90-120mins')
<CustomDropDownGym>:
Button:
text: 'Yes'
size_hint_y: None
height: 44
on_release: root.select('Yes')
Button:
text: 'No'
size_hint_y: None
height: 44
on_release: root.select('No')
< KivyTutorRoot >:
orientation: "vertical"
ActionBar:
ActionView:
ActionPrevious:
title: 'Kevin Adrian'
with_previous: False
ActionOverflow:
ActionButton:
text: "Settings"
on_press: app.open_settings()
ScreenManager:
id: kivy_screen_manager
StartScreen:
name: "start_screen"
AboutScreen:
id: about_screen
name: "about_screen"
getFitScreen:
id: getFitScreen
name: "getFitScreen"
<StartScreen#Screen>:
BoxLayout:
#settings
orientation: "vertical"
padding: root.width * .2, root.height*.1
spacing: min(root.width, root.height)*.1
WrappedLabel:
text: "[b] Kevin Adrian [/b]"
font_size: min(root.height, root.width) /10
Button:
text: "Get Fit"
font_size: 35
on_release: app.root.changeScreen(self.text.lower())
Button:
text: "Create User"
font_size: 20
Button:
text: "About this app"
on_release: app.root.changeScreen(self.text.lower())
<AboutScreen#Screen>:
BoxLayout:
padding: root.width * .02, root.height*.02
Label:
text: app.getText()
halign: "center"
markup: True
font_size: root.height / 20
text_size: self.width, None
center_y: .5
on_ref_press: app.on_ref_press(*args)
<getFitScreen>:
id: getFitScreen
top_layout: topLayoutID
dd_btn: btn_ddID
BoxLayout:
id: topLayoutID
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .95}
Button:
id: btn_ddID
text: 'Time Availablity'
on_release: root.drop_down.open(self)
Button:
id: btn_ddID2
text: 'Gym Access'
on_release: root.drop_down.open(self)
Button:
text: 'Training Level'
on_release: root.drop_down.open(self)
class getFitScreen(Screen):
def __init__(self, *args, **kwargs):
super(getFitScreen, self).__init__(*args, **kwargs)
self.dropdown = DropDown()
self.dropdown1 = DropDown()
self.dropdown2= DropDown()
time = ['15-30mins', '30-60mins', '60-90mins', '90-120mins']
level = [1, 2, 3]
access = [True, False]
for times in time:
btn = Button(text='%r' % times, size_hint_y=None, height=30)
btn.bind(on_release=lambda btn: self.dropdown.select(btn.text))
self.dropdown.add_widget(btn)
mainbutton = Button(text='Time Available', size_hint=(1, 1))
mainbutton.bind(on_release=self.dropdown.open)
self.dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
for levels in level:
btn1 = Button(text='%r' % levels, size_hint_y=None, height=30)
btn1.bind(on_release=lambda btn1: self.dropdown1.select(btn1.text))
self.dropdown1.add_widget(btn1)
mainbutton1 = Button(text='Training Level', size_hint=(1, 1))
mainbutton1.bind(on_release=self.dropdown1.open)
self.dropdown1.bind(on_select=lambda instance, x: setattr(mainbutton1, 'text', x))
for bool in access:
btn2 = Button(text = '%r' % bool, size_hint_y=None,height=30)
btn2.bind(on_release = lambda btn2 : self.dropdown2.select(btn2.text))
self.dropdown2.add_widget(btn2)
mainbutton2 = Button(text = 'Gym Access',size_hint=(1,1))
mainbutton2.bind(on_release=self.dropdown2.open)
self.dropdown2.bind(on_select=lambda instance, x: setattr(mainbutton2,'text', x))
So this is the change to the .py file above. I left out the rest of the code, just the code for the screen that I am trying to put the dropdown menus into.
<getFitScreen>:
id: getFitScreen
top_layout: topLayoutID
dd_btn: btn_ddID
BoxLayout:
id: topLayoutID
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .95}
Button:
id: btn_ddID
text: 'Time Availablity'
on_release: root.dropdown.open(self)
Button:
id: btn2_ddID2
text: 'Training Level'
on_release: root.dropdown1.open(self)
Button:
id: btn3_ddID3
text: 'Gym Access'
on_release: root.dropdown2.open(self)
Then the only change I made to the .kv file was at the very end, corresponding to the screen I wanted to make adjustments to. Basically I just had to change a couple variable names.
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 made an input dialog box in kivy. But I'm not sure if this is a correct format or correct code or correct way to code. I tried to display an error message itself in the popup below the button. It works while the input is empty, after input, also it's showing the error.
Can anyone help me to do it in a correct way?
Builder.load_string("""
<Main>:
Button:
text: 'Click'
on_press: root.callit()
<design>:
orientation: 'vertical'
cols:2
pos: self.pos
size: root.size
Label:
text: 'Enter Value'
TextInput:
id: val
multiline: False
hint_text:'Age'
input_filter: 'int'
Button:
text: 'Enter'
background_color: 255,0,0,0.9
on_press: root.enter()
Button:
text: 'Cancel'
background_color: 0,1,255,0.7
on_press: root.cancel()
#Label:
# id: er
# foreground_color: [1,250,100,1]
# text: 'Error'
""")
class design(GridLayout):
def __init__(self,**kwargs):
super(design,self).__init__(**kwargs)
#cancel=ObjectProperty(None)
self.er=Label(text='Please enter some input!')
def enter(self):
if self.ids.val.text=='':
print "Enter text"
#self.add_widget(self.er)#Label(text='Please enter some input!',width=200))
#time.sleep(10000)
#self.remove_widget(self.er)
#self.ids.er.text='Please enter some input!'
#self.ids.val.hint_text='Please enter input!'
else:
print self.ids.val.text
#self.ids.val.hint_text='Please enter input!'
self.ids.val.text=''
self.cancel()
def cancel(self):
obj=OpenDialog()
obj.call_pops(1)
class Main(BoxLayout):
pass
def callit(self):
obj=OpenDialog()
obj.call_pops(0)
class OpenDialog(Popup):
global popup
cont=design()
popup=Popup(title='InputDialog',content=cont,size_hint=(None, None), size=(400, 150),auto_dismiss=False)
def call_pops(self,val):
if val==1:
self.close_pops()
elif val==0:
popup.open()
def close_pops(self):
popup.dismiss()
class SriPop(App):
def build(self):
return Main()
if __name__ == '__main__':
SriPop().run()
You need to use the on_text event so that when a number is entered the label stops showing the error. For example:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.properties import NumericProperty, StringProperty, ObjectProperty
from kivy.lang import Builder
Builder.load_string("""
<Main>:
BoxLayout:
orientation: "vertical"
Button:
text: 'Click'
on_press: root.callit()
Label:
id: age_lab
size_hint_y: 0.1
text: root.str_age
<OpenDialog>:
title: 'InputDialog'
size_hint: None, None
size: 400, 120
auto_dismiss: False
text: input.text
lb_error: er
BoxLayout:
orientation: 'vertical'
pos: self.pos
size: root.size
BoxLayout:
orientation: 'horizontal'
Label:
text: 'Enter Value'
TextInput:
id: input
multiline: False
hint_text:'Age'
input_filter: 'int'
on_text: root.error = ''
BoxLayout:
orientation: 'horizontal'
Button:
text: 'Enter'
background_color: 255,0,0,0.9
on_press: root._enter()
Button:
text: 'Cancel'
background_color: 0,1,255,0.7
on_press: root._cancel()
Label:
id: er
foreground_color: 1, 250, 100, 1
color: 1, 0.67, 0, 1
size_hint_y: None
height: 0
text: root.error
""")
class Main(BoxLayout):
age = NumericProperty()
str_age = StringProperty("None")
def callit(self):
obj = OpenDialog(self)
obj.open()
def on_age(self, *args):
self.str_age = "Age: {}".format(self.age)
class OpenDialog(Popup):
_age = NumericProperty()
error = StringProperty()
def __init__(self, parent, *args):
super(OpenDialog, self).__init__(*args)
self.parent = parent
self.bind(_age=self.parent.setter('age'))
def on_error(self, inst, text):
if text:
self.lb_error.size_hint_y = 1
self.size = (400, 150)
else:
self.lb_error.size_hint_y = None
self.lb_error.height = 0
self.size = (400, 120)
def _enter(self):
if not self.text:
self.error = "Error: enter age"
else:
self._age = int(self.text)
self.dismiss()
def _cancel(self):
self.dismiss()
class SriPop(App):
def build(self):
return Main()
if __name__ == '__main__':
SriPop().run()
When I click on Account(root.display_account()) then call display_account().After that RVACCOUNT() function call .After that when i click on +Add Account then def add_account(self): call
I have a class AccountPopup which define a attribute state_text and assign value text:'Testing' in .kv file
How to get value of state_text 'Testing' and pass in on_text: root.filter(self.text,state_text) and print in def filter function.
test.py
class AccountPopup(Popup):
state_text = ObjectProperty(None)
popupAccountCity = ObjectProperty(None)
def display_cities_treeview_account(self, instance):
if len(instance.text) > 0:
#if self.popupAccountCity is None:
self.popupAccountCity = TreeviewCityAccount(self.state_text.text)
self.popupAccountCity.filter(instance.text,self.state_text.text)
self.popupAccountCity.open()
class TreeviewCityAccount(Popup):
state_text = ObjectProperty(None)
def __init__(self,state_text, **kwargs):
print(state_text)
def filter(self, f,state):
print(state)
class RVACCOUNT(BoxLayout):
def add_account(self):
self.mode = "Add"
popup = AccountPopup(self)
popup.open()
class MainMenu(BoxLayout):
def display_account(self):
self.dropdown.dismiss()
self.remove_widgets()
self.rvaccount = RVACCOUNT()
self.content_area.add_widget(self.rvaccount)
class FactApp(App):
title = "Test"
def build(self):
self.root = Builder.load_file('test.kv')
return MainMenu()
if __name__ == '__main__':
FactApp().run()
test.kv
<AccountPopup>:
state_text:state_text
TextInput:
id:state_text
text:'Testing'
<TreeviewCityAccount>:
BoxLayout
orientation: "vertical"
TextInput:
id: treeview
size_hint_y: .1
on_text: root.filter(self.text,state_text)
<RVACCOUNT>:
BoxLayout:
orientation: "vertical"
Button:
size_hint: .07, .03
text: "+Add Account"
on_press: root.add_account()
<MainMenu>:
content_area: content_area
dropdown: dropdown
BoxLayout:
orientation: 'vertical'
#spacing : 10
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
MenuButton:
id: btn
text: 'Master'
size : (60,30)
on_release: dropdown.open(self)
CustDrop:
DropdownButton:
text: 'Account'
size_hint_y: None
height: '32dp'
on_release: root.display_account()
Can someone help me?
You should reference it as self.state_text everywhere, also make it a StringProperty in the py file and can than access it as
on_text: root.filter(self.text,root.state_text)
root in kv refers to the most left widget aka <TreeviewCityAccount>: in your case.
See https://kivy.org/docs/api-kivy.lang.html
Alternatively you can work with ids in the kv file.
the value you are looking for is not in your immediate root which is why this is not working.The say thing to do is get the full path to that property like so:
Snippet:
<AccountPopup>:
id: ac_popup
#bunch of code
<TreeviewCityAccount>:
#chunk of code
TextInput:
id: tree view
on_text:root.filter(self.text,app.ac_popup.state_text
Also,generally,it's a good idea to id your classes mate
Disclaimer:code not tested