Initial commit
This commit is contained in:
parent
4cb1aa28a3
commit
2513fa6f9a
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@ __pycache__/
|
|||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
|
venv/
|
||||||
env/
|
env/
|
||||||
build/
|
build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
|
16
README.md
16
README.md
@ -1,3 +1,17 @@
|
|||||||
# py-reminder
|
# py-reminder
|
||||||
|
|
||||||
Simple application to set reminders in python
|
Simple application to set reminders in python
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
For development, we use the [black code formatter](https://black.readthedocs.io/en/stable/), and pylint as a linter
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install black pylint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install pywin32
|
||||||
|
```
|
||||||
|
11
notify.py
Normal file
11
notify.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
def show_notification(title, msg):
|
||||||
|
if os.name == 'nt':
|
||||||
|
from notify_win import show_toast
|
||||||
|
show_toast(title, msg)
|
||||||
|
else:
|
||||||
|
from gi.repository import Notify
|
||||||
|
Notify.init(title)
|
||||||
|
Notify.Notification.new(msg).show()
|
||||||
|
Notify.uninit()
|
90
notify_win.py
Normal file
90
notify_win.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import win32con
|
||||||
|
import win32gui
|
||||||
|
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
|
class ToastNotifier:
|
||||||
|
def show_toast(self, title, msg, icon_path=None, duration=5):
|
||||||
|
message_map = {win32con.WM_DESTROY: self.on_destroy}
|
||||||
|
# Register the Window class.
|
||||||
|
wc = win32gui.WNDCLASS()
|
||||||
|
wc.lpszClassName = "PythonTaskbar"
|
||||||
|
wc.lpfnWndProc = message_map # could also specify a wndproc.
|
||||||
|
|
||||||
|
classAtom = win32gui.RegisterClass(wc)
|
||||||
|
self.hinst = wc.hInstance = win32gui.GetModuleHandle(None)
|
||||||
|
|
||||||
|
# Create the Window.
|
||||||
|
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
|
||||||
|
self.hwnd = win32gui.CreateWindow(
|
||||||
|
classAtom,
|
||||||
|
"Taskbar",
|
||||||
|
style,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
win32con.CW_USEDEFAULT,
|
||||||
|
win32con.CW_USEDEFAULT,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
self.hinst,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
win32gui.UpdateWindow(self.hwnd)
|
||||||
|
|
||||||
|
self.hicon = self._set_icon(icon_path)
|
||||||
|
|
||||||
|
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
|
||||||
|
nid = (self.hwnd, 0, flags, win32con.WM_USER + 20, self.hicon, "Tooltip")
|
||||||
|
# Add a new notification icon
|
||||||
|
win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
|
||||||
|
win32gui.Shell_NotifyIcon(
|
||||||
|
win32gui.NIM_MODIFY,
|
||||||
|
(
|
||||||
|
self.hwnd,
|
||||||
|
0,
|
||||||
|
win32gui.NIF_INFO,
|
||||||
|
win32con.WM_USER + 20,
|
||||||
|
self.hicon,
|
||||||
|
"Balloon Tooltip",
|
||||||
|
msg,
|
||||||
|
200,
|
||||||
|
title,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
sleep(duration)
|
||||||
|
win32gui.DestroyWindow(self.hwnd)
|
||||||
|
win32gui.UnregisterClass(wc.lpszClassName, None)
|
||||||
|
|
||||||
|
def _set_icon(self, icon_path):
|
||||||
|
# icon
|
||||||
|
if icon_path is not None:
|
||||||
|
icon_path = os.path.realpath(icon_path)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
|
||||||
|
try:
|
||||||
|
hicon = win32gui.LoadImage(
|
||||||
|
self.hinst, icon_path, win32con.IMAGE_ICON, 0, 0, icon_flags
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Some trouble with the icon ({}): {}".format(icon_path, e))
|
||||||
|
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
|
||||||
|
return hicon
|
||||||
|
|
||||||
|
def on_destroy(self, hwnd, msg, wparam, lparam):
|
||||||
|
nid = (hwnd, 0)
|
||||||
|
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
|
||||||
|
win32gui.PostQuitMessage(0)
|
||||||
|
|
||||||
|
|
||||||
|
_notifier = ToastNotifier()
|
||||||
|
def show_toast(title, msg, icon_path=None, duration=5):
|
||||||
|
_notifier.show_toast(title, msg, icon_path=None, duration=5)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
toaster = ToastNotifier()
|
||||||
|
toaster.show_toast("Hello world", "Test")
|
21
run.py
Normal file
21
run.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from schedule import Scheduler
|
||||||
|
from notify import show_notification
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def notify_hello():
|
||||||
|
show_notification("Hello world", "test")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
_scheduler = Scheduler()
|
||||||
|
|
||||||
|
# _scheduler.every(datetime.timedelta(seconds=3), lambda: print("Hello"))
|
||||||
|
# _scheduler.at(datetime.timedelta(seconds=5), lambda: print("Should run only once"))
|
||||||
|
|
||||||
|
_scheduler.at(datetime.datetime(2019, 7, 1, 12, 4), notify_hello)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
_scheduler.run()
|
||||||
|
time.sleep(1)
|
50
schedule.py
Normal file
50
schedule.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Job:
|
||||||
|
def __init__(self, target, interval, times: int):
|
||||||
|
self.target = target
|
||||||
|
self.interval = interval
|
||||||
|
self.next_run = datetime.datetime.now() + interval
|
||||||
|
self.times = times
|
||||||
|
self.last_run = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_run(self):
|
||||||
|
if self.times == 0:
|
||||||
|
return False
|
||||||
|
return datetime.datetime.now() >= self.next_run
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
logging.info(f"Running job {self}")
|
||||||
|
self.last_run = datetime.datetime.now()
|
||||||
|
self.target()
|
||||||
|
self.times -= 1
|
||||||
|
self.next_run = self.last_run + self.interval
|
||||||
|
logging.debug(f"Last run {self.last_run}")
|
||||||
|
logging.debug(f"Next run {self.next_run}")
|
||||||
|
logging.debug(f"Should run {self.should_run}")
|
||||||
|
|
||||||
|
|
||||||
|
class Scheduler:
|
||||||
|
jobs: [Job] = []
|
||||||
|
|
||||||
|
def every(self, interval: datetime.timedelta, target, times=-1):
|
||||||
|
self.jobs.append(Job(target, interval, times))
|
||||||
|
|
||||||
|
def at(self, time, target):
|
||||||
|
interval = time - datetime.datetime.now()
|
||||||
|
self.jobs.append(Job(target, interval, 1))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for job in self.jobs:
|
||||||
|
if job.should_run:
|
||||||
|
job.run()
|
||||||
|
# else:
|
||||||
|
# logging.info("No jobs to run…")
|
Loading…
Reference in New Issue
Block a user