0

I am trying to make the user input the message of the push a notification. I want the user to receive this notification at a certain time(eg. 5 secs interval). When I run the app, it runs, everything else works fine but the notifications do not show and I receive an error only when I close the app. Error:

   File "C:/Users/Dell/PycharmProjects/MantraApp/main.py", line 55, in <module>
     schedule.run_pending()
   File "C:\Users\Dell\AppData\Local\Programs\Python\Python37\lib\site-packages\schedule\__init__.py", line 563, in run_pending
     default_scheduler.run_pending()
   File "C:\Users\Dell\AppData\Local\Programs\Python\Python37\lib\site-packages\schedule\__init__.py", line 94, in run_pending
     self._run_job(job)
   File "C:\Users\Dell\AppData\Local\Programs\Python\Python37\lib\site-packages\schedule\__init__.py", line 147, in _run_job
     ret = job.run()
   File "C:\Users\Dell\AppData\Local\Programs\Python\Python37\lib\site-packages\schedule\__init__.py", line 466, in run
     ret = self.job_func()
   File "C:/Users/Dell/PycharmProjects/MantraApp/main.py", line 49, in show_notification
     plyer.notification.notify(title='test', message=self.root.ids.mantra_text.text)
 AttributeError: 'NoneType' object has no attribute 'ids'

Process finished with exit code 1

Here is my main.py

from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.button import ButtonBehavior
from kivy.uix.image import Image
from kivy.properties import StringProperty, ObjectProperty,NumericProperty
from kivy.uix.popup import Popup
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
import plyer
import time
import schedule



class MainScreen(Screen, FloatLayout):
    mantra_text = ObjectProperty(None)

    def printMantra(self):
        print(self.ids.mantra_text.text)

    def icon_popup(self):
        popup = Popup(title="Profile Icon", content=Popup_Content(), size_hint=(0.8, 0.3))
        popup.open()


class Popup_Content(RelativeLayout):
    pass # contents in kv file. contains grid of profile icons.


class MainApp(App):
    def build(self):
        return MainScreen()

    def set_profile_icon(self, image):
        self.root.ids.profile_icon.source = image.source
        
    def show_notification(self):
        plyer.notification.notify(title='test', message=self.root.ids.mantra_text.text) # mantra_ text is text input id
        


schedule.every(5).seconds.do(MainApp().show_notification)
while 1:
    schedule.run_pending()
    time.sleep(1)
    MainApp().run()
Abacito
  • 141
  • 7
  • 3
    `self.root` being `None` is your ultimate problem, but aside from that, I don't think you've provided a [MCVE] that we could actually diagnose the ultimate cause from. – ShadowRanger Aug 16 '20 at 01:25
  • can you please elaborate on what you meant with self.root. self.root can be used in the MainApp class. How's the question now, can you understand it better now? – Abacito Aug 16 '20 at 01:42

1 Answers1

1

When you call MainApp().show_notification, you are creating a new instance of MainApp (not the one you are running) and calling the show_notification(). Since you have not called the run() method of that new instance of MainApp, it has no root.

I suggest you use kivy.clock to schedule your notifications. Something like this:

class MainApp(App):
    def build(self):
        schedule.every(5).seconds.do(self.show_notification)
        Clock.schedule_interval(lambda dt: schedule.run_pending(), 1)
        return MainScreen()

    def set_profile_icon(self, image):
        self.root.ids.profile_icon.source = image.source
        
    def show_notification(self, *args):
        plyer.notification.notify(title='test', message=self.root.ids.mantra_text.text) # mantra_ text is text input id
        
if __name__ == '__main__':
    MainApp().run()
John Anderson
  • 35,991
  • 4
  • 13
  • 36
  • The thing is I want to call the notifications at specific time like everyday at for example 3pm and schedule allows me to do that. The 5 secs was just an example to test the code Can I do that with kivy.clock? – Abacito Aug 16 '20 at 02:03
  • I've changed it to `schedule.every(5).seconds.do(MainApp().get_running_app().show_notification)` but I still receive the same error – Abacito Aug 16 '20 at 02:05
  • Updated once again. – John Anderson Aug 16 '20 at 02:24
  • You may need to add the `@mainthread` decorator to the `show_notification()` method. – John Anderson Aug 16 '20 at 02:32
  • what does this line: `Clock.schedule_interval(schedule.run_pending, 1)` do because I received an error saying `run_pending takes 0 positional argument but 1 was given` – Abacito Aug 16 '20 at 02:42
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219891/discussion-between-abacito-and-john-anderson). – Abacito Aug 16 '20 at 02:44
  • The `Clock.schedule_interval` schedule a call to its argument every interval (the second argument). In the code, I forgot to handle the `dt` argument tat is added by `Clock.schedule_interval`. I have updated my answer to handle that. – John Anderson Aug 16 '20 at 13:19
  • Can you please explain what lambda dt does – Abacito Aug 16 '20 at 14:09
  • [See this](https://stackoverflow.com/questions/890128/why-are-python-lambdas-useful). – John Anderson Aug 16 '20 at 15:56