Search code examples
pythonkivykivy-language

Kivy screenmanager: switching screen after timeout with signal


Goal is to move to a settings screen when no button is pressed, text is entered or whatever for a certain time.

In fact, functionality is like a screensaver of some sorts.

code version 1

import signal
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen


class MenuScreen(Screen):
    pass


class SettingsScreen(Screen):
    pass


class wiscApp(App):

    def setscreensaver(self, *args):
        print("switching to settings")
        # --> here I need to switch to the settings screen
        # but this doens't work, bnoth sm and setscreen are not known here
        sm.switch_to(setscreen)

    def resetscreensavertimeout(self):
        print("resetting screensaver timer")
        signal.alarm(10)  # just 5 seconds for debugging

    def build(self):
        sm = ScreenManager()
        setscreen = SettingsScreen(name='settings')
        sm.add_widget(MenuScreen(name='menu'))
        sm.add_widget(setscreen)
        signal.signal(signal.SIGALRM, self.setscreensaver)
        self.resetscreensavertimeout()
        return sm


if __name__ == "__main__":
    wiscApp().run()

and the .kv

<MenuScreen>:
    BoxLayout:
        orientation: 'vertical'
        BoxLayout:
            Button:
                text: "resettimeout"
                on_press: app.resetscreensavertimeout()
            Button:
                text: "do other things"
        Button:
            text: 'settings'
            on_press: root.manager.current = 'settings'

<SettingsScreen>:
    BoxLayout:
        Button:
            text: "stop app"
            on_press: app.stop()
        Button:
            text: 'Back to menu'
            on_press: root.manager.current = 'menu'

This works perfectly up until the calling of sm.switch_to(setscreen) in the setscreensaver function.

I tried the following: code version 2

import signal
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen


class MenuScreen(Screen):
    pass


class SettingsScreen(Screen):
    pass


class wiscApp(App):
    sm = ScreenManager()
    setscreen = SettingsScreen(name='settings')

    def setscreensaver(self, *args):
        print("switching to settings")
        # --> here I need to switch to the settings screen
        # but this doens't work, bnoth sm and setscreen are not known here
        self.sm.switch_to(self.setscreen)

    def resetscreensavertimeout(self):
        print("resetting screensaver timer")
        signal.alarm(10)  # just 5 seconds for debugging

    def build(self):
        self.sm.add_widget(MenuScreen(name='menu'))
        self.sm.add_widget(self.setscreen)
        signal.signal(signal.SIGALRM, self.setscreensaver)
        self.resetscreensavertimeout()
        return self.sm


if __name__ == "__main__":
    wiscApp().run()

But then the settings screen is BLANK! In the first version of the code, I understand by it doens't work: both sm and setscreen are undefined variables in that function. In the second version, I don't understand why the settings screen is blank.

edit *** version 3 of the code***

import signal
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen


class MenuScreen(Screen):
    pass


class SettingsScreen(Screen):
    pass


class wiscApp(App):
    def setscreensaver(self, *args):
        print("switching to settings")
        # --> here I need to switch to the settings screen
        # but this doens't work, bnoth sm and setscreen are not known here
        self.sm.switch_to(self.setscreen)

    def resetscreensavertimeout(self):
        print("resetting screensaver timer")
        signal.alarm(10)  # just 5 seconds for debugging

    def build(self):
        self.sm = ScreenManager()
        self.setscreen = SettingsScreen(name='settings')
        self.sm.add_widget(MenuScreen(name='menu'))
        self.sm.add_widget(self.setscreen)
        signal.signal(signal.SIGALRM, self.setscreensaver)
        self.resetscreensavertimeout()
        return self.sm


if __name__ == "__main__":
    wiscApp().run()

In this version 3, transitioning to the settings screen with the signal works fine, but if I then click the menu button, I get this error (this error does not appear in the other versions of the code):

 kivy.uix.screenmanager.ScreenManagerException: No Screen with name "menu".

So, I have several questions

  1. how do I reset the timer every time a button is pressed, text is entered of whatever, other than defining callbacks for every event (e.g on_press: app.resetscreensavertimeout()) in the .kv code)?
  2. How do I switch to the settings screen in the first version of the code?
  3. Why is the settings screen blank in code version 2?
  4. Why does the error occur in version 3?
  5. Is there another (better) way to code this?

thanks a lot!


Solution

  • Here is a modified version of your code that uses Clock.schedule_once() instead of signal:

    class wiscApp(App):
        def setscreensaver(self, *args):
            print("switching to settings")
            self.resetscreensavertimeout()
            self.sm.current = 'settings'
    
        def resetscreensavertimeout(self, *args):
            print("resetting screensaver timer")
            self.resetEvent.cancel()
            self.resetEvent = Clock.schedule_once(self.setscreensaver, 5)
    
        def build(self):
            self.sm = ScreenManager()
            self.setscreen = SettingsScreen(name='settings')
            self.sm.add_widget(MenuScreen(name='menu'))
            self.sm.add_widget(self.setscreen)
            self.resetEvent = Clock.schedule_once(self.setscreensaver, 5)
            Window.bind(on_touch_down=self.resetscreensavertimeout)
            Window.bind(on_key_down=self.resetscreensavertimeout)
            return self.sm
    

    This also uses Window.bind() to trigger the reset of the timeout whenever a button is pressed or a key is pressed.