Interrupting Kivy Toggle Button State Change on Click - python

I've got a toggle button that I'm using to start and stop a process. When the process is being stopped, I have a popup box that comes up, asking the user to enter a password to confirm they want to end the process.
Only once the correct password is provided do I want the state of the toggle button to change from "down" to "normal" as the process being ended uses the toggle button state to determine if the process should continue running.
The problem I'm having at the moment is that the moment the toggle button is pressed, the state changes from "down" to "normal" and thus ends the process before the password box to authenticate the user is shown.
Any advice on how to interrupt the state change of the toggle button when it is clicked?
EDITED!
main.py:
# import kivy modules
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.properties import ObjectProperty
from kivy.config import Config
Config.set('kivy', 'exit_on_escape', '0')
# import self defined backend class
from ProcessPanel import ProcessPanel
class Automation(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
return AppPanels()
class AppPanels(TabbedPanel):
process_tab = ObjectProperty(None)
process_tab_panel = ObjectProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
if __name__ == '__main__':
Automation().run()
ProcessPanel.py:
# import kivy modules
from kivy.properties import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.properties import ObjectProperty
# import self defined backend classes
from ToolStopWindow import ToolStopWindow
class ProcessPanel(BoxLayout):
process_toggle = ObjectProperty()
def __init__(self, **kwargs):
# inherit BoxLayout Attributes and Methods
super().__init__(**kwargs)
self.num = 0
# create the custom query pop up window
self.TSW = ToolStopWindow(passwd="testPassword",
title_text="Are you sure you want to stop the process?",
sub_title_text="Enter Password to Stop Process...",
external_button=self.process_toggle)
self.TSW.confirm_btn.bind(on_release=self.end_process)
self.process_toggle.bind(_do_press=self.toggle_switch_state())
self.process_schedule = []
# Determine if process has been activated
def toggle_switch_state(self):
if self.process_toggle.state == "normal":
self.process_schedule = Clock.schedule_interval(self.my_process, 5)
self.process_toggle._do_unpress()
else:
self.TSW.open()
def my_process(self, dt):
self.num = self.num + 1
print(self.num)
def end_process(self):
self.process_schedule.cancel()
class StartStopToggle(ToggleButton):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def _do_unpress(self):
if (not self.allow_no_selection and
self.group and self.state == 'down'):
return
self._release_group(self)
self.state = 'normal' if self.state == 'down' else 'down'
ToolStopWindow.py:
import time
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
class ToolStopWindow(Popup):
passwd_box = ObjectProperty()
passwd = ObjectProperty()
confirm_btn = ObjectProperty()
confirm_btn_callback = ObjectProperty()
cancel_btn = ObjectProperty()
title_text = ObjectProperty()
sub_title = ObjectProperty()
sub_title_text = ObjectProperty()
external_button = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.is_true = False
def check_pass(self):
if self.passwd_box.text == self.passwd:
self.sub_title.text = "Password Correct!"
time.sleep(1)
self.external_button._do_unpress()
self.dismiss()
else:
self.is_true = False
self.passwd_box.text = ""
self.sub_title.text = "Invalid Password!"
return
def reset_label_text(self):
if self.sub_title.text != self.sub_title_text:
self.sub_title.text = self.sub_title_text
Automation.kv:
<AppPanels>:
process_tab: process_tab
process_tab_panel: process_tab_panel
id: Tab_Level
size_hint: 1, 1
pos_hint: {'center_x': .5, 'center_y': .5}
do_default_tab: False
tab_pos: 'top_mid'
tab_width: root.width/5
font_size: 24
TabbedPanelItem:
id: process_tab
text: "Process Tab"
ProcessPanel:
id: process_tab_panel
<ProcessPanel>:
orientation: "vertical"
process_toggle: process_toggle
Button:
text: "normal button"
on_release: root.my_process(self)
StartStopToggle:
id: process_toggle
on_state: root.toggle_switch_state()
<StartStopToggle>:
text: "Start Process Schedule"
font_size: 36
<ToolStopWindow>:
id: close_window
auto_dismiss: False
title: root.title_text
size_hint: 0.8, 0.8
passwd_box: passwd_box
confirm_btn: confirm_btn
cancel_btn: cancel_btn
sub_title: sub_title
BoxLayout:
id: main_panel
orientation: "vertical"
Label:
id: sub_title
text: root.sub_title_text
font_size: 36
TextInput:
id: passwd_box
multiline: False
password: True
on_text: root.reset_label_text()
on_text_validate: root.check_pass()
BoxLayout:
id: Buttons
orientation: "horizontal"
Button:
id: confirm_btn
text: "Confirm!"
on_release: root.check_pass()
Button:
id: cancel_btn
text: "Cancel"
on_release: root.dismiss()
What I would like to do is bind the _do_press function to a function in the ProcessPanel class, but I keep getting an attribute error saying "'None type' object has no attribute 'bind'", i assume this is becuase the id's i'm assigning to the UI objects are assigned after init?
Additionally, there i'm a bit stuck on how to cancel the clock i have created to periodically call the my_process function when the correct password is given.
I hope the addition of this example is helpful!

I would probably subclass the ToggleButton to override the _do_press methods do do the work of opening your popup and then
https://github.com/kivy/kivy/blob/master/kivy/uix/behaviors/togglebutton.py#L112-L115
class ConfirmPopup(Popup):
button = ObjectProperty()
password = StringProperty()
def check_password(self, pass):
if self.ids.password.text == self.password:
self.button._do_unpress()
self.dismiss()
class PasswordCheckedToggleButton(ToggleButton):
def _do_press(self):
ConfirmPopup(button=self, password="secret").open()
def _do_unpress(self):
if (not self.allow_no_selection and
self.group and self.state == 'down'):
return
self._release_group(self)
self.state = 'normal' if self.state == 'down' else 'down'
with something like
<ConfirmPopup>:
title: "password!"
BoxLayout:
orientation: "vertical"
TextInput:
id: password
Button:
text: "it’ a me!"
on_release: root.check_password()

Hi I figured out how to use your answer in my code, posted answer below in case anyone finds it useful.
Not sure why but in a stand alone example, the on_request_close feature isnt working, despite the code being the same as the project i'm working on and it working there, spent a while trying to locate the issue but can't find it.... Either way its working for me now so that even when i try to close the app, i am asked for a password to stop the process before the app will close
main.py:
# import kivy modules
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.properties import ObjectProperty
from kivy.config import Config
Config.set('kivy', 'exit_on_escape', '0')
# import self defined backend class
from StartStopToggle import StartStopToggle
from ProcessPanel import ProcessPanel
from ToolStopWindow import ToolStopWindow
class Automation(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
Window.bind(on_request_close=self.on_request_close)
return AppPanels()
def on_request_close(self, *args):
if self.root.process_tab_panel.process_toggle.state == "down":
self.root.process_tab_panel.process_toggle.TSW.opening_object = self
self.root.process_tab_panel.process_toggle.TSW.open()
else:
self.stop()
return True
def do_tsw_function(self):
self.stop()
class AppPanels(TabbedPanel):
process_tab = ObjectProperty(None)
process_tab_panel = ObjectProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
if __name__ == '__main__':
Automation().run()# import kivy modules
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.properties import ObjectProperty
from kivy.config import Config
Config.set('kivy', 'exit_on_escape', '0')
# import self defined backend class
from ProcessPanel import ProcessPanel
class Automation(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
return AppPanels()
class AppPanels(TabbedPanel):
process_tab = ObjectProperty(None)
process_tab_panel = ObjectProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
if __name__ == '__main__':
Automation().run()
ProcessPanel.py:
# import kivy modules
from kivy.properties import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.properties import ObjectProperty
# import self defined backend classes
from ToolStopWindow import ToolStopWindow
class ProcessPanel(BoxLayout):
process_toggle = ObjectProperty()
def __init__(self, **kwargs):
# inherit BoxLayout Attributes and Methods
super().__init__(**kwargs)
self.num = 0
# create the custom query pop up window
self.process_schedule = []
# Determine if process has been activated
def toggle_switch_state(self):
if self.process_toggle.state == "down":
self.process_schedule = Clock.schedule_interval(self.my_process, 5)
self.process_toggle.text = "Stop Process Schedule"
else:
self.process_schedule.cancel()
self.process_toggle.text = "Start Process Schedule"
def my_process(self, dt):
self.num = self.num + 1
print(self.num)
def end_process(self):
self.process_schedule.cancel()
ToolStopWindow.py
import time
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
class ToolStopWindow(Popup):
passwd_box = ObjectProperty()
passwd = ObjectProperty()
confirm_btn = ObjectProperty()
confirm_btn_callback = ObjectProperty()
cancel_btn = ObjectProperty()
title_text = ObjectProperty()
sub_title = ObjectProperty()
sub_title_text = ObjectProperty()
opening_object = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
def check_pass(self):
if self.passwd_box.text == self.passwd:
self.passwd_box.text = ""
self.sub_title.text = "Password Correct!"
time.sleep(1)
self.dismiss()
self.opening_object.do_tsw_function()
else:
self.passwd_box.text = ""
self.sub_title.text = "Invalid Password!"
def reset_label_text(self):
if self.sub_title.text != self.sub_title_text:
self.sub_title.text = self.sub_title_text
StartStopToggle.py:
from kivy.uix.togglebutton import ToggleButton
from ToolStopWindow import ToolStopWindow
class StartStopToggle(ToggleButton):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.TSW = ToolStopWindow(passwd="password",
title_text="Are you sure you want to stop the process?",
sub_title_text="Enter Password to Stop Process...")
def _do_press(self):
self.TSW.opening_object = self
self.TSW.open()
def do_tsw_function(self):
self._do_actual_press()
def _do_actual_press(self):
if (not self.allow_no_selection and
self.group and self.state == 'down'):
return
self._release_group(self)
self.state = 'normal' if self.state == 'down' else 'down'
Automation.kv
<AppPanels>:
process_tab: process_tab
process_tab_panel: process_tab_panel
id: Tab_Level
size_hint: 1, 1
pos_hint: {'center_x': .5, 'center_y': .5}
do_default_tab: False
tab_pos: 'top_mid'
tab_width: root.width/5
font_size: 24
TabbedPanelItem:
id: process_tab
text: "Process Tab"
ProcessPanel:
id: process_tab_panel
<ProcessPanel>:
orientation: "vertical"
process_toggle: process_toggle
Button:
text: "normal button"
on_release: root.my_process(self)
StartStopToggle:
id: process_toggle
on_state: root.toggle_switch_state()
<StartStopToggle>:
text: "Start Query Schedule"
<ToolStopWindow>:
id: close_window
auto_dismiss: False
title: root.title_text
size_hint: 0.8, 0.8
passwd_box: passwd_box
confirm_btn: confirm_btn
cancel_btn: cancel_btn
sub_title: sub_title
BoxLayout:
id: main_panel
orientation: "vertical"
Label:
id: sub_title
text: root.sub_title_text
font_size: 36
TextInput:
id: passwd_box
multiline: False
password: True
on_text: root.reset_label_text()
on_text_validate: root.check_pass()
BoxLayout:
id: Buttons
orientation: "horizontal"
Button:
id: confirm_btn
text: "Confirm!"
on_release: root.check_pass()
Button:
id: cancel_btn
text: "Cancel"
on_release: root.dismiss()

Related

Can't access a modified attribute from another screen class

I create a simple app with Python and KivyMD. When the app running, the login screen asks for input. It was something like this:
from kivy.app import App
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.properties import StringProperty
from kivy.uix.screenmanager import ScreenManager,Screen
class Login(Screen):
def __init__(self, *args, **kwargs):
super(login,self).__init__(*args,**kwargs)
self.user=""
self.username=self.ids.usernamebox.text
self.password=self.ids.passwordbox.text
def checkusername(self)
if self.username=="something" and self.password=="something":
self.user="admin"
else:
self.user="member"
sm.current='nextscreen'
class NextScreen(Screen):
def somefeatures(self):
if Login.user="admin":
print(Login.user)
pass
if Login.user="member":
print(Login.user)
pass
class MyApp(MDApp):
def build(self):
global sm
sm=ScreenManager()
sm.add_widget(Logon(name='login'))
sm.add_widget(NextScreen(name='nextscreen'))
also, the kivy code if it any helps:
<Login>
MDBoxLayout:
orientation: "vertical"
MDTextField:
id: usernamebox
hint_text: "Username"
MDTextField:
id: passwordbox
hint_text: "Password"
MDFillRoundFlatButton:
id: login
text: "Login"
on_release: root.checkusername()
pos_hint: {"center_x":.5}
<NextScreen>
MDBoxLayout:
orientation: "vertical"
MDFlatButton:
text: "test"
on_press: root.somefeatures()
I'm using kivy screen manager to switch between the classes.
What I try to achieve is how to make somefeatures prints the correct value of user after the user logged in
So far I've tried:
initializing the login class l=login() and access with l.user, didn't work because __init__ returns 'user' as empty string
create global user, but still empty string
create setuser and getuser method within class login, but still can't return the value
initializing user outside the class user="" before any other classes, also empty string
Accessing the value of login class like the code above, it says type object Login has no attribute user
Is there is a way to achieve this? I want the value of user is still remembered as long as the app running (or until I make logout button, idk)
Thank you so much.
this is main.py
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import StringProperty, ObjectProperty, NumericProperty, ReferenceListProperty
Builder.load_file('the.kv')
class fscreen(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
theapp.user = ''
def checkusername(self):
self.username = self.ids.password.text
self.password = self.ids.username.text
if self.username == "something" and self.password == "something":
theapp.user = "admin"
else:
theapp.user = "member"
print(self.username, self.password, theapp.user)
theapp.screenm.current = "second screen"
theapp.secscreen.userlabel = 'welcome ' + theapp.user
class secscreen(Widget):
userlabel = StringProperty()
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.userlabel = ''
class thscreen(Widget):
def __init__(self, **kwargs):
supper().__init__(**kwargs)
pass
class theapp(App):
def build(self):
self.screenm = ScreenManager()
self.fscreen = fscreen()
screen = Screen(name = "first screen")
screen.add_widget(self.fscreen)
self.screenm.add_widget(screen)
self.secscreen = secscreen()
screen = Screen(name = "second screen")
screen.add_widget(self.secscreen)
self.screenm.add_widget(screen)
return self.screenm
if __name__ == "__main__":
theapp = theapp()
theapp.run()
the.kv file
<fscreen>
TextInput:
size: root.width*0.2, root.height*0.1
pos: root.width*0.45, root.height*0.5
id: username
hint_text: 'username'
TextInput:
size: root.width*0.2, root.height*0.1
pos: root.width*0.45, root.height*0.4
id: password
hint_text: 'password'
Button:
size: root.width*0.2, root.height*0.1
pos: root.width*0.45, root.height*0.1
text: 'login or something'
on_press: root.checkusername()
<secscreen>
Label:
text: root.userlabel
size: root.width*0.2, root.height*0.1
pos: root.width*0.45, root.height*0.4
ur problem was a class related, u should read more about python classes.

Changing widget from another screen in Kivy

I'm trying to update one widget in through a button in another view at Kivy.
Kivy code:
<MainScreen>:
relatorios : relatorios
despesas : despesas
GridLayout:
size: root.width, root.height
cols : 1
Button:
id: relatorios
text: 'Relatorios'
on_press: root.change_screen(rep = 'Report')
Button:
id: despesas
text: 'Despesa'
on_press: root.change_screen(rep = 'Expend')
<ReportScreen>:
GridLayout:
size: root.width, root.height
cols : 1
Button:
id: semanal
text: 'Semanal'
Button:
id: mensal
text: 'Mensal'
Button:
id: anual
text: 'Anual'
Button:
id: voltar
text: 'Voltar'
on_press: root.change_screen(rep = 'Main')
<ExpendScreen>:
kind_selec1 : kind_select
GridLayout:
size: root.width, root.height
cols : 2
Label:
id: kind
text: 'Tipo de Despesa'
Button:
id: kind_select
text: 'Selecionar'
on_press: root.change_screen(rep = 'Kind')
Label:
id: way
text: 'Meio de Pagamento'
Button:
id: selec_way
text: 'Selecionar'
on_press: root.change_screen(rep = 'Way')
Label:
id: parcelas
text: 'Quantidade de Parcelas'
TextInput:
id: qtdy
Label:
id: value
text: 'Valor Total'
TextInput:
id: qtdy
Button:
id: back
text: 'Voltar'
on_press: root.change_screen(rep = 'Main')
Button:
id: submit
text: 'Registrar'
on_press: root.change_name()
<DropDown1>:
cols : 1
orientation: 'vertical'
<DropDown2>:
cols : 1
orientation: 'vertical'
Python code:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from functools import partial
from functions import Expense
from kivy.properties import ObjectProperty
import kivy
import time
class MainScreen(Widget):
relatorios = ObjectProperty(None)
despesas = ObjectProperty(None)
def change_screen(self,rep):
app_finan_con.screen_manager.current = rep
return
class ReportScreen(Widget):
def change_screen(self,rep):
app_finan_con.screen_manager.current = rep
return
class ExpendScreen(Widget):
kind_selec1 = ObjectProperty()
def __init__(self, **kwargs):
super(ExpendScreen, self).__init__(**kwargs)
def change_screen(self,rep):
app_finan_con.screen_manager.current = rep
def change_name(self):
self.kind_selec1.text= 'porra'
class Dropdown1(BoxLayout):
def __init__(self, **kwargs):
super(Dropdown1, self).__init__(**kwargs)
list = ['Alimentação', 'Transporte', 'Entretenimento','Moradia','Doação']
for item in list:
self.but = Button(text=item)
self.add_widget(self.but)
self.but.bind(on_release=partial(self.It,arg = item))
def It(self,instance,arg):
app_finan_con.screen_manager.current = 'Expend'
ExpendScreen.change_name(ExpendScreen)
class Dropdown2(BoxLayout):
def __init__(self, **kwargs):
super(Dropdown2, self).__init__(**kwargs)
list = ['Crédito', 'Débito', 'Dinheiro']
for item in list:
self.but = Button(text=item)
self.add_widget(self.but)
self.but.bind(on_release=partial(self.It, arg=item))
def It(self,instance,arg):
app_finan_con.screen_manager.current = 'Expend'
class MyApp(App):
def build(self):
self.screen_manager = ScreenManager()
self.mainscreen = MainScreen()
screen = Screen(name='Main')
screen.add_widget(self.mainscreen)
self.screen_manager.add_widget(screen)
self.reportscreen = ReportScreen()
screen = Screen(name='Report')
screen.add_widget(self.reportscreen)
self.screen_manager.add_widget(screen)
self.expendscreen = ExpendScreen()
screen = Screen(name='Expend')
screen.add_widget(self.expendscreen)
self.screen_manager.add_widget(screen)
self.dropdown1 = Dropdown1()
screen = Screen(name='Kind')
screen.add_widget(self.dropdown1)
self.screen_manager.add_widget(screen)
self.dropdown2 = Dropdown2()
screen = Screen(name='Way')
screen.add_widget(self.dropdown2)
self.screen_manager.add_widget(screen)
return self.screen_manager
def change_screen():
app_finan_con.screen_manager.current ='Report'
return
if __name__ == '__main__':
app_finan_con = MyApp()
app_finan_con.run()
The idea is to use screen Dropdown1 to modify screen ExpendScreen through this code:
class Dropdown1(BoxLayout):
def __init__(self, **kwargs):
super(Dropdown1, self).__init__(**kwargs)
list = ['Alimentação', 'Transporte', 'Entretenimento','Moradia','Doação']
for item in list:
self.but = Button(text=item)
self.add_widget(self.but)
self.but.bind(on_release=partial(self.It,arg = item)) (this line activates It method)
def It(self,instance,arg):
app_finan_con.screen_manager.current = 'Expend'
ExpendScreen.change_name(ExpendScreen) (this line activates method in ExpendScreen):
class ExpendScreen(Widget):
kind_selec1 = ObjectProperty()
def __init__(self, **kwargs):
super(ExpendScreen, self).__init__(**kwargs)
def change_screen(self,rep):
app_finan_con.screen_manager.current = rep
def change_name(self):
self.kind_selec1.text= 'porra'
but I get the following error: AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'text'
but when I call the same method from a button in the same screen (ExpendScreen) it works as expected, kivy code:
Button:
id: submit
text: 'Registrar'
on_press: root.change_name()
Image examples:
Can anyone help me?
Your line of code:
ExpendScreen.change_name(ExpendScreen)
is calling an instance method of ExpendScreen without an instance. That won't work. You need to get the instance of ExpendScreen that is part of your gui, and call its change_name method. I haven't tested this, but I think replacing the above line with:
app_finan_con.expendscreen.change_name()
will do what you want.

How to change counter variable from a different class and show on kivy screen python

Please Help me with this new problem.
I want to increment the variable Num of ScreenOne Class from different class ScreenTwo by it's Function Increment.
from kivymd.app import MDApp
from kivmob import TestIds ,RewardedListenerInterface,AdMobRewardedVideoAdListener ,KivMob
from kivymd.theming import ThemeManager
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.lang.builder import Builder
from kivy.uix.label import Label
from kivy.properties import NumericProperty
class Screen_Manager(ScreenManager):
pass
class ScreenOne(Screen):
Num = NumericProperty(0)
def Increase(self):
ScreenTwo().Increase()
class ScreenTwo(ScreenOne):
def Increase(self):
ScreenOne.Num +=1
Builder.load_string("""
#.import MDRaisedButton kivymd.uix.button.MDRaisedButton
<Screen_Manager>:
ScreenOne:
<ScreenOne>:
name:"One"
Label:
text:"Number "+str(root.Num)
pos_hint:{"center_x":0.5,"top":0.7}
MDRaisedButton:
text:"Click here"
pos_hint:{"center_x":0.5,"top":0.6}
on_release:
root.Increase()
""")
class TestApp(MDApp):
def __init__(self,**kwargs):
self.theme_cls.theme_style = "Dark"
super().__init__(**kwargs)
def build(self):
return Screen_Manager()
if __name__ == "__main__":
TestApp().run()
This code gives me error
TypeError: unsupported operand type(s) for +=: 'kivy.properties.NumericProperty' and 'int'
Please Find me a way to increment the Num variable from class ScreenTwo.
and the condition is that it must show on Kivy Screen
Any Help will be Appreciated.
Please Help !!
I assigned id to ScreenManager and added this manager to every Screen
<Screen_Manager>:
id: screen_manager
ScreenOne:
id: one
name: "One"
manager: screen_manager
ScreenTwo:
id: two
name:"Two"
manager: screen_manager
This way I can access other window using manager and id of other screen.
class ScreenOne(Screen):
Num = NumericProperty(0)
def Increase(self):
# change in current Screen - ScreenOne
self.Num += 1
print('One:', self.Num)
# change in other Screen - ScreenTwo
self.manager.ids.two.Num += 2
print('Two:', self.manager.ids.two.Num)
# or
#self.manager.ids.two.Increase()
Full working code which changes Num in other screen and with buttons to see other screens.
from kivymd.app import MDApp
from kivymd.theming import ThemeManager
#from kivmob import TestIds, RewardedListenerInterface, AdMobRewardedVideoAdListener, KivMob
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang.builder import Builder
from kivy.uix.label import Label
from kivy.properties import NumericProperty
class Screen_Manager(ScreenManager):
pass
class ScreenOne(Screen):
Num = NumericProperty(0)
def Increase(self):
# change in current Screen - ScreenOne
self.Num += 1
print('One:', self.Num)
# change in other Screen - ScreenTwo
#self.manager.ids.two.Num += 2
#print('Two:', self.manager.ids.two.Num)
# or
self.manager.ids.two.Increase()
class ScreenTwo(ScreenOne):
def Increase(self):
# change in current Screen - ScreenTwo
self.Num += 2
print('Two:', self.Num)
Builder.load_string("""
#.import MDRaisedButton kivymd.uix.button.MDRaisedButton
<Screen_Manager>:
id: screen_manager
ScreenOne:
id: one
name: "One"
manager: screen_manager
ScreenTwo:
id: two
name:"Two"
manager: screen_manager
<ScreenOne>:
Label:
id: lb1
text: "Number " + str(root.Num)
pos_hint:{"center_x":0.5,"top":0.7}
MDRaisedButton:
text:"Click here"
pos_hint:{"center_x":0.5,"top":0.6}
on_release:
root.Increase()
MDRaisedButton:
text: "Go To Screen Two"
pos_hint:{"center_x":0.5,"top":0.1}
on_release:
root.manager.current = 'Two'
<ScreenTwo>:
Label:
id: lb2
text: "Number " + str(root.Num)
pos_hint:{"center_x":0.5,"top":0.7}
MDRaisedButton:
text:"Click here"
pos_hint:{"center_x":0.5,"top":0.6}
on_release:
root.Increase()
MDRaisedButton:
text: "Go To Screen One"
pos_hint:{"center_x":0.5,"top":0.1}
on_release:
root.manager.current = 'One'
""")
class TestApp(MDApp):
def __init__(self,**kwargs):
self.theme_cls.theme_style = "Dark"
super().__init__(**kwargs)
def build(self):
return Screen_Manager()
if __name__ == "__main__":
TestApp().run()
EDIT: it seems you don't have to assign screen_manager to manager because it has already built-in manager (or you can use built-in parent)
# using `id
#self.parent.ids.two.Increase()
self.manager.ids.two.Increase()
# using `name`
#self.parent.get_screen('Two').Increase()
self.manager.get_screen('Two').Increase()
Using id you have to set ids in Screen_Manager, not in Screen
<Screen_Manager>:
ScreenOne:
id: one
ScreenTwo:
id: two
But using name you can set names in Screen_Manager or in Screen
from kivymd.app import MDApp
from kivymd.theming import ThemeManager
#from kivmob import TestIds, RewardedListenerInterface, AdMobRewardedVideoAdListener, KivMob
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang.builder import Builder
from kivy.uix.label import Label
from kivy.properties import NumericProperty
class Screen_Manager(ScreenManager):
pass
class ScreenOne(Screen):
Num = NumericProperty(0)
def Increase(self):
# change in current Screen - ScreenOne
self.Num += 1
print('One:', self.Num)
# change in other Screen - ScreenTwo
# using `id`
#self.parent.ids.two.Num += 2
#self.parent.ids.two.Increase()
#self.manager.ids.two.Num += 2
#self.manager.ids.two.Increase()
#print('Two:', self.manager.ids.two.Num)
# using `name`
#self.parent.get_screen('Two').Num += 2
#self.parent.get_screen('Two').Increase()
#self.manager.get_screen('Two').Num += 2
self.manager.get_screen('Two').Increase()
class ScreenTwo(ScreenOne):
def Increase(self):
# change in current Screen - ScreenTwo
self.Num += 2
print('Two:', self.Num)
Builder.load_string("""
#.import MDRaisedButton kivymd.uix.button.MDRaisedButton
<Screen_Manager>:
ScreenOne:
id: one
ScreenTwo:
id: two
<ScreenOne>:
name: "One"
other: "Two"
Label:
id: lb1
text: "Number " + str(root.Num)
pos_hint:{"center_x":0.5,"top":0.7}
MDRaisedButton:
text:"Click here"
pos_hint:{"center_x":0.5,"top":0.6}
on_release:
root.Increase()
MDRaisedButton:
text: "Go To Screen " + root.other
pos_hint:{"center_x":0.5,"top":0.1}
on_release:
root.manager.current = root.other
<ScreenTwo>:
name: "Two"
other: "One"
Label:
id: lb2
text: "Number " + str(root.Num)
pos_hint:{"center_x":0.5,"top":0.7}
MDRaisedButton:
text:"Click here"
pos_hint:{"center_x":0.5,"top":0.6}
on_release:
root.Increase()
MDRaisedButton:
text: "Go To Screen " + root.other
pos_hint:{"center_x":0.5,"top":0.1}
on_release:
root.manager.current = root.other
""")
class TestApp(MDApp):
def __init__(self,**kwargs):
self.theme_cls.theme_style = "Dark"
super().__init__(**kwargs)
def build(self):
return Screen_Manager()
if __name__ == "__main__":
TestApp().run()
BTW: you can use manager or parent to set some value(s) in ScreenManager and share it with all Screens.
EDIT:
Using inheritance
class ScreenTwo(ScreenOne):
was missleading.
if ScreenTwo is not a Screen then you can create it without inheritance
class ScreenTwo():
And create it in ScreenOne.__init__. And send ScreenOne (self) to ScreenTwo so ScreenTwo will have access to variables in ScreenOne
class ScreenOne(Screen):
Num = NumericProperty(0)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.second = ScreenTwo(self) # create instance of `ScreenTwo` and send `ScreenOne` (self) as argument
def Increase(self):
self.second.Increase() # run `Increase()` from `ScreenTwo`
class ScreenTwo():
def __init__(self, other):
self.first = other # keep access to `ScreenOne`
def Increase(self):
self.first.Num +=1 # change `Num` in `ScreanOne`
Full example
from kivymd.app import MDApp
from kivymd.theming import ThemeManager
#from kivmob import TestIds, RewardedListenerInterface, AdMobRewardedVideoAdListener, KivMob
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang.builder import Builder
from kivy.uix.label import Label
from kivy.properties import NumericProperty
class Screen_Manager(ScreenManager):
pass
class ScreenOne(Screen):
Num = NumericProperty(0)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.second = ScreenTwo(self) # create instance of `ScreenTwo` and send `ScreenOne` (self) as argument
def Increase(self):
self.second.Increase() # run `Increase()` from `ScreenTwo`
class ScreenTwo():
def __init__(self, other):
self.first = other # keep access to `ScreenOne`
def Increase(self):
self.first.Num +=1 # change `Num` in `ScreanOne`
Builder.load_string("""
#.import MDRaisedButton kivymd.uix.button.MDRaisedButton
<Screen_Manager>:
ScreenOne:
<ScreenOne>:
name: "One"
Label:
id: lb1
text: "Number " + str(root.Num)
pos_hint:{"center_x":0.5,"top":0.7}
MDRaisedButton:
text:"Click here"
pos_hint:{"center_x":0.5,"top":0.6}
on_release:
root.Increase()
""")
class TestApp(MDApp):
def __init__(self,**kwargs):
self.theme_cls.theme_style = "Dark"
super().__init__(**kwargs)
def build(self):
return Screen_Manager()
if __name__ == "__main__":
TestApp().run()

Kivy TabbedPanel. How to call switch_to from another class?

In below code I need the button on the edit_button_tab to switch to edit_input_tab. I really need to switch it that way as I need to switch between predefined classes EditButton and EditInput. This is a part of a bigger program with few Buttons in different location of a layout and I cant define them within <MainTabbedPanel> class. I've tried many ways to call switch_to (example in the quotes) but they didn't work.
CODE
from kivy.animation import Animation
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelStrip
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelHeader
from kivy.factory import Factory
theRoot = """
#:import Factory kivy.factory.Factory
<EditButton>
orientation: 'vertical'
Button:
text: 'Switch to Edit Screen'
on_press: root.change_tab('edit_screen')
<EditInput>
orientation: 'vertical'
TextInput:
<UnCloseableHeader>
color: 0,0,0,1
disabled_color: self.color
# variable tab_width
text: 'tabx'
size_hint_x: None
width: self.texture_size[0] + 40
BoxLayout:
pos: root.pos
size_hint: None, None
size_y: 20
padding: 3
Label:
id: lbl
text: root.text
<MainTabbedPanel#BoxLayout>
size_hint: (1, 1)
default_tab: edit_button_tab
tab_width: 130
FloatLayout:
EditButton:
id: edit_button
EditInput:
id: edit_input
UnCloseableHeader:
id: edit_button_tab
text: 'Edit'
content: edit_button.__self__
UnCloseableHeader:
id: edit_input_tab
text: 'Edit Tab'
content: edit_input.__self__
MainTabbedPanel:
"""
class EditInput(BoxLayout):
def __init__(self, **kwargs):
super(EditInput, self).__init__(**kwargs)
class EditButton(BoxLayout):
def __init__(self, **kwargs):
super(EditButton, self).__init__(**kwargs)
def change_tab(self, tab):
print('TAB', tab)
#call switch method from MainTabbedPanel
'''the way I've tried
mtp = MainTabbedPanel
mtp.switch_to('edit_input_tab')'''
class MainTabbedPanel(TabbedPanel):
def __init__(self, **kwargs):
super(MainTabbedPanel, self).__init__(**kwargs)
def switch(self, tab):
print("SWITCH TO", tab, self.ids.keys())
self.switch_to(self.ids[tab])
class UnCloseableHeader(TabbedPanelHeader):
pass
Factory.register('UnCloseableHeader', cls=UnCloseableHeader)
sm = Builder.load_string(theRoot)
class TabbedPanelApp(App):
def build(self):
return sm
if __name__ == '__main__':
TabbedPanelApp().run()
EDIT
I've tried with below snippet. It prints IDS of MainTabbedPanel but does not change the tabs.
class EditButton(BoxLayout):
def __init__(self, **kwargs):
super(EditButton, self).__init__(**kwargs)
def change_tab(self, tab):
print('TAB', tab)
MainTabbedPanel.tab = tab
MainTabbedPanel()
#call switch method from MainTabbedPanel
'''the way I've tried
mtp = MainTabbedPanel
mtp.switch_to('edit_input_tab')'''
class MainTabbedPanel(TabbedPanel):
tab = ''
def __init__(self, **kwargs):
super(MainTabbedPanel, self).__init__(**kwargs)
self.tabs_showing = True
if self.tab != '':
Clock.schedule_once(self.switch)
def switch(self, dt):
print("SWITCH TO", self.tab, self.ids.keys())
self.switch_to(self.ids[self.tab])
Use App.get_running_app() to get an instance of your app
Use root to get an instance of your root
Snippets
def change_tab(self, tab):
print('TAB', tab)
mtp = App.get_running_app().root
mtp.switch_to(mtp.ids.edit_input_tab)
Notes
In your kv, you defined a Dynamic class,
<MainTabbedPanel#BoxLayout>. It should be a class rule,
<MainTabbedPanel> because in your Python code, you have defined
class MainTabbedPanel(TabbedPanel): i.e. inheritance mismatch and
class type mismatch.

Why is global variable not updated at start, but it's updated at the button press?

I started working on this small calculator using kivy and I have 2 windows. One with the calculator and one with the result. I have 4 buttons for operations and when I press one of them I want to go to the second window where I have a label which is supposed to be the result. I store the result in a global variable.When I change windows the label gets updated with the default value of the global, but if I press my button I can update with the newest one. Can anyone explain why?
import time
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.properties import StringProperty
ResultVariable = -1
class CalculatorWindow(Screen):
numberone = ObjectProperty(None)
numbertwo = ObjectProperty(None)
def addition(self):
global ResultVariable
sm.current="Result"
ResultVariable=int(self.numberone.text)+int(self.numbertwo.text)
ResultWindow().updatevalue()
print(ResultVariable)
def substraction(self):
pass
def multiplication(self):
pass
def division(self):
pass
class ResultWindow(Screen):
result_number = StringProperty()
def __init__(self, **kwargs):
super(ResultWindow, self).__init__(**kwargs)
self.updatevalue()
def updatevalue(self):
print("It has entered the function")
self.result_number = str(ResultVariable)
def restart(self):
self.result_number = str(ResultVariable)
#sm.current="Calculator"
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("graphics.kv")
sm = WindowManager()
screens = [CalculatorWindow(name="Calculator"),ResultWindow(name="Result")]
for screen in screens:
sm.add_widget(screen)
sm.current = "Calculator"
class MyMainApp(App):
def build(self):
return sm
if __name__ == "__main__":
MyMainApp().run()
print("program ended")
And this is the kivy
<CalculatorWindow>:
name: "Calculator"
numberone: firstone
numbertwo: secondone
FloatLayout:
Label:
text: "First number"
pos_hint: {"x":0.0, "top":0.8}
size_hint: 0.5, 0.1
Label:
text: "Second number"
pos_hint: {"x":0.0, "top":0.6}
size_hint: 0.5, 0.1
TextInput:
pos_hint: {"x":0.5, "top":0.8}
size_hint: 0.4, 0.1
id: firstone
multiline: False
TextInput:
pos_hint: {"x":0.5, "top":0.6}
size_hint: 0.4, 0.1
id: secondone
multiline: False
Button:
text: "+"
pos_hint: {"x":0.04, "top":0.35}
size_hint: 0.2, 0.2
on_release:
root.manager.transition.direction = "left"
root.addition()
Button:
text: "-"
pos_hint: {"x":0.28, "top":0.35}
size_hint: 0.2, 0.2
on_release:
root.manager.transition.direction = "left"
root.addition()
Button:
text: "*"
pos_hint: {"x":0.52, "top":0.35}
size_hint: 0.2, 0.2
on_release:
root.manager.transition.direction = "left"
root.addition()
Button:
text: "/"
pos_hint: {"x":0.76, "top":0.35}
size_hint: 0.2, 0.2
on_release:
root.manager.transition.direction = "left"
root.addition()
<ResultWindow>
name: "Result"
resultvariable:solution
FloatLayout:
Label:
id: solution
text: root.result_number
pos_hint: {"x":0.0, "top":0.8}
size_hint: 0.5, 0.1
Button:
text: "Reset"
pos_hint: {"x":0.1, "top":0.6}
size_hint: 0.8, 0.4
on_release:
root.manager.transition.direction = "right"
root.restart()
Using
ResultWindow().updatevalue()
you create new instance of class ResultWindow which has nothing to do with diplayed window which was created in
screens = [CalculatorWindow(name="Calculator"), ResultWindow(name="Result")]
You have to use
screens[1].updatevalue()
import time
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.properties import StringProperty
ResultVariable = -1
class CalculatorWindow(Screen):
numberone = ObjectProperty(None)
numbertwo = ObjectProperty(None)
def addition(self):
global ResultVariable
sm.current = "Result"
ResultVariable = int(self.numberone.text)+int(self.numbertwo.text)
screens[1].updatevalue()
print(ResultVariable)
def substraction(self):
pass
def multiplication(self):
pass
def division(self):
pass
class ResultWindow(Screen):
result_number = StringProperty()
def __init__(self, **kwargs):
#super(ResultWindow, self).__init__(**kwargs)
super().__init__(**kwargs)
self.updatevalue()
def updatevalue(self):
print("It has entered the function")
self.result_number = str(ResultVariable)
def restart(self):
self.result_number = str(ResultVariable)
#sm.current="Calculator"
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("graphics.kv")
sm = WindowManager()
screens = [CalculatorWindow(name="Calculator"), ResultWindow(name="Result")]
for screen in screens:
sm.add_widget(screen)
sm.current = "Calculator"
class MyMainApp(App):
def build(self):
return sm
if __name__ == "__main__":
MyMainApp().run()
EDIT: you can also try to use on_enter() in ResultWindow() which may update every time when kivy shows Screen but you may see how value is changed after scroll screen. I tried to do the same with on_pre_enter() but it didn't work for me. See events for Screen
import time
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.properties import StringProperty
ResultVariable = -1
class CalculatorWindow(Screen):
numberone = ObjectProperty(None)
numbertwo = ObjectProperty(None)
def addition(self):
global ResultVariable
sm.current = "Result"
ResultVariable = int(self.numberone.text)+int(self.numbertwo.text)
#screens[1].updatevalue()
print(ResultVariable)
def substraction(self):
pass
def multiplication(self):
pass
def division(self):
pass
class ResultWindow(Screen):
result_number = StringProperty()
def __init__(self, **kwargs):
#super(ResultWindow, self).__init__(**kwargs)
super().__init__(**kwargs)
self.updatevalue()
def updatevalue(self):
print("It has entered the function")
self.result_number = str(ResultVariable)
def restart(self):
self.result_number = str(ResultVariable)
#sm.current="Calculator"
def on_enter(self):
self.updatevalue()
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("graphics.kv")
sm = WindowManager()
screens = [CalculatorWindow(name="Calculator"), ResultWindow(name="Result")]
for screen in screens:
sm.add_widget(screen)
sm.current = "Calculator"
class MyMainApp(App):
def build(self):
return sm
if __name__ == "__main__":
MyMainApp().run()

Categories