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