Search code examples
pythonwindowstoast

Python - Showing windows toast notifications in borderless windows


I'm writing a small app which provides notifications to the user but they don't seem to be showing while in windowed fullscreen mode (borderless). I don't expect it to work in full-screen but I figured they should be showing in borderless.

I've tried plyer, win10toast,notifu but they all eventually use Shell_NotifyIcon or IUserNotification and fail to show the notification in that mode.

I've been playing around with this snippet which is based on another snippet I found, but no dice.

import win32api, win32con, win32gui
import time

class Taskbar:
    def __init__(self):
        self.visible = 0
        message_map = {
            win32con.WM_DESTROY: self.onDestroy,
        }

        # Register the Window class.
        wc = win32gui.WNDCLASS()
        hinst = wc.hInstance = win32api.GetModuleHandle(None)
        wc.lpszClassName = "PythonTaskbar"

        wc.lpfnWndProc = message_map
        classAtom = win32gui.RegisterClass(wc)

        # Create the Window.
        style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
        exStyle = win32con.WS_EX_TOPMOST # win32con.WS_EX_TOOLWINDOW | win32con.WS_EX_TOPMOST #| win32con.WS_EX_NOACTIVATE
        self.hwnd = win32gui.CreateWindowEx(exStyle, classAtom, "Taskbar Demo", style, \
                                          0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \
                                          0, 0, hinst, None)

        win32gui.UpdateWindow(self.hwnd)

        icon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
        self.setIcon(icon)
        self.show()

    def setIcon(self, hicon, tooltip=None):
        self.hicon = hicon
        self.tooltip = tooltip

    def showBaloon(self):

        flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_INFO
        nid = (self.hwnd, 0, flags, win32con.WM_USER + 20, self.hicon, "", "Balloon Message", 10, "Balloon",
               win32gui.NIIF_INFO)

        win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)

    def show(self):
        """Display the taskbar icon"""
        flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE
        if self.tooltip is not None:
            flags |= win32gui.NIF_TIP
            nid = (self.hwnd, 0, flags, win32con.WM_USER + 20, self.hicon, self.tooltip)
        else:
            nid = (self.hwnd, 0, flags, win32con.WM_USER + 20, self.hicon)

        if self.visible:
            self.hide()
        win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
        self.visible = 1

    def hide(self):
        """Hide the taskbar icon"""
        if self.visible:
            nid = (self.hwnd, 0)
            win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
        self.visible = 0

    def onDestroy(self, hwnd, msg, wparam, lparam):
        self.hide()
        win32gui.PostQuitMessage(0)  # Terminate the app.

t = Taskbar()
time.sleep(3)
t.showBaloon()
time.sleep(3)

I tried using different extended window styles for the notification window (namely WS_EX_TOPMOST, WS_EX_TOOLWINDOW, WS_EX_NOACTIVATE) but it doesn't seem to have any effect.

Is there a way to do this with notifications? Shouldn't it be simple to show since it's not a fullscreen mode?


Solution

  • It seems to me that it's impossible to do with windows notifications. No matter what flags I was pumping in, the notification adds its own flags and that probably messes up the wanted window behavior. Anyway I couldn't find a way to make that work.

    Only options seem to be:

    1. Using CreateWindowEx with the appropriate flags, entails designing, animating, handling behavior of multiple instances, etc..
    2. Same as (1) but using some DirectX/OpenGL library for overlays (probably would work for fullscreen)
    3. NO IDEA about this one but I'm guessing some overlays are done by hooking to a specific process and adding a window. that way it can stay topmost within that process' window because it has permission to be set as foreground?

    For those wondering, I went with (1) or rather Growl. It listens to notification msgs which you can easily send with a lib called gntp. That way I get stylish customizable notifications easily.