How to make a countdown timer which is button controlled and sends out a notification on end

Viewed 174

Hi everybody and anybody seeing this thanks so much for taking the time to look over my question.

I have a program which is using kivy, kivymd, and pyobjus to currently house multiple windows and a notification that DOES go out on mac now! Only problem is I need the notification to go out after a twenty minute timer ends not right at the start. I'm completely lost on what to do. Hopefully somebody can provide anything at all? Code is below, cheers!

from kivymd.app import MDApp
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.picker import MDDatePicker
from kivymd.uix.picker import MDThemePicker
from kivy.config import Config
from kivy.uix.boxlayout import BoxLayout
from kivymd.theming import ThemableBehavior
from kivymd.uix.list import MDList
from kivy.properties import ObjectProperty
from kivymd.uix.navigationdrawer import MDNavigationDrawer
from kivy.properties import StringProperty, ListProperty
from kivy.animation import Animation
from kivy.properties import StringProperty, NumericProperty
from kivy.lang import Builder
from kivy.core.window import Window
from plyer import notification
import threading
from pyobjus import autoclass, objc_str
from pyobjus.dylib_manager import load_framework, INCLUDE
import time
import os
import subprocess
from kivy.clock import Clock
from kivy.uix.label import Label
from datetime import datetime, date, time
 

load_framework(INCLUDE.AppKit)


Config.set('graphics', 'width', '1600')
Config.set('graphics', 'height', '1200')


KV = '''
<Box@BoxLayout>:
    bg: .65, .48, .35, 1 
       
             

BoxLayout:
    Rectangle:    
        size: 1600, 1200

    Box:
        bg: app.theme_cls.bg_light
    Box:
        bg: app.theme_cls.bg_normal
    Box:
        bg: app.theme_cls.bg_dark
    Box:
        bg: app.theme_cls.bg_darkest
            
'''
            
screen_helper = """
ScreenManager:
    MenuScreen:
    HomeScreen:
    BreakScreen:
    SettingsScreen
        
<MenuScreen>:
    name: 'menu'
    
  
        
    MDLabel:
        rectangle:
        background_color: .65, .48, .35, 1           
        size: 300, 700
        pos_hint: {'center_x':0.1, 'center_y':0.0}
        
                
    MDLabel:
        text: "Welcome to"
        font_style: 'H2'
        size:500, 500
        pos_hint: {'center_x':0.95,'center_y':0.84}
        
    MDLabel:
        text: "LEAF"
        font_style: 'H3'
        size:500, 500
        pos_hint: {'center_x':1.045,'center_y':0.7}
        
        
    MDFlatButton:
        text: 'Calendar'
        font_style: 'H6'
        pos_hint: {'center_x':0.091,'center_y':0.65}
        on_release: app.show_date_picker()
            
            
    MDFlatButton:
        text: 'Home'
        font_style: 'H6'
        pos_hint: {'center_x':0.076,'center_y':0.57}
        on_press: root.manager.current = 'home'
        
    MDFlatButton:
        text: 'Settings'
        font_style: 'H6'
        pos_hint: {'center_x':0.09,'center_y':0.49}
        on_press: root.manager.current = 'settings'
        
            
    MDRectangleFlatButton:
        text: 'Continue'
        font_style: 'H6'
        pos_hint: {'center_x':0.61,'center_y':0.345}
        on_press: root.manager.current = 'home'
   


    
<HomeScreen>:
    name: 'home'    
    
    Image
        source: '/Users/oats/Downloads/LEAF 1.0/Assets/MenuScreen.png'
        pos_hint: {'center_x':0.5,'center_y':0.8}
        size_hint_y: None 
        height: dp(60) 
        

        
    MDToolbar:
        id: toolbar
        title: "Home"
        pos_hint: {"top": 1}
        elevation: 5
        left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
        
    Widget:

        MDNavigationDrawer:
            id: nav_drawer
    
            ContentNavigationDrawer:
                orientation: 'vertical'
                padding: "8dp"
                spacing: "8dp"
                

                MDFlatButton:
                    text: 'Return to menu'
                    font_style: 'Subtitle1'
                    on_release: root.manager.current = 'menu'

                MDFlatButton:
                    text: 'Open Calendar'
                    font_style: 'Subtitle1'
                    on_release: app.show_date_picker()
                    
                MDFlatButton:
                    text: 'Open Settings'
                    font_style: 'Subtitle1'
                    on_press: root.manager.current = 'settings'
                    
                MDLabel:
                    size_hint_y: None
                    text: '. '
        
                MDLabel:
                    size_hint_y: None
                    text: ' '
        
                MDLabel:
                    size_hint_y: None
                    text: ' '
        
                MDLabel:
                    size_hint_y: None
                    text: ' '
         
                MDLabel:
                    size_hint_y: None
                    text: ' '

                MDLabel:
                    size_hint_y: None
                    text: ' '



         
            
    
<BreakScreen>:
    name: 'break'
    
<SettingsScreen>:
    name: 'settings'
    
    
    MDFlatButton:
        text: 'Return to menu'
        font_style: 'H6'
        pos_hint: {'center_x':0.1,'center_y':0.05}
        on_press: root.manager.current = 'menu'
        
    MDFloatingActionButton:
        icon: 'palette'
        md_bg_color: 0, 0.039, 0.867, 0.557
        pos_hint: {'center_x':0.9,'center_y':0.5}
        on_release: app.show_theme_picker() 
        
    MDFloatingActionButton:
        icon: 'moon-waning-crescent'
        theme_text_color: "Custom"
        md_bg_color: 0, 0.039, 0.867, 0.557
        pos_hint: {'center_x':0.9,'center_y':0.3}
        on_press: self.theme_cls.theme_style = "Dark"  # "Light"
        
    MDFloatingActionButton:
        icon: 'MDFloatingActionButton'
        icon: 'lightbulb'
        pos_hint: {'center_x':0.9,'center_y':0.1}
        on_press: self.theme_cls.theme_style = "Light"  # "Dark"
    

        
"""  

class MenuScreen(Screen):
    pass


class HomeScreen(Screen):
    pass


class BreakScreen(Screen):
    pass

class SettingsScreen(Screen):
    pass

class ContentNavigationDrawer(BoxLayout):
    pass

class DrawerList(ThemableBehavior, MDList):
    pass

class IncrediblyCrudeClock(Label):
    a = NumericProperty(1200)  # seconds

    def start(self):
        Animation.cancel_all(self)  # stop any current animations
        self.anim = Animation(a=0, duration=self.a)
        def finish_callback(animation, incr_crude_clock):
            incr_crude_clock.text = "FINISHED"
        self.anim.bind(on_complete=finish_callback)
        self.anim.start(self)

    def on_a(self, instance, value):
        self.text = str(round(value, 1))




    
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(HomeScreen(name='profile'))
sm.add_widget(BreakScreen(name='upload'))
sm.add_widget(SettingsScreen(name='settings'))



class LeafApp(MDApp):

    
    def show_date_picker(self):
        date_dialog = MDDatePicker()
        date_dialog.open()

    def show_theme_picker(self):
        theme_dialog = MDThemePicker()
        theme_dialog.open()



   
    def notify(title, text):   
        os.system("""
              osascript -e 'display notification "{}" with title "{}"'
              """.format(text, title))
              
    notify("LEAF", "Hey, its been twenty minutes, take a break?")
        
        
    def timer_loop(self):
            
            time.sleep(5)
            

    
    def build(self):
        crudeclock = IncrediblyCrudeClock()
        crudeclock.start()
        screen = Builder.load_string(screen_helper)
        return screen
    

    

    
LeafApp().run()
1 Answers

Rather than using Animation as a timer, try using Clock.schedule_once() like this:

def build(self):
    Clock.schedule_once(partial(LeafApp.notify, "LEAF", "Hey, its been twenty minutes, take a break?"), 1200)
    screen = Builder.load_string(screen_helper)
    return screen

with a slight change to the notify() method:

@staticmethod
def notify(title, text, *args):
    os.system("""
          osascript -e 'display notification "{}" with title "{}"'
          """.format(text, title))
    # schedule another notify
    Clock.schedule_once(partial(LeafApp.notify, "LEAF", "Hey, its been another twenty minutes, take a break?"), 1200)

The *args handles the argument (dt) that Clock.schedule_once() adds to the notify() call. Then the notify() method schedules another call to itself in another 20 minutes. The @staticmethod decorator indicates that the notify() method is a static method that can be called without an actual instance of the LeafApp. The partial() allows you to define a method call and its arguments.

and remove the line:

notify("LEAF", "Hey, its been twenty minutes, take a break?")

which is causing the notification at the start.

Related