Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 53 additions & 38 deletions win10toast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,28 @@
from win32gui import UpdateWindow
from win32gui import WNDCLASS


# ############################################################################
# ########### Classes ##############
# ##################################


class ToastNotifier(object):
"""Create a Windows 10 toast notification.

from: https://github.com/jithurjacob/Windows-10-Toast-Notifications
"""

def __init__(self):
"""Initialize."""
self.hwnd = None
self.nid = None
self._thread = None
self.wc = None
self.hicon = None

def _show_toast(self, title, msg,
icon_path, duration):
"""Notification settings.

:title: notification title
:msg: notification message
:icon_path: path to the .ico file to custom notification
Expand All @@ -71,53 +74,52 @@ def _show_toast(self, title, msg,
message_map = {WM_DESTROY: self.on_destroy, }

# Register the window class.
self.wc = WNDCLASS()
self.hinst = self.wc.hInstance = GetModuleHandle(None)
self.wc.lpszClassName = str("PythonTaskbar") # must be a string
self.wc.lpfnWndProc = message_map # could also specify a wndproc.
try:
self.classAtom = RegisterClass(self.wc)
except:
pass #not sure of this
style = WS_OVERLAPPED | WS_SYSMENU
self.hwnd = CreateWindow(self.classAtom, "Taskbar", style,
0, 0, CW_USEDEFAULT,
CW_USEDEFAULT,
0, 0, self.hinst, None)
if self.wc is None:
self.wc = WNDCLASS()
self.hinst = self.wc.hInstance = GetModuleHandle(None)
self.wc.lpszClassName = str("PythonTaskbar") # must be a string
self.wc.lpfnWndProc = message_map # could also specify a wndproc.
try:
self.classAtom = RegisterClass(self.wc)
except:
pass # not sure of this
style = WS_OVERLAPPED | WS_SYSMENU
if self.hwnd is None:
self.hwnd = CreateWindow(self.classAtom, "Taskbar", style,
0, 0, CW_USEDEFAULT,
CW_USEDEFAULT,
0, 0, self.hinst, None)
UpdateWindow(self.hwnd)

# icon
if icon_path is not None:
icon_path = path.realpath(icon_path)
else:
icon_path = resource_filename(Requirement.parse("win10toast"), "win10toast/data/python.ico")
icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE
try:
hicon = LoadImage(self.hinst, icon_path,
IMAGE_ICON, 0, 0, icon_flags)
except Exception as e:
logging.error("Some trouble with the icon ({}): {}"
.format(icon_path, e))
hicon = LoadIcon(0, IDI_APPLICATION)
if self.hicon is None:
if icon_path is not None:
icon_path = path.realpath(icon_path)
else:
icon_path = resource_filename(Requirement.parse("win10toast"), "win10toast/data/python.ico")
icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE
try:
self.hicon = LoadImage(self.hinst, icon_path,
IMAGE_ICON, 0, 0, icon_flags)
except Exception as e:
logging.error("Some trouble with the icon ({}): {}"
.format(icon_path, e))
self.hicon = LoadIcon(0, IDI_APPLICATION)

# Taskbar icon
flags = NIF_ICON | NIF_MESSAGE | NIF_TIP
nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip")
Shell_NotifyIcon(NIM_ADD, nid)
if self.nid is None:
self.nid = (self.hwnd, 0, flags, WM_USER + 20, self.hicon, "Tooltip")
Shell_NotifyIcon(NIM_ADD, self.nid)
Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO,
WM_USER + 20,
hicon, "Balloon Tooltip", msg, 200,
self.hicon, "Balloon Tooltip", msg, 200,
title))
# take a rest then destroy
sleep(duration)
DestroyWindow(self.hwnd)
UnregisterClass(self.wc.lpszClassName, None)
return None

def show_toast(self, title="Notification", msg="Here comes the message",
icon_path=None, duration=5, threaded=False):
icon_path=None, duration=5, threaded=False):
"""Notification settings.

:title: notification title
:msg: notification message
:icon_path: path to the .ico file to custom notification
Expand All @@ -141,9 +143,23 @@ def notification_active(self):
return True
return False

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.destroy()

def destroy(self):
DestroyWindow(self.hwnd)
UnregisterClass(self.wc.lpszClassName, None)
self.hwnd = None
self.nid = None
self._thread = None
self.wc = None
self.hicon = None

def on_destroy(self, hwnd, msg, wparam, lparam):
"""Clean after notification ended.

:hwnd:
:msg:
:wparam:
Expand All @@ -154,4 +170,3 @@ def on_destroy(self, hwnd, msg, wparam, lparam):
PostQuitMessage(0)

return None

16 changes: 12 additions & 4 deletions win10toast/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,26 @@
# ###### Stand alone program ########
# ###################################
if __name__ == "__main__":
# Example
# Example simple use
toaster = ToastNotifier()
toaster.show_toast(
"Hello World!!!",
"Python is 10 seconds awsm!",
duration=10)
"Python is 10 seconds awsm!")
toaster.destroy()

# Example with case
with toaster:
toaster.show_toast(
"Hello World!!!",
"Python is 10 seconds awsm!")

# Example with threaded
toaster.show_toast(
"Example two",
"This notification is in it's own thread!",
icon_path=None,
duration=5,
threaded=True
)
# Wait for threaded notification to finish
while toaster.notification_active(): time.sleep(0.1)
toaster.destroy()