So I am building a general knowledge quiz game, and upon startup of the app, the player's score (that is kept in a .txt file) should be displayed in a label.
I've tried to use the on_start() function for this, but I can't seem to access the ids of the 'score' label. Here is the line of my code that gives the error:
self.root.get_screen("home_screen").ids.score.text = str(playerScore)
I receive the following error:
AttributeError: 'super' object has no attribute '__getattr__'
Here is my full code:
#main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file('design.kv')
class RootWidget(ScreenManager):
pass
class HomeScreen(Screen):
pass
class MainApp(App):
def build(self):
return RootWidget()
def on_start(self):
with open("score.txt", "r") as f:
playerScore = f.readline()
self.root.get_screen("home_screen").ids.score.text = str(playerScore)
if __name__ == "__main__":
MainApp().run()
and
#design.kv
<HomeScreen>:
GridLayout:
cols: 1
GridLayout:
cols: 2
Button:
id: infoButton
text: "Wiki"
Label:
id: score
GridLayout:
cols: 1
Label:
id: Question
text: "Question"
Button:
id: Button1
text: "Option 1"
Button:
id: Button1
text: "Option 2"
Button:
id: Button1
text: "Option 3"
Button:
id: Button1
text: "Option 4"
<RootWidget>:
HomeScreen:
name: "home_screen"
and also a 'score.txt' file with only - '100' inside.
Thank you.
Your approach was right. I am not sure what you want your layout to look like, but you made two mistakes:
Your .kv code was missing indentations after your class definitions
Even with indentations, you placed three GridLayouts on top of each other. One approach would be to put these three GridLayouts inside a BoxLayout, or just structurize your GridLayout in another way (e.g. pack all widgets inside one GridLayout). Basically you had your "Wiki" and "100" (score) button already, but they just were behind your Option buttons so you could barely see them.
#design.kv
<HomeScreen>:
BoxLayout:
GridLayout:
cols: 1
GridLayout:
cols: 2
Button:
id: infoButton
text: "Wiki"
Label:
id: score
GridLayout:
cols: 1
Label:
id: Question
text: "Question"
Button:
id: Button1
text: "Option 1"
Button:
id: Button1
text: "Option 2"
Button:
id: Button1
text: "Option 3"
Button:
id: Button1
text: "Option 4"
<RootWidget>:
HomeScreen:
name: "home_screen"
Related
I am trying to change the label of the button "Choose Activity" to "Schlafen" (in the Screen "DayWindow"), if the user clicks on the button "Schlafen" in the popup window.
My problem is that I dont know how to access the Screen "DayWindow" from the Popup-Class.
As you can see in the code, I tried to access it through the ScreenManager but than I get the Error: 'super' object has no attribute '__getattr__'
In another try I got the Error: Screen "day" does not exist
I would be very thankful for any help and tips.
My Pyhton File:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import runTouchApp
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
from datetime import date, timedelta
import json
class MenuWindow(Screen):
pass
class DayWindow(Screen):
null = ObjectProperty("Null")
def btn(self,index):
show_ActivityPopup()
def change_button():
scn = ScreenManager()
scn.ids.null.text = "Geschafft"
class WeekWindow(Screen):
pass
class MonthWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class ActivityPopup(FloatLayout):
def change_text(self):
DayWindow.change_button()
def show_ActivityPopup():
show = ActivityPopup()
ActivityPopupWindow = Popup(title="Activities", content=show, auto_dismiss=False)
ActivityPopupWindow.open()
kv = Builder.load_file("my.kv")
class MyMainApp(App):
def build(self):
return kv
if __name__ == "__main__":
MyMainApp().run()enter code here
My .kv - File
WindowManager:
MenuWindow:
DayWindow:
WeekWindow:
MonthWindow:
<MenuWindow>:
name: "menu"
GridLayout:
cols:1
Label:
text: "Time/MoodTracker"
Button:
text: "Day"
on_release:
app.root.current = "day"
root.manager.transition.direction ="left"
GridLayout:
cols: 2
Button:
text: "Weelky Overview"
on_release:
app.root.current = "week"
root.manager.transition.direction ="left"
Button:
text: "Monthly Overview"
on_release:
app.root.current = "month"
root.manager.transition.direction ="left"
<DayWindow>:
name: "day"
ScrollView:
GridLayout:
cols: 2
Label:
text: "0:00 - 0:30"
Button:
id: null
text: root.null
on_release:
root.btn(0)
Label:
text: "0:30 - 1:00"
id: Eins
Button:
text: "Choose Aviticity"
on_release: root.btn(1)
Label:
text: "1:00 - 1:30"
Button:
text: "Choose Aviticity"
on_release: root.btn(2)
Label:
text: "1:30 - 2:00"
Button:
text: "Choose Aviticity"
on_release: root.btn(3)
Label:
text: "2:00 - 2:30"
Button:
text: "Choose Aviticity"
on_release: root.btn(4)
<ActivityPopup>:
auto_dismiss: False
ScrollView:
GridLayout:
size_hint_y: None
cols:1
height: dp(600)
spacing: "10dp"
Button:
text: "Schlafen"
on_release:
root.change_text()
root.parent.parent.parent.dismiss()
Button:
text: "Frühstücken"
on_release:
root.parent.parent.parent.dismiss()
Button:
text: "Kochen"
Button:
text: "Lernen"
Button:
text: "Joggen"
<WeekWindow>:
name: "week"
Button:
text: "Back"
on_release:
app.root.current = "menu"
root.manager.transition.direction ="right"
<MonthWindow>:
name: "month"
Button:
text: "Back"
on_release:
app.root.current = "menu"
root.manager.transition.direction ="right"
A couple problems:
The code:
def change_button():
scn = ScreenManager()
scn.ids.null.text = "Geschafft"
is creating a new instance of ScreenManager, which has nothing to do with your App. So nothing that you do with scn will have any effect on your App.
And, in that same method, you are trying to access ids of the ScreenManager. Even if you were actually dealing with the WindowManager in your App, this would not work, because the WindowsManager has no defined ids.
The ids defined in your kv file are defined in the object that is the root of the rule where that id is defined. So, for example, the null id is defined in the ids of the DayWindow. The kivy documentation on this issue is confusing due to its multiple uses of the term root widget.
The fix is to just modify the change_text() method of the ActivityPopup class. Here is an updated version of that method, that should fix your problems:
class ActivityPopup(FloatLayout):
def change_text(self):
scn = App.get_running_app().root.get_screen('day')
scn.ids.null.text = "Geschafft"
So, I am creating a program with kivy that is dependant on being able to randomly choose a location from an already created dictionary by using buttons. I want the choice to display on the window the button takes you too rather than in the command line. Is there a way to do that? I have attached a snippet of .py code and my kivy code. I want the output displayed on the window (picture also attached) where it says "Go to:"
.py code:
import kivy
import random
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
FoodPlaces={'Asian': ['joy yee','strings','ramen san','chi cafe']}
class MainWindow(Screen):
pass
class FoodWindow(Screen):
def asianBtn(self):
print(random.choice(FoodPlaces['Asian']))
class AsianWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv=Builder.load_file("picker.kv")
class pickerApp(App):
def build(self):
return kv
if __name__=="__main__":
pickerApp().run()
kivy code:
WindowManager:
MainWindow:
FoodWindow:
AsianWindow:
<MainWindow>:
name:"main"
GridLayout:
cols:1
Label:
text:"Pick a Category"
Button:
text:"Food"
on_release:
app.root.current="food"
root.manager.transition.direction="left"
<FoodWindow>:
name: "food"
GridLayout:
cols:1
Label:
text:"Pick a Food Type"
Button:
text: "Asian"
on_release:
app.root.current="asian"
root.manager.transition.direction="left"
root.asianBtn()
Button:
text: "Go Back"
on_release:
app.root.current="main"
root.manager.transition.direction="right"
<AsianWindow>
name:"asian"
GridLayout:
cols:1
Label:
text: "Go to:"
Button:
text: "Go Back"
on_release:
app.root.current="food"
root.manager.transition.direction="right"
One way to do that is to add an id to the Label:
<AsianWindow>
name:"asian"
GridLayout:
cols:1
Label:
id: goto # Use this id to access the Label
text: "Go to:"
Button:
text: "Go Back"
on_release:
app.root.current="food"
root.manager.transition.direction="right"
To keep it simpler, put the asianBtn() method in the AsianWindow class:
class AsianWindow(Screen):
def asianBtn(self):
self.ids.goto.text = random.choice(FoodPlaces['Asian'])
And change its call in the kv to:
<FoodWindow>:
name: "food"
GridLayout:
cols:1
Label:
text:"Pick a Food Type"
Button:
text: "Asian"
on_release:
app.root.current="asian"
root.manager.transition.direction="left"
app.root.current_screen.asianBtn()
Button:
text: "Go Back"
on_release:
app.root.current="main"
root.manager.transition.direction="right"
With the asianBtn() method in the AsianWindow class, the path to the goto Label is simpler and the path to the asianBtn() method itself is simpler (since the current_screen is the AsianWindow at that point).
An even simpler way is to just use the on_enter() method of AsianWindow, so that a random choice is displayed whenever the AsianWindow is displayed. to do this, just replace the asianBtn() method with an on_enter() method:
class AsianWindow(Screen):
def on_enter(self, *args):
self.ids.goto.text = random.choice(FoodPlaces['Asian'])
And now you don't even need to call asianBtn() from the Button:
<FoodWindow>:
name: "food"
GridLayout:
cols:1
Label:
text:"Pick a Food Type"
Button:
text: "Asian"
on_release:
app.root.current="asian"
root.manager.transition.direction="left"
Button:
text: "Go Back"
on_release:
app.root.current="main"
root.manager.transition.direction="right"
So you are using print which won't help you display within the UI. I'm not 100% sure how/where you want to display, but I would advise you to add a label wherever you want to display the output. You would want this label to have an id property so you can change its text value dynamically to whatever the random function chooses from the dict.
The lable will look something like this in kv:
Label:
id: output
And to set its value you need a line like this in the py file:
self.ids.output.text = "name of restaurant"
Hope this helps!
I am new to Kivy so this may be a trivial question. I am working on a project that has two screens, each of which contains a button that generates a popup. I would like for the popup to display a statement containing the name of the current screen. My problem is despite having a method to change the popup text, the placeholder text is always being displayed. Why doesn't the changeText method change the text of the popup?
My problem seems similar to the one shown:
Kivy Label.text Property doesn't update on the UI
However I am having some trouble understanding how to apply it to my specific situation.
Python Code:
class Screen1(Screen):
pass
class Screen2(Screen):
pass
class MyManager(ScreenManager):
pass
class PopUp(Popup):
def changeText(self,nameStr):
self.ids.label.text = "You are on Screen %s!" %nameStr #this is text that I want to display
class PrimaryApp(App):
def build(self):
return MyManager()
PrimaryApp().run()
Kv Code:
#:import Factory kivy.factory.Factory
<MyManager>:
Screen1:
id: screen1
Screen2:
id: screen2
<Screen1>:
name: "one"
GridLayout:
id: grid
rows: 2
Button:
id: button1
text: "Go to Screen Two"
on_release: root.manager.current = "two"
Button:
id: button2
text: "Display Popup"
on_release:
Factory.PopUp().changeText(root.name)
Factory.PopUp().open()
<Screen2>:
name: "two"
GridLayout:
id: grid
rows: 2
Button:
id: button1
text: "Go to Screen One"
on_release: root.manager.current = "one"
Button:
id: button2
text: "Display Popup"
on_release:
Factory.PopUp().changeText(root.name)
Factory.PopUp().open()
<PopUp>:
id:pop
size_hint: (.5,.5)
title: "Notice!"
Label:
id: label
text: "PLACEHOLDER TEXT" #this is not the code I want displayed
[1]:
Use Popup event, on_open to change the Popup content, Label widget's text.
Popup » API
Events: on_open:
Fired
when the Popup is opened.
Snippets
<PopUp>:
on_open:
label.text = "You are on Screen %s!" % app.root.current
id:pop
...
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.popup import Popup
class Screen1(Screen):
pass
class Screen2(Screen):
pass
class MyManager(ScreenManager):
pass
class PopUp(Popup):
pass
class PrimaryApp(App):
def build(self):
return MyManager()
PrimaryApp().run()
primary.kv
#:kivy 1.10.0
#:import Factory kivy.factory.Factory
<MyManager>:
Screen1:
id: screen1
Screen2:
id: screen2
<Screen1>:
name: "one"
GridLayout:
id: grid
rows: 2
Button:
id: button1
text: "Go to Screen Two"
on_release: root.manager.current = "two"
Button:
id: button2
text: "Display Popup"
on_release:
Factory.PopUp().open()
<Screen2>:
name: "two"
GridLayout:
id: grid
rows: 2
Button:
id: button1
text: "Go to Screen One"
on_release: root.manager.current = "one"
Button:
id: button2
text: "Display Popup"
on_release:
Factory.PopUp().open()
<PopUp>:
on_open:
label.text = "You are on Screen %s!" % app.root.current
id:pop
size_hint: (.5,.5)
title: "Notice!"
Label:
id: label
text: "PLACEHOLDER TEXT" #this is not the code I want displayed
Output
everytime you call Factory().Popup() it creates a brand new Popup which has nothing to do with the previous. What you can do it's:
in the kv:
...
<Screen1>:
name: "one"
GridLayout:
id: grid
rows: 2
Button:
id: button1
text: "Go to Screen Two"
on_release: root.manager.current = "two"
Button:
id: button2
text: "Display Popup"
on_release:
p = Factory.PopUp()
p.changeText(root.name)
p.open()
And the same thing for the second screen. But everytime you release these buttons it creates a new created popup too much memory wasted. The best thing you can do it is initialize your screen manager with a popup and then only change the text of this popup:
Python:
...
from kivy.properties import ObjectProperty
...
class PopUp(Popup):
def changeText(self,*args):
self.ids.label.text = "You are on Screen %s!" % args[0].current
class MyManager(ScreenManager):
popup = ObjectProperty()
def __init__(self, **kwargs):
super(MyManager, self).__init__(**kwargs)
self.popup = PopUp()
self.bind(current=self.popup.changeText)
and the kv:
...
<PopUp>:
id:pop
size_hint: (.5,.5)
title: "Notice!"
Label:
id: label
text: "You are on Screen one!"
<Screen1>:
name: "one"
GridLayout:
id: grid
rows: 2
Button:
id: button1
text: "Go to Screen Two"
on_release: root.manager.current = "two"
Button:
id: button2
text: "Display Popup"
on_release:
root.manager.popup.open() #Same thing for the second screen
I have an app with 2 screens (ScreenManager).
I wrote a function in the second screen (SettingsScreen) and I want this function to update a label in the first screen.
Below the two classes:
class MenuScreen(Screen):
count = NumericProperty(30)
class SettingsScreen(Screen):
def set_20(self):
self.count = 20
The classes are linked to a button and a label in the Kivy stylesheet with two different screens
<MenuScreen>:
FloatLayout:
orientation: 'horizontal'
Label:
id: l_label
text: str(root.count)
<SettingsScreen>:
BoxLayout:
orientation: 'vertical'
padding: 50
FloatLayout:
Button:
text: "20"
on_release: root.set_20()
To be clearer, the user has to click on the Button in the SettingsScreen to set the value of the NumericProperty in the first screen to 20.
At the moment, if I click on the button nothing happens.
What you see above is an extract - The full code of the app is stored below if you want to see more.
https://github.com/marcogdepinto/LifeCounter
Thanks in advance for your help.
I think the problem is that when your button gets clicked and calls the set_20 method, that method is trying to set the property count of the SettingsScreen to 20. In other words
def set_20(self):
self.count = 20
would try to set the the count property inside the SettingsScreen (hence the word self) and it cannot find it. There is another count property inside MenuScreen which it knows nothing about. To fix this I think you should give an id to MenuScreen
<MenuScreen>:
id: menu
and in the on_release method of the Button do
on_release: menu.count = 20
EXAMPLE
Python file
class MenuScreen(Screen):
counter = NumericProperty(0)
class SettingsScreen(Screen):
pass
class MyWidget(ScreenManager):
pass
class MyRandomApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyRandomApp().run()
kv file
<MyWidget>:
id:manager
MenuScreen:
id: menu
name: 'menuscreen'
BoxLayout:
orientation: 'vertical'
Label:
text: str(menu.counter)
Button:
text: 'Go to S2'
on_press: manager.current = 'settingsscreen'
SettingsScreen:
id: settings
name: 'settingsscreen'
BoxLayout:
orientation: 'vertical'
Button:
text: 'Click to edit label of Prev screen'
on_press: menu.counter = 20
Button:
text: 'Go to S1'
on_press: manager.current = 'menuscreen'
My app gets data from a database and and is stored into variables in Python. The code below is a simplified version where you have two screens. The first screen has two buttons and the second screen has a label and a back button. The text of the label on the second screen will change depending on what button is pressed.
When run, the label is set to the value of the StringProperty, which is "Test". When one of the buttons are clicked the ChangeScreen function is run and works out the correct new label. The LabelUpdater function on the second is run which should change the string property but doesn't. How do I fix this issue? Thanks <3
Python:
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty
class DemoScreen1(Screen):
def ChangeScreen(self, button_text):
if button_text == "Button 1":
new_label = "This is the new label when button 1 is pressed"
DemoScreen2.LabelUpdater(new_label)
else:
new_label2 = "This is the new label when button 2 is pressed"
DemoScreen2.LabelUpdater(new_label2)
self.parent.current = "demoscreen2"
class DemoScreen2(Screen):
screen2_label = StringProperty("Test")
def LabelUpdater(NEW_LABEL):
screen2_label = StringProperty(NEW_LABEL)
class AppScreenManager(ScreenManager):
pass
class Tester(App):
pass
if __name__ == '__main__':
Tester().run()
Kivy:
AppScreenManager:
DemoScreen1:
DemoScreen2:
<DemoScreen1>:
name: "demoscreen1"
orientation: "vertical"
GridLayout:
rows: 2
Button:
id: Button1
text: "Button 1"
on_release: root.ChangeScreen(Button1.text)
Button:
id: Button2
text: "Button 2"
on_release: root.ChangeScreen(Button2.text)
<DemoScreen2>:
name: "demoscreen2"
orientation: "vertical"
GridLayout:
rows:2
Label:
text: root.screen2_label
Button:
text:"Back"
on_release: app.root.current = "demoscreen1"
Use ids and reference through AppScreenManager aka the ScreenManager.
self.parent.ids.screen2.screen2_label = new_label
See full example below.
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty
class DemoScreen1(Screen):
def ChangeScreen(self, button_text):
if button_text == "Button 1":
new_label = "This is the new label when button 1 is pressed"
print('Palim')
#DemoScreen2.LabelUpdater(new_label)
self.parent.ids.screen2.screen2_label = new_label
else:
new_label2 = "This is the new label when button 2 is pressed"
self.parent.ids.screen2.screen2_label = new_label2
#DemoScreen2.LabelUpdater(new_label2)
self.parent.current = "demoscreen2"
class DemoScreen2(Screen):
screen2_label = StringProperty("Test")
def LabelUpdater(NEW_LABEL):
screen2_label = StringProperty(NEW_LABEL)
class AppScreenManager(ScreenManager):
pass
class Tester(App):
pass
if __name__ == '__main__':
Tester().run()
kv
AppScreenManager:
DemoScreen1:
id: screen1
DemoScreen2:
id: screen2
<DemoScreen1>:
name: "demoscreen1"
orientation: "vertical"
GridLayout:
rows: 2
Button:
id: Button1
text: "Button 1"
on_release: root.ChangeScreen(Button1.text)
Button:
id: Button2
text: "Button 2"
on_release: root.ChangeScreen(Button2.text)
<DemoScreen2>:
name: "demoscreen2"
orientation: "vertical"
GridLayout:
rows:2
Label:
text: root.screen2_label
Button:
text:"Back"
on_release: app.root.current = "demoscreen1"