[SOLVED] Kivy, Python – Functions in app not working when using ScreenManager

Issue

My app doesn’t function as soon as I use ScreenManager.

I had a problem with a stopwatch function that was solved here. In short, the stopwatch needed to pause when the pause button opens the popup menu, and to resume when the menu is closed.

The problem returned when the ScreenManager was used, so I am probably not using the ScreenManager correctly, or missing some essential basic knowledge about Kivy or Python to do what I want to do.

Python code:

import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.popup import Popup
from kivy.clock import Clock

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen

root_widget = Builder.load_file('app.kv')


class ExampleWidget(Screen):
    time = NumericProperty(0)
    paused = False
    stop = False

    # Keeping time
    def increment_time(self, interval):

        self.time += .1
        print(self.time)  # To check if stopwatch is running or not

    # Stop should mean that the stopwatch must reset when it starts again.
    # When paused it should resume when it starts again

    def start(self):
        # Keeping time
        self.time = 0
        Clock.schedule_interval(self.increment_time, .1)

    def stop(self):
        Clock.unschedule(self.increment_time)
        print('Stopped')

    def pause(self):

        # Pause stopwatch
        if self.paused:
            Clock.unschedule(self.increment_time)
            print("!!", self.time)  # To make it easier to see if stopwatch actually resumes where it left off
            print('unscheduled')  # Just to confirm and to make it a bit easier to see

        # resume stopwatch
        elif not self.paused:
            Clock.schedule_interval(self.increment_time, .1)


class PopupMenu(Popup):
    example = ExampleWidget()


class Menu(Screen, BoxLayout):
    pass


class WindowManager(ScreenManager):
    pass


class MyApp(App):
    ExampleWidget = ExampleWidget()
    WindowManager = WindowManager()

    def build(self):
        return self.WindowManager


MyApp().run()

.kv file:

#:import Factory kivy.factory.Factory
#: import WipeTransition kivy.uix.screenmanager.WipeTransition
<WindowManager>:
    transition: WipeTransition()
    canvas.before:
        Color:
            rgba: 0, 0, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size
    Menu:
    ExampleWidget:

<[email protected]>
    auto_dismiss: False
    size_hint_y: .8
    size_hint_x: .9
    title: 'Pause'
    example: app.ExampleWidget

    BoxLayout:
        Button:
            text: 'resume'
            on_press: root.example.paused = False
            on_release: root.dismiss(); root.example.pause()
            size: self.size



<Menu>:
    id: menu
    name: "first"
    orientation: 'vertical'
    size: root.width, root.height
    secondary_color: .4,.4,.4,1
    secondary_color2: 0,.7,.7,1
    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size
    Button:
        id: start_button
        text: "Start"
        font_size: 32
        on_release: app.root.current = "second"
        size: root.width/2, root.height/12
        size_hint: None, None
        pos: root.width/4, root.height/2.5
        background_color: 0, 0, 0, 0
        canvas.before:
            Color:
                rgba: menu.secondary_color if start_button.state=='normal' else menu.secondary_color2
            RoundedRectangle:
                size: start_button.width, start_button.height
                pos: start_button.pos

<ExampleWidget>:
    name: 'second'
    GridLayout:
        col: 2
        rows: 3
        size: root.size
        Button:
            text: 'start'
            size: self.size
            on_press: root.start()
        Button:
            text: 'stop'
            size: self.size
            on_press: root.stop()
        Button:
            text: 'Pause menu'
            size: self.size
            on_press: root.paused = True
            on_release: Factory.PopupMenu().open(); root.pause()
        Label:
            text: str(round(root.time))
            size: self.size


Thank you in advance for your help or ideas.

Solution

The problem is that you have references to an ExampleWidget that is not part of your GUI. The code:

class PopupMenu(Popup):
    example = ExampleWidget()

is creating a reference to a new instance of ExampleWidget that is not the one in your GUI. This can be just replaced with:

class PopupMenu(Popup):
    pass

Then, in your kv, if you add an id for the ExampleWidget instance that is actually in your GUI:

<WindowManager>:
    transition: WipeTransition()
    canvas.before:
        Color:
            rgba: 0, 0, 0, 1
        Rectangle:
            pos: self.pos
            size: self.size
    Menu:
    ExampleWidget:
        id: example  # added to enable accessing this instance of ExampleWidget

Now your MyApp class can be:

class MyApp(App):
    WindowManager = WindowManager()

    def build(self):
        self.ExampleWidget = self.WindowManager.ids.example
        return self.WindowManager

Now app.ExampleWidget will be a reference to the ExampleWidget instance that is in your GUI.

Answered By – John Anderson

Answer Checked By – Terry (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published.