Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User-specified icon support for Windows notifications #11

Merged
merged 2 commits into from
Mar 6, 2014
Merged
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
15 changes: 12 additions & 3 deletions examples/notification/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from os.path import dirname
from os.path import join
from os.path import realpath

import kivy
kivy.require('1.8.0')

Expand All @@ -6,18 +10,23 @@
from kivy.uix.boxlayout import BoxLayout

from plyer import notification
from plyer.utils import platform


class NotificationDemo(BoxLayout):

def do_notify(self, mode='normal'):
import os
kwargs = {'title': self.ids.notification_title.text,
'message': self.ids.notification_text.text}
if mode == 'fancy':
kwargs['app_name'] = "Plyer Notification Example"
kwargs['app_icon'] = os.path.dirname(os.path.realpath(__file__))\
+ '/plyer-icon.png'
if platform == "win":
kwargs['app_icon'] = join(dirname(realpath(__file__)),
'plyer-icon.ico')
kwargs['timeout'] = 4
else:
kwargs['app_icon'] = join(dirname(realpath(__file__)),
'plyer-icon.png')
notification.notify(**kwargs)


Expand Down
Binary file added examples/notification/plyer-icon.ico
Binary file not shown.
7 changes: 5 additions & 2 deletions plyer/facades.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,23 @@ class Notification(object):
'''Notification facade.
'''

def notify(self, title='', message='', app_name='', app_icon=''):
def notify(self, title='', message='', app_name='', app_icon='',
timeout=10):
'''Send a notification.

:param title: Title of the notification
:param message: Message of the notification
:param app_name: Name of the app launching this notification
:param app_icon: Icon to be displayed along with the message
:param timeout: time to display the message for, defaults to 10
:type title: str
:type message: str
:type app_name: str
:type app_icon: str
:type timeout: int
'''
self._notify(title=title, message=message, app_icon=app_icon,
app_name=app_name)
app_name=app_name, timeout=timeout)

# private

Expand Down
52 changes: 36 additions & 16 deletions plyer/platforms/win/libs/balloontip.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,60 @@
import sys
import time

import win32con
import win32gui
from win32api import GetModuleHandle

WS_OVERLAPPED = 0x00000000
WS_SYSMENU = 0x00080000
WM_DESTROY = 2
CW_USEDEFAULT = 8

LR_LOADFROMFILE = 16
LR_DEFAULTSIZE = 0x0040

IMAGE_ICON = 1

IDI_APPLICATION = 32512

WM_USER = 1024

class WindowsBalloonTip:

def __init__(self, title, msg):
message_map = {win32con.WM_DESTROY: self.OnDestroy, }
def __init__(self, title, message, app_name, app_icon, timeout=10):
message_map = {WM_DESTROY: self.OnDestroy, }
# Register the Window class.
wc = win32gui.WNDCLASS()
hinst = wc.hInstance = GetModuleHandle(None)
wc.lpszClassName = "PythonTaskbar"
wc.lpfnWndProc = message_map # could also specify a wndproc.
class_atom = win32gui.RegisterClass(wc)
# Create the Window.
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
style = WS_OVERLAPPED | WS_SYSMENU
self.hwnd = win32gui.CreateWindow(class_atom, "Taskbar", style,
0, 0, win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT, 0, 0,
0, 0, CW_USEDEFAULT,
CW_USEDEFAULT, 0, 0,
hinst, None)
win32gui.UpdateWindow(self.hwnd)
icon_path_name = os.path.abspath(os.path.join(sys.path[0],
if app_icon:
icon_path_name = app_icon
else:
icon_path_name = os.path.abspath(os.path.join(sys.path[0],
"balloontip.ico"))
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE
try:
hicon = win32gui.LoadImage(hinst, icon_path_name,
win32con.IMAGE_ICON, 0, 0, icon_flags)
IMAGE_ICON, 0, 0, icon_flags)
except:
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
hicon = win32gui.LoadIcon(0, IDI_APPLICATION)
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
nid = (self.hwnd, 0, flags, win32con.WM_USER+20, hicon, "tooltip")
nid = (self.hwnd, 0, flags, WM_USER+20, hicon, "tooltip")
win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY,
(self.hwnd, 0, win32gui.NIF_INFO,
win32con.WM_USER+20, hicon,
"Balloon tooltip", msg, 200, title))
WM_USER+20, hicon,
"Balloon tooltip", message, 200, title))
# self.show_balloon(title, msg)
time.sleep(10)
time.sleep(timeout)
win32gui.DestroyWindow(self.hwnd)
win32gui.UnregisterClass(class_atom, hinst)

Expand All @@ -54,5 +69,10 @@ def OnDestroy(self, hwnd, msg, wparam, lparam):
win32gui.PostQuitMessage(0) # Terminate the app.


def balloon_tip(title, msg):
w = WindowsBalloonTip(title, msg)
def balloon_tip(**kwargs):
title = kwargs.get('title', '')
message = kwargs.get('message', '')
app_name = kwargs.get('app_name', '')
app_icon = kwargs.get('app_icon', '')
timeout = kwargs.get('timeout', 10)
w = WindowsBalloonTip(**kwargs)
7 changes: 3 additions & 4 deletions plyer/platforms/win/notification.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import thread
from threading import Thread as thread

from plyer.facades import Notification
from libs.balloontip import balloon_tip
from plyer.platforms.win.libs.balloontip import balloon_tip


class WindowsNotification(Notification):
def _notify(self, **kwargs):
thread.start_new_thread(balloon_tip,
(kwargs.get('title'), kwargs.get('message')))
thread(target=balloon_tip, kwargs=kwargs).start()


def instance():
Expand Down