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!
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"
I have a class called StatSummary that has labels and a button at the bottom called "Reset". When I press this button, a popup appears with a button that also says "Reset". When I press the popup "Reset" button, I want to update the labels' text of the class StatSummary. I created a method inside StatSummary called resetStatSummary which updates the labels' text using IDs.
However, when I call Factory.StatSummary().resetStatSummary() from ResetPopup, the labels on the screen do not update. This is especially confusing because I added a print statement in my Python file to see if the function runs and it does, but the labels' text does not update.
Python File:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.config import Config
from kivy.uix.popup import Popup
class StatSummary(Screen):
def resetStatSummary(self): # resets StatSummary text when "Reset" button is pressed
self.ids.summaryShotFractionLabel.text = "0/0"
self.ids.summaryShotPercentLabel.text = "0.0%"
self.ids.summaryLongStreakLabel.text = "Longest Streak: 0"
print("resetStatSummary ran")
class ResetPopup(Popup):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file('basketball.kv')
class MainApp(App):
def build(self):
return kv
if __name__ == "__main__":
MainApp().run()
.kv File:
#:import Factory kivy.factory.Factory
WindowManager:
StatSummary
<StatSummary>
name: "statSummary"
GridLayout:
BoxLayout:
orientation: "vertical"
Label:
id: summaryShotFractionLabel
text: "0/0"
Label:
id: summaryShotPercentLabel
text: "0.0%"
BoxLayout:
orientation: "vertical"
Label:
id: summaryLongStreakLabel
text: "Longest Streak: 0"
BoxLayout:
orientation: "horizontal"
Button:
text: "Reset"
on_release:
Factory.ResetPopup().open()
<ResetPopup>:
title: "Wait..."
BoxLayout:
cols: 1
orientation: "vertical"
Label:
text: "Are you sure you want to reset?"
Button:
text: "Go back"
on_release:
root.dismiss()
Button:
text: "Reset"
on_release:
root.dismiss()
Factory.StatSummary().resetStatSummary()
app.root.current = "interact"
app.root.transition.direction = "right"
The label doesn't change because you call wrong object's resetStatSummary method.
Within reset's on_release button method you have:
Factory.StatSummary().resetStatSummary()
which means: create fresh new StatSummary object (StatSummary() will create and return new object - class StatSummary instance), then call it's method resetStatSummary(). So you call this method on brand new object, not this created by kv file. To access expected StatSummary class instance just replace line:
Factory.StatSummary().resetStatSummary()
with
app.root.get_screen('statSummary').resetStatSummary()
I want to catch a value from my first screen into my thirdscreen.
In the first, I write my name in an input field.
I go to the next window.
And I try to show my name in this last window.
So I share the code with you and I hope I will find an issue.
Python code :
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
#define ou different screens
class FirstWindow(Screen):
def envoyer(self):
name = self.ids.nom_input.text
self.ids.my_name.text = name
class SecondWindow(Screen):
pass
class ThirdWindow(Screen):
#PROBLEM HERE
def on_press(self):
self.ids.recup_infos.text = self.root.get_screen('FirstWindow').ids.my_name.text
class WindowManager(ScreenManager):
pass
class MonWidget(Widget):
pass
kv = Builder.load_file('new_window.kv')
class AwesomeApp(App):
def build(self):
Window.clearcolor = (0,0,0,0)
return kv
if __name__ == '__main__':
AwesomeApp().run()
My KV CODE :
WindowManager:
FirstWindow:
SecondWindow:
ThirdWindow:
<FirstWindow>:
name: "romain"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
id: my_name
text: "Entrez votre nom"
font_size: 32
TextInput:
id: nom_input
multiline: False
size_hint: (1, .5)
Button:
text: "Next screen"
font_size: 32
on_press: root.envoyer()
on_release:
app.root.current = "Mickael"
root.manager.transition.direction = "left"
<SecondWindow>:
name: "Mickael"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
text: "Entre votre ville"
font_size: 32
TextInput:
id: ville_input
multiline: False
size_hint: (1, .5)
Button:
text: "Vérifier les infos"
font_size: 32
on_release:
app.root.current = "foncier"
root.manager.transition.direction = "left"
Button:
text: "go back first screen"
font_size: 32
on_release:
app.root.current = "romain"
root.manager.transition.direction = "right"
<ThirdWindow>:
name: "foncier"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
text: "Verifier : "
font_size: 32
Label:
id: recup_infos
text: ""
font_size: 32
color: 'white'
Button:
text: "On press"
font_size: 32
#Problem HERE
on_press: root.on_press()
Button:
text: "Précedent"
font_size: 32
on_release:
app.root.current = "Mickael"
root.manager.transition.direction = "right"
Could you help me ?
Thank you
Romain
In your on_press method:
def on_press(self):
self.ids.recup_infos.text = self.root.get_screen('FirstWindow').ids.my_name.text
self.root.get_screen('FirstWindow').ids.my_name.text isn't the correct way to get access to widgets outside of the class that you are in right now, or in this situation, screen. The correct way is to use the App.get_running_app() method:
self.ids.recup_infos.text = App.get_running_app().root.ids.First.ids.my_name.text
But before doing that, you have to give ids to the screens of your app, so that the First argument of the method demonstrated above actually makes sense:
WindowManager:
FirstWindow:
id: First
# "First" is the id of the FirstWindow class
# which can also explain why there was a "First" arg
# inside "App.get_running_app().root.ids.First.ids.my_name.text"
SecondWindow:
id: Second
ThirdWindow:
id: Third
Still confused to why this works? Let's divide the attributes of App.get_running_app().root.ids.First.ids.my_name.text into 3 parts:
App.get_running_app(): this method returns the location of your running App class, in this case AwesomeApp. This also acts as self if you were to get the variable inside the App object itself
.root.ids.First: if you read the Kivy documentation, or just simply watched Kivy course videos online, carefully, you should know that self.root.ids inside the App object returns a list of ids of the widgets inside your root widget. In this case, App.get_running_app().root.ids is doing the same thing here, and your screens are passed in the ScreenManager root widget, hence make First an available attribute in App.get_running_app().root.ids
.ids.my_name.text: same as above, App.get_running_app().root.ids.First acts the same as self if you were to run it in your FirstWindow class, which gives you the opportunity to get access to variables outside of your working classes/screens
I am trying to incorporate ads into a Kivy app I made, and to do this, I want to have one class (with the ads) in a FloatLayout to be at the top of my screen, and the rest of the app to be below, in a separate class.
I was trying to get this to work with some test code (simple .py and .kv file that has multiple screens and classes and organizes accordingly). The code is supposed to have two float layouts: One has text, the other has a button that you press and it takes you to the next screen. However the issue I am having is that I can't position the button correctly, as it appears that the widget is shrunk in the bottom left corner. It is supposed to be next to the text box.
Here is my .kv file:
WindowManager:
Screen1:
Screen2:
<Screen1>:
name: "screen1"
FloatLayout:
Label:
pos_hint: {'top': 1, "center_x": 0.5}
size_hint: (0.2, 0.5)
font_size: 40
text: "TEXT AT TOP OF SCREEN"
FloatLayout:
TextInput:
pos_hint: {"x": 0.1, "y": 0.05}
size_hint: (0.3, 0.05)
multline:False
GoS:
FloatLayout:
Button:
text: "PRESS TO GO TO SCREEN 2"
pos_hint: {"right": 0.5, "center_y": 0.7}
on_press: widget.goscreen()
<Screen2>:
name: "screen2"
Label:
text: "YOU ARE ON SCREEN TWO"
and here is the .py file:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class Screen1(Screen):
pass
class GoS(Widget):
def goscreen(self):
self.parent.current = "screen2"
class Screen2(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("rec_view.kv")
class TestApp(App):
def build(self):
return kv
if __name__ == "__main__":
TestApp().run()
Why is this happening?
Another small point also is that my button doesn't work because I can't seem to call the correct class. If I use "root.goscreen()", it doesn't work as my root widget doesn't have this function. What should be the correct syntax here?
I recommend you to use BoxLayout to divide your GUI.
Your button don't work because you don't link it to a valid Widget.
The GoS widget must be define in KV or be imported.
Here is a proposition for your rec_view.kv file :
WindowManager:
Screen1:
Screen2:
<GoS>:
Button:
text: "PRESS TO GO TO SCREEN 2"
on_press: root.goscreen()
<Screen1>:
name: "screen1"
BoxLayout:
orientation: "vertical"
BoxLayout:
orientation: "horizontal"
size_hint_y: 0.5 # Proportion of screen height that the widget occupe
Label:
font_size: 40
text: "TEXT AT TOP OF SCREEN"
BoxLayout:
orientation: "horizontal"
size_hint_y: 0.3
TextInput:
multline:False
BoxLayout:
orientation: "horizontal"
size_hint_y: 0.2
GoS:
<Screen2>:
name: "screen2"
Label:
text: "YOU ARE ON SCREEN TWO"
The definition of goscreen is not correct, GoS is not a Screen, so his parent don't have current , use instead App.get_running_app().root.current = "screen2".
I don't know what you want to do and why you define the GoS class, but you can avoid it. By moving the goscreen definition into Screen1 class, and replace the 3rd BoxLayout of Screen1 with this:
BoxLayout:
orientation: "horizontal"
size_hint_y: 0.2
Button:
text: "PRESS TO GO TO SCREEN 2"
on_press: root.goscreen()
I made this to code to show what I want but fail to do. I get the message: "AttributeError: 'super' object has no attribute 'getattr'" when I try to acces a widget.
Someone please explain how to use widget id:s so that you can acces them from anywhere using python code, i'm sure I'm doing something fundamentally wrong.
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager, SlideTransition
from kivy.lang.builder import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
import random
Builder.load_string('''
<Manager>:
id: screen_manager
SettingScreen:
id: settings
name: 'settings'
manager: 'screen_manager'
MainScreen:
id: mainscreen
name: 'mainscreen'
manager: 'screen_manager'
ThirdScreen:
id: thirdscreen
name: 'thirdscreen'
manager: 'screen_manager'
<SettingScreen>:
BoxLayout:
id: settingbox
orientation: "vertical"
TextInput:
id: getthis
text: "this is the data"
font_size: 40
size_hint: 1,0.1
Button:
text: "NEXT SCREEN"
font_size: 40
size_hint: 1,0.1
on_release:
app.root.current = "mainscreen"
app.root.transition.direction="left"
<MainScreen>:
BoxLayout:
orientation: 'vertical'
Label:
id: changethis
text: "to be changed"
Button:
text: "Push this to make above into the text from TextInput on last screen"
on_release:
app.root.ids.changethis.text = app.root.ids.getthis.text #<<<<<
Button:
text: "Go to third Screen"
on_release:
app.root.current = "thirdscreen"
app.root.transition.direction="left"
<ThirdScreen>
put_label_here: put_label_here
BoxLayout:
orientation: 'horizontal'
Button:
text: 'Make make label'
on_release:
self.parent.parent.makelabel()
BoxLayout:
orientation: 'vertical'
id: put_label_here
''')
class MainScreen(Screen):
def __init__(self, **kwargs):
Screen.__init__(self, **kwargs)
class SettingScreen(Screen):
def __init__(self, **kwargs):
super(SettingScreen, self).__init__(**kwargs)
class ThirdScreen(Screen):
def __init__(self, **kwargs):
super(ThirdScreen, self).__init__(**kwargs)
def makelabel(self): #this should make a Label with text from TextInput on SettingScreen
print('Runs function makelabel')
thelabel=Label(text=self.parent.settings.settingbox.getthis.text)
self.put_label_here.add_widget(thelabel)
class Manager(ScreenManager):
pass
sm = ScreenManager(transition=SlideTransition())
sm.add_widget(MainScreen(name='mainscreen'))
sm.add_widget(SettingScreen(name='settings'))
sm.add_widget(SettingScreen(name='thirdscreen'))
class testApp(App):
def build(self):
return Manager()
if __name__ == "__main__":
testApp().run()
Well, I can fix this for you, but honestly I don't think you are making the right use of kv templates. But it will work anyway.
A new template is a rule by itself, it has its own root, so in return the application root (app.root) won't have direct access to children's ids defined inside their own templates. So you have to reach its direct child which makes a template containing the child you want to access then you reach that child by its id.
For instance, in your kv code, you will need to change line 63 (kv) from:
app.root.ids.changethis.text = app.root.ids.getthis.text
to:
app.root.ids.mainscreen.ids.changethis.text = app.root.ids.settings.ids.getthis.text
and line 99 (python) from:
thelabel=Label(text=self.parent.settings.settingbox.getthis.text)
to:
thelabel=Label(text=self.parent.ids.settings.ids.getthis.text)
Why shouldn't root (or any parent widget) access the ids defined inside separate templates ?
Templates, or Dynamic classes are made for re-usability, just like any none-static class.
Consider the following scenario:
BoxLayout:
CustomWidget:
id: wdg1
CustomWidget:
id: wdg2
Button:
text: 'change text of first label'
on_press: root.ids.lbl.text = 'new text'
<CustomWidget#Widget>:
Label:
text: 'some text'
id: lbl
Now The root widget have two instances of CustomWidget as children, thus two children Labels having the id 'lbl'. Which one should be called if the root called lbl directly?
To handle this, you can call lbl from its direct template instance. Thus: root.ids.wdg1.ids.lbl.text for the label inside the first widget, or root.ids.wdg2.ids.lbl.text for the label inside the second one.