Initial commit

This commit is contained in:
Thibaud Gasser 2019-07-01 12:18:34 +02:00
parent 4cb1aa28a3
commit 2513fa6f9a
6 changed files with 188 additions and 1 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ __pycache__/
# Distribution / packaging
.Python
venv/
env/
build/
develop-eggs/

View File

@ -1,3 +1,17 @@
# py-reminder
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
View 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
View 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
View 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
View 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…")