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!
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()