Using a Spinner for changing several Screens in kivy - python

I need help. I'm making an app, And when i want to use an Spinner. I assigned several values ("Catalogue","Buy","Payment Methods", "Contact"). I want that when choosing the value "Buy", My screen change to the "SecondWindow(Screen)". I was trying to do it from my py.file but I'm not sure how to do it correctly. I was trying to do it from my "def spinner_clicked(self,value):"
If you I can do it from my kv.file straightly. I would like to learn how to do it too.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.core.spelling import Spelling
from kivy.uix.screenmanager import ScreenManager, Screen
kv= Builder.load_file('test2.kv')
class MyLayout(Widget):
def spinner_clicked(self,value):
sm= ScreenManager()
screen= Screen(name='second')
self.ids.Label1.text= f'You selected: {value}'
#IN THIS PART WHEN PRESSING IN MY SPINNER the choice "buy" I need to change to the SecondWindow(Screen)
if self.ids.spinner_id.values == "Buy":
#self.sm.current= 'second'
#sm.root.manager.current= "second"
#sm.switch_to(screen)
pass
#Definine our different screens
class FirstWindow(Screen):
pass
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class AwesomeApp(App):
def build(self):
return MyLayout()
if __name__ == '__main__':
AwesomeApp().run()
kv.file
<MyLayout>
BoxLayout:
orientation:"vertical"
size: root.width, root.height
GridLayout:
cols:3
rows:3
Spinner:
id: spinner_id
text: "Menu"
values: ["Catalogue","Buy","Payment Methods", "Contact"]
on_text: root.spinner_clicked(spinner_id.text)
#on_text: root.manager.current= "second" - it doesnt' work
#I want to change the screens from here choosing the value "Buy" to the 2ndScreen
Label:
id: Label1
text: "My Panel"
Button:
text:"Buy"
WindowManager:
FirstWindow:
SecondWindow:
<FirstWindow>:
name: "first"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
text: "First Screen"
font_size: 32
Button:
text: "Next Screen"
font_size: 32
on_release:
root.manager.current= "second"
root.manager.transition.direction= "left" #up
<SecondWindow>:
name: "second"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
text: "Second Screen"
font_size: 32
Button:
text: "Go Back"
font_size: 32
on_release:
#app.root.current= "first"
root.manager.current= "first"
root.manager.transition.direction= "right"

If you set the Screen names to the values in your Spinner, then you can modify your kv slightly. In the Spinner section:
Spinner:
id: spinner_id
text: "Menu"
values: ["Catalogue","Buy","Payment Methods", "Contact"]
on_text: root.spinner_clicked(spinner_id.text)
on_text: window_manager.current = self.text # uses an id defined in the WindowManager section
And the window_manager id is defined later in the kv:
WindowManager:
id: window_manager
FirstWindow:
SecondWindow:
The window_manager.current refers to the id defined for the WindowManager, and accesses its current property.
Note that the root.manager in the kv file only works inside a rule for a Screen, like inside <FirstWindow>: or <SecondWindow>:. In that context, the root refers to the root widget of the rule (one of those Screens), and manager then refers to the ScreenManager that contains that Screen.

Related

Why my ScreenManager not switching in Kivy

I'm trying to build an interface in python with Kivy. To do this, i want to follow this structure:
[ScreenManager#1] -> [Login Screen]
[ScreenManager#2] -> [All others screens]
My ScreenManager#1 is declared in my App function in python file. My ScreenManager#2 is initialised in a kv file. To simply the help that you can bring to me, i've joined the two in the code below.
my AllScreensScreenManager#2, need to have a template shared with all the other screens. I've gave an id to it and call this one with my button in my LoginScreen, like this was suggested here. But the switch is not working. What i'm doing wrong with it.
import kivy
kivy.require('2.1.0') # replace with your current kivy version !
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager,Screen
Builder.load_string('''
<AllScreens>:
id: second_manager
manager: scr_manager
orientation: "vertical"
BoxLayout:
ScreenManager:
id: scr_manager
transition: "left"
Welcome:
GenerateScreen:
ModifyScreen:
ConsultScreen:
LogoutScreen:
BoxLayout:
size_hint: 1, None
height: "40dp"
pos_hint: {"bottom": 0}
spacing: "5dp"
Button:
id: consult
text: "Consult"
on_press: root.manager.current("consult")
Button:
id: modify
text: "Modify"
on_press: root.manager.current("modify")
Button:
id: generate
text: "Generate"
on_press: root.manager.current("generate")
Button:
id: logout
text: "Logout"
on_press: root.manager.current("logout")
<LoginScreen>:
name: "login"
BoxLayout:
Label:
text: "Login"
Button:
text: "Connect"
on_release: second_manager.current("welcome")
<WelcomeScreen>:
name: "welcome"
BoxLayout:
Label:
text: "welcome"
<ConsultScreen>:
name: "consult"
BoxLayout:
Label:
text: "consult"
<GenerateScreen>:
name: "generate"
BoxLayout:
Label:
text: "password"
''')
class ScreenManagement(ScreenManager):
pass
class LoginScreen(Screen):
print("login screen")
pass
class WelcomeScreen(Screen):
pass
class AllScreens(ScreenManager):
pass
class ConsultScreen(Screen):
pass
class GenerateScreen(Screen):
pass
class MyApp(App):
def build(self):
sm = ScreenManagement()
sm.add_widget(LoginScreen(name="login"))
return sm
if __name__ == '__main__':
MyApp().run()

Adding a class(screen) to a class(widget) in kivy

I'm trying to make an app in where the top_part of the app there are a Spinner,Label and Button and these 3 are inside a GridLayout with 3 columns. When I click in the spinner. It's displays some choices such as: "Catalogue", "Buy", "Payment Methods". I want that every time I press the Spinner and select a choice, change the screen that is below of this GridLayout. In these case would be 3 screens because they are 3 choices ("Catalogue", "Buy", "Payment Methods"). and these should be below of BoxLayout in MyLayout(U can see it in the kv.code)
The screens are not working, and I got an error when run the app(invalid class). Just work the part of MyLayout, but from ScreenManager and Screens are not working, I don't know how to fix it.
Py.file
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.core.spelling import Spelling
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file('test2.kv')
class MyLayout(Widget):
def spinner_clicked(self,value):
self.ids.Label1.text= f'You selected: {value}'
#Definine our different screens
class FirstWindow(Screen):
pass
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class AwesomeApp(App):
def build(self):
return MyLayout()
if __name__ == '__main__':
AwesomeApp().run()
This is the kv.file
<MyLayout>
BoxLayout:
orientation:"vertical"
size: root.width, root.height
GridLayout:
cols:3
Spinner:
id: spinner_id
text: "Menu"
values: ["Catalogue","Buy","Payment Methods", "Contact"]
on_text: root.spinner_clicked(spinner_id.text)
Label:
id: Label1
text: "My Panel"
Button:
text:"Buy"
Label:
id: Label2
text: "My Panel"
WindowManager:
FirstWindow:
SecondWindow:
<FirstWindow>:
name: "first"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
text: "First Screen"
font_size: 32
Button:
text: "Next Screen"
font_size: 32
on_release:
app.root.current= "second"
root.manager.transition.direction= "left" #up
<SecondWindow>:
name: "second"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
Label:
text: "Second Screen"
font_size: 32
Button:
text: "Go Back"
font_size: 32
on_release:
app.root.current= "first"
root.manager.transition.direction= "right"
As #ApuCoder suggested, you can change MyLayout to extend BoxLayout, and add the WindowManager to that. Just change the definition of MyLayout:
class MyLayout(BoxLayout):
def spinner_clicked(self,value):
self.ids.Label1.text= f'You selected: {value}'
And modify your kv to use the modified MyLayout:
<MyLayout>
orientation:"vertical"
GridLayout:
cols:3
Spinner:
id: spinner_id
text: "Menu"
values: ["Catalogue","Buy","Payment Methods", "Contact"]
on_text: root.spinner_clicked(spinner_id.text)
Label:
id: Label1
text: "My Panel"
Button:
text:"Buy"
# add ScreenManager and Screens
WindowManager:
FirstWindow:
SecondWindow:
<FirstWindow>:
name: "first"
BoxLayout:
orientation: "vertical"
Label:
text: "First Screen"
font_size: 32
Button:
text: "Next Screen"
font_size: 32
on_release:
root.manager.current= "second" # use root.manager instead of app.root
root.manager.transition.direction= "left" #up
<SecondWindow>:
name: "second"
BoxLayout:
orientation: "vertical"
Label:
text: "Second Screen"
font_size: 32
Button:
text: "Go Back"
font_size: 32
on_release:
root.manager.current= "first" # use root.manager instead of app.root
root.manager.transition.direction= "right"

Only "else statement" shown when running app with python/kivy

I have been working on an app with Kivy that receives 0, 1 or 2 as text input by the user and a label on the final screen shows a text which should differ based on the received input. It does not present with any errors any longer, but the final screen will only show the "else" text ("Bad Luck") even when the requirements of the "if statement" are met.
By searching similar questions, I tried using an "input_filter" for integers in case it resolved the issue, but it didn't.
I am a beginner with no programming background, so please feel free to be as elaborate as you like in your answers. Thank you in advance for your time and any ideas/recommendations you may share. Following are the codes of the .py and .kv files:
.py file:
import kivy
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.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import NumericProperty, StringProperty, NumericProperty
class MainWindow(Screen):
pass
class SecondWindow(Screen):
pass
class ThirdWindow(Screen):
pass
class FourthWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class MyApp(App):
def build(self):
kv = Builder.load_file("my.kv")
return kv
if __name__=="__main__":
MyApp().run()
.kv file:
WindowManager:
MainWindow:
SecondWindow:
ThirdWindow:
FourthWindow:
<MainWindow>:
name: "main"
GridLayout:
cols:1
rows:2
Label:
text: "stuff"
Button:
text: "stuff"
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
<SecondWindow>:
name: "second"
GridLayout:
cols:1
rows:2
GridLayout:
cols:2
Label:
text: "stuff"
TextInput:
id: ti_a
multiline:False
Label:
text: "stuff"
TextInput:
id: ti_b
multiline:False
Label:
text: "stuff"
TextInput:
id: ti_c
multiline:False
Label:
text: "stuff"
TextInput:
id: ti_d
multiline:False
Label:
text: "stuff"
TextInput:
id: ti_e
multiline:False
GridLayout:
cols:2
Button:
text: "stuff"
on_release:
app.root.current = "third"
root.manager.transition.direction = "left"
Button:
text: "Back"
on_release:
app.root.current = "main"
root.manager.transition.direction = "right"
<ThirdWindow>:
name: "third"
GridLayout:
cols:1
rows:2
GridLayout:
cols:2
Label:
text: "stuff"
TextInput:
id: ti_f
multiline:False
GridLayout:
cols:2
Button:
text: "stuff"
on_release:
app.root.current = "fourth"
root.manager.transition.direction = "left"
Button:
text: "Back"
on_release:
app.root.current = "second"
root.manager.transition.direction = "right"
<FourthWindow>:
name: "fourth"
Label:
text: "Stuff" if root.manager.get_screen("second").ids.ti_a.text == "0" and root.manager.get_screen("second").ids.ti_b.text == "0" and root.manager.get_screen("second").ids.ti_c.text == "0" and root.manager.get_screen("second").ids.ti_d.text == "0" and root.manager.get_screen("second").ids.ti_e.text == "1" and root.manager.get_screen("third").ids.ti_f.text == "0" else "Bad Luck"
The problem is that kivy doesn't recognize the expression:
root.manager.get_screen("second").ids.ti_a.text
as a property that it can bind to, so the value of Label text in your FourthWindow is only evaluated once (when the FourthWindow is created). Any changes to the other Labels in your app will have no effect on FourthWindow. See the documentation.
The fix is to write your own code to update that Label by adding an id to that Label:
<FourthWindow>:
name: "fourth"
Label:
id: lab
text: ""
And adding a on_pre_enter() method to FourthWindow:
class FourthWindow(Screen):
def on_pre_enter(self, *args):
if self.manager.get_screen("second").ids.ti_a.text == "0" and \
self.manager.get_screen("second").ids.ti_b.text == "0" and \
self.manager.get_screen("second").ids.ti_c.text == "0" and \
self.manager.get_screen("second").ids.ti_d.text == "0" and \
self.manager.get_screen("second").ids.ti_e.text == "1" and \
self.manager.get_screen("third").ids.ti_f.text == "0":
self.ids.lab.text = 'Stuff'
else:
self.ids.lab.text = 'Bad Luck'
This will evaluate your expression and set the text each time just before the FourthWindow is displayed.

.py file interact with .kv file

So i have created some screens and added some properties to them (layouts, buttons, name etc) but everything is on the kivy file. Now, i want to make a function in the python main file which will take me from the starting screen to the next one after a brief time. Though nothing seems to work without me having to move everything from the kivy file to the python file. Any ideas?
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class AndAnotherScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class BackgroundLabel(Label):
pass
class CompanyImage(Image):
pass
class LoadingScreen(Screen):
def __init__(self, **kwargs):
super(LoadingScreen, self).__init__(**kwargs)
def ChangeScreen(self):
ScreenManager().current = MainScreen().name
presentation = Builder.load_file("tsalapp.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
.kv
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#:import Clock kivy.clock.Clock
#: import Color kivy.graphics
#: import Rectangle kivy.graphics
ScreenManagement:
transition: FadeTransition()
LoadingScreen:
MainScreen:
AnotherScreen:
AndAnotherScreen:
<BackgroundLabel>:
background_color: 30,144,255,1
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: self.size
<LoadingScreen>:
name: "main"
id: "yolo"
on_enter: Clock.schedule_once(none, 3)
#app.root.current: "menu"
Image:
source: 'Untitled.png'
height: root.height
width: root.width
allow_stretch: True
keep_ratio: False
AnchorLayout:
Label:
text: "Tsala Inc."
anchor_x: 'center'
anchor_y: 'center'
font_size: root.height/15
<MainScreen>:
name: "menu"
id: "swag"
BoxLayout:
orientation: "vertical"
Button:
text: "Next Page"
background_color: 1,1,4,1
size_hint: (0.5, 0.5)
on_release:
app.root.current= "Another Screen"
Button:
text: "Previous Page"
background_color: 1,4,1,1
size_hint: (0.5, 0.5)
on_release:
app.root.current= "And Another Screen"
<AnotherScreen>:
name: "Another Screen"
GridLayout:
cols: 2
Button:
text: "Previous Page"
background_color: 0,0,1,1
size_hint: (0.25,1)
on_release:
app.root.current = "menu"
Button:
text: "Exit"
background_color: 1,0,0,1
size_hint: (0.25, 1)
on_release:
exit()
<AndAnotherScreen>:
name: "And Another Screen"
BoxLayout:
orientation: "vertical"
Button:
text: "Next Page"
background_color: 1,1,4,1
size_hint: (0.5, 0.5)
on_release:
app.root.current= "menu"
Button:
text: "Nextest Page"
background_color: 1,4,1,1
size_hint: (0.5, 0.5)
on_release:
app.root.current= "Another Screen"
Firstly I recommend create a global variable for screenmanager or place screenmanager in the MainApp as class property and create a list or a dictionary for the screens:
class MyApp(App):
sm = ScreenManager() # screenmanager
screens = {} # dictionary for the screens, I prefer dictionary because string indexes are more convenient
Then in a build method you can create all screens you need and return the screenmanager as root widget:
class MyApp(App):
sm = ScreenManager() # screenmanager
screens = {} # dictionary for the screens, I prefer dictionary because string indexes are more convenient
def build(self):
self.__create_screens()
MyApp.sm.add_widget(MyApp.screens['main_screen'])
return MyApp.sm
def __create_screens(self):
MyApp.screens['main_screen'] = MainScreen(name='mainscreen')
MyApp.screens['another_screen'] = AnotherScreen(name='another_screen')
MyApp.screens['and_another_screen'] = AndAnotherScreen(name='and_another_screen')
So now you can switch your app's screens easily wherever you want using switch_to method:
MyApp.sm.switch_to(MyApp.screens['another_screen'], direction='left', duration=1)

Linking a Kivy Button to Function

There is something in kivy I do not understand, and am hoping that someone could shed light. I have done a lot of reading in this topic but it just doesn't seem to be connecting in my head.
My issue comes from linking a function to a kivy button.
Right now I am trying to learn how to do a simple function:
def Math():
print 1+1
What I would like to do something more complex:
def Math(a,b):
print a^2 + b^2
Where a and b are input labels from kivy, and upon clicking a button the answer will be printed.
This is what I have so far:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
#######``Logics``#######
class Math(FloatLayout):
def add(self):
print 1+1
#######``Windows``#######
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("GUI_Style.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
This is my kivy language file:
import NoTransition kivy.uix.screenmanager.NoTransition
ScreenManagement:
transition: NoTransition()
MainScreen:
AnotherScreen:
<MainScreen>:
name: "main"
FloatLayout:
Button:
on_release: app.root.current = "other"
text: "Next Screen"
font_size: 50
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.2
pos_hint: {"right":1, "top":1}
<AnotherScreen>:
name: "other"
FloatLayout:
Button:
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.2
text: "add"
pos_hint: {"x":0, "y":0}
on_release: root.add
Button:
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.2
text: "Back Home"
on_release: app.root.current = "main"
pos_hint: {"right":1, "top":1}
<AnotherScreen>:
name: "other"
FloatLayout:
Button:
...
on_release: root.add <-- here *root* evaluates to the top widget in the rule.
Which is an AnotherScreen instance, but it doesn't have an add method.
class Math(FloatLayout):
def add(self):
print 1+1
Here you have declared a Math class by inheriting from the FloatLayout, which is a uix component -a widget-. And you defined a method on this class add. Still you haven't used it. In the kv file you used FloatLayout.
Now in order for you to access a function in kv, most of the time you'll acces it as a method of a uix component, by using either root/self or app, you can also import it e.g:
#: import get_color_from_hex kivy.utils.get_color_from_hex
<ColoredWidget>:
canvas:
Color:
rgba: get_color_from_hex("DCDCDC")
Rectangle:
size: self.size
pos: self.pos
So you can't do it like this:
<AnotherScreen>:
name: "other"
Math:
id: math_layout
Button:
...
on_release: math_layout.add()
or like this:
class AnotherScreen(Screen):
def add(self):
print(1+1)
If you still have problems conserning this topic, i'll be glad to provide more help .

Categories

Resources