Linking a Kivy Button to Function - python

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 .

Related

Using a Spinner for changing several Screens in kivy

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.

Kivy Clock and Popup

How do I properly create active properties for switch_id and switch_id_popup so I can create a conditional statement inside of timer_loop with kivy clock?
I got great feedback on a similar question(thanks again eyllanesc), but I am unable to incorporate advice now that popup is involved.
Below is a sketch that illustrates my issue. I have identified all of the areas in question with arrows. Thank you in advance for any help.
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
import time
from kivy.uix.popup import Popup
theRoot = Builder.load_string('''
<CustomPopup1>:
StackLayout:
active2: switch_id_popup.active #<---- switch_id_popup declared here(active2)
Switch: #<---- This switch (switch_id_popup)
id: switch_id_popup
size_hint: .5, .5
active: switch_id_popup.active
Button:
text: 'Close'
on_release: root.dismiss()
size_hint: .5, .5
StackLayout:
active1: switch_id.active #<---- switch_id declared here(active1)
orientation: 'lr-tb'
padding: 10
spacing: 5
Label:
text: "Zone 1 Valve"
size_hint: .25, .1
Switch: #<---- This switch (switch_id)
id: switch_id
size_hint: .25, .1
active: switch_id.active
Button:
text: "Program 1"
on_press: app.open_popup1()
size_hint: .5,.1
''')
class CustomPopup1(Popup):
pass
class theApp(App):
def build(self):
Clock.schedule_interval(self.timer_loop, 2)
return theRoot
def timer_loop(self, dt):
if theRoot.active1 and theRoot.active2: #<---- How do I make this work if switch_id_popup is within a popup?
print("Do something")
else:
print("Do nothing")
def open_popup1(self):
the_popup = CustomPopup1()
the_popup.open()
if __name__ == '__main__':
theApp().run()
As I mentioned in my previous solution, you should consider each class as a black box and expose properties that allow you to obtain and establish values. In the case of Popup, the active property must be part of the class and not the internal StackLayout. On the other hand in the open_popup method you are always creating a new Popup that will be deleted when you close it making the property is not accessible, so we deduce that there must be a Popup object with a larger scope for it must be created outside of theApp class or be a member of it. Lastly, active2 is not a property of theRoot.
Considering the above, the following solution is obtained:
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
import time
from kivy.uix.popup import Popup
theRoot = Builder.load_string('''
<CustomPopup>:
active: switch_id_popup.active
StackLayout:
Switch:
id: switch_id_popup
size_hint: .5, .5
Button:
text: 'Close'
on_release: root.dismiss()
size_hint: .5, .5
StackLayout:
active: switch_id.active
orientation: 'lr-tb'
padding: 10
spacing: 5
Label:
text: "Zone 1 Valve"
size_hint: .25, .1
Switch:
id: switch_id
size_hint: .25, .1
Button:
text: "Program 1"
on_press: app.open_popup()
size_hint: .5,.1
''')
class CustomPopup(Popup):
pass
class TheApp(App):
def build(self):
self.the_popup = CustomPopup()
Clock.schedule_interval(self.timer_loop, 2)
return theRoot
def timer_loop(self, dt):
if self.root.active and self.the_popup.active:
print("Do something")
else:
print("Do nothing")
def open_popup(self):
self.the_popup.open()
if __name__ == '__main__':
TheApp().run()

Setting Global variables Kivy

I am using the Screen Manager to manage several different screens. One screen has two buttons that both lead to another screen, but depending on which button was pressed, I would like a label to display different text on the second screen. Here is my code:
.py
MY_GLOBAL = "test"
class ChooseProgScreen(Screen):
global MY_GLOBAL
def setTitle(self, newTitle):
MY_GLOBAL = newTitle
print(MY_GLOBAL)
class SwitchScreen(Screen):
global MY_GLOBAL
def getTitle(self):
return MY_GLOBAL
class ScreenManagement(ScreenManager):
pass
class MainApp(App):
def build(self):
presentation = Builder.load_file("kivy.kv")
return presentation
.kv
ScreenManagement:
transition: FadeTransition()
HomeScreen:
ChooseProgScreen:
SwitchScreen:
NewProgScreen:
<ChooseProgScreen>:
name: "chooseprog"
FloatLayout:
Button:
text: "test1"
on_release:
root.setTitle("test1")
app.root.current = "switch"
color: 1,1,1,1
font_size: 25
size_hint: 0.15,0.15
pos_hint: {"center_x":.1, "center_y":.9}
Button:
text: "test2"
on_release:
root.setTitle("test2")
app.root.current = "switch"
color: 1,1,1,1
font_size: 25
size_hint: 0.15,0.15
pos_hint: {"center_x":.3, "center_y":.9}
<SwitchScreen>:
name: "switch"
FloatLayout:
Label:
text: root.getTitle()
pos_hint: {"center_x":.1, "center_y":.1}
font_size: 25
In ChooseProgScreen in .kv, when the button is released, I call a method from the .py file that sets the global variable to a new screen and prints it. When you press one of the buttons, the print part works fine, and the global variable prints as the new string, but the SwitchScreen label still shows
"test"
and not
"test1" or "test2"
I think global variables is probably a terrible way of doing this, but I am at a loss for how else to do it using the Screen manager and the kivy language. If someone could help use global variables properly, or suggest a better way to do this, that would be greatly appreciated.
EDIT
Problem was that the screen only updated upon the first load. I added an update method to SwitchScreen:
def update(self):
self.ids.switchtitle.text = self.getTitle()
and updated the SwitchScreen in the .kv file:
<SwitchScreen>:
on_enter:
root.update()
Label:
id: switchtitle
text: root.getTitle()
pos_hint: {"center_x":.1, "center_y":.1}
font_size: 25
Solution
Define a class attribute, MY_GLOBAL of type StringProperty in your root widget, class ScreenManagement().
Update MY_GLOBAL in kv file.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import StringProperty
class ChooseProgScreen(Screen):
pass
class SwitchScreen(Screen):
pass
class ScreenManagement(ScreenManager):
MY_GLOBAL = StringProperty('test')
class MainApp(App):
def build(self):
return Builder.load_file("kivy.kv")
if __name__ == "__main__":
MainApp().run()
kivy.kv
#:kivy 1.10.0
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
ChooseProgScreen:
SwitchScreen:
<ChooseProgScreen>:
name: "chooseprog"
FloatLayout:
Button:
text: "test1"
on_release:
root.manager.MY_GLOBAL = self.text
root.manager.current = "switch"
color: 1,1,1,1
font_size: 25
size_hint: 0.15,0.15
pos_hint: {"center_x":.1, "center_y":.9}
Button:
text: "test2"
on_release:
root.manager.MY_GLOBAL = self.text
root.manager.current = "switch"
color: 1,1,1,1
font_size: 25
size_hint: 0.15,0.15
pos_hint: {"center_x":.3, "center_y":.9}
<SwitchScreen>:
name: "switch"
FloatLayout:
Label:
text: root.manager.MY_GLOBAL
pos_hint: {"center_x":.1, "center_y":.1}
font_size: 25
Output
class ChooseProgScreen(Screen):
def setTitle(self, newTitle):
global MY_GLOBAL
MY_GLOBAL = newTitle
print(MY_GLOBAL)
That should resolve your issue. MY_GLOBAL would technically be available via self.MY_GLOBAL, instead you assigned it to a new variable under the method but didn't assign it to the global variable object.

how to bind two or more events with on_text_validate

I want that on pressing enter in a TextInput widget it should do two things-
change the screen (i am using screen manager)
and search for the keyword
I do know that with on_text_validate we can perform either of the tasks by-
1.root.manager.current='namesomething'
2.root.function_which_has_search_algorithm()
is there anyway with which i can do both the things(changing_the_screen,calling_the_search_function) using on_text_validate or do i have to use some other technique?
here a sample code:
import kivy
kivy.require("1.10.0")
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.lang import Builder
Builder.load_file('screenswitch.kv')
class MainScreen(Screen):
def SelectWord(self):
''' some search
code'''
class OtherScreen(Screen):
pass
sm = ScreenManager()
sm.add_widget(MainScreen(name='main'))
sm.add_widget(OtherScreen(name='other'))
class ScreenSwitchApp(App):
def build(self):
return sm
obj = ScreenSwitchApp()
obj.run()
heres my kivy code:
<MainScreen>:
BoxLayout:
TextInput:
text: "Search your word here"
color: 1,1,1,1
id: search_input
width: 200
size_hint: None, .20
pos_hint: {"center_x": .5, "center_y": 0.5}
multiline: False
on_text_validate: root.SelectWord() # i want this to change screen also
<OtherScreen>:
BoxLayout:
Button:
text: 'back to main screen'
on_press: root.manager.current='main'
A possible solution is to create a new function that calls that 2 functions:
*.py
import kivy
kivy.require("1.10.0")
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.lang import Builder
Builder.load_file('screenswitch.kv')
class MainScreen(Screen):
def some_function(self):
self.SelectWord()
self.manager.current='other'
def SelectWord(self):
print("SelectWord")
class OtherScreen(Screen):
pass
sm = ScreenManager()
sm.add_widget(MainScreen(name='main'))
sm.add_widget(OtherScreen(name='other'))
class ScreenSwitchApp(App):
def build(self):
return sm
obj = ScreenSwitchApp()
obj.run()
*.kv
<MainScreen>:
BoxLayout:
TextInput:
text: "Search your word here"
color: 1,1,1,1
id: search_input
width: 200
size_hint: None, .20
pos_hint: {"center_x": .5, "center_y": 0.5}
multiline: False
on_text_validate: root.some_function() # i want this to change screen also
<OtherScreen>:
BoxLayout:
Button:
text: 'back to main screen'
on_press: root.manager.current='main'
TextInput:
id:qty_inp
size_hint_x:.1
multiline:False
on_text_validate:root.update_price()
on_text_validate:root.update_purchase
Like the above code you can add one more on_text_validate: root.method()

.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)

Categories

Resources