无法在wxPython中正确使用wx.NotificationMessage

7 投票
2 回答
8487 浏览
提问于 2025-04-17 02:53

我最近升级到了wxPython的开发版本(wxPython 2.9.2.4),因为我需要在我的应用程序中使用wx.NotificationMessage这个功能。我一直在尝试创建一些通知气泡,来响应用户的某些操作,但一直没有成功。我觉得这可能是个bug。在提交这个bug之前,我想先问问邮件列表上的朋友们,看看他们觉得问题可能出在哪里,希望能在我的代码中找到解决方案。

这是我用的代码:

import wx, sys

app = wx.PySimpleApp()

class TestTaskBarIcon(wx.TaskBarIcon):

    def __init__(self):
        wx.TaskBarIcon.__init__(self)
        # create a test icon
        bmp = wx.EmptyBitmap(16, 16)
        dc = wx.MemoryDC(bmp)
        dc.SetBrush(wx.RED_BRUSH)
        dc.Clear()
        dc.SelectObject(wx.NullBitmap)

        testicon = wx.EmptyIcon()
        testicon.CopyFromBitmap(bmp)

        self.SetIcon(testicon)
        self.Bind(wx.EVT_TASKBAR_LEFT_UP, lambda e: (self.RemoveIcon(),sys.exit()))

        wx.NotificationMessage("", "Hello world!").Show()

icon = TestTaskBarIcon()
app.MainLoop()

在我的Windows 7电脑上,这段代码创建了一个小白色的任务栏图标,并弹出了一个写着“Hello World!”的提示框。问题是?消息并没有显示在我的图标上,而是创建了另一个图标,消息被放在那里。你可以看看这个图片:
http://www.pasteall.org/pic/18068

我认为这可能是因为我在第22行没有传递父参数:

wx.NotificationMessage("", "Hello world!").Show()

这是我修改后的代码:

wx.NotificationMessage("", "Hello world!", self).Show()

这里的'self'指的是任务栏图标。当我这样做时,我收到了一个错误:

Traceback (most recent call last):
  File "C:\Python27\testnotificationmessage.py", line 24, in <module>
    icon = TestTaskBarIcon()
  File "C:\Python27\testnotificationmessage.py", line 22, in __init__
    wx.NotificationMessage("", "Hello world!", self).Show()
  File "C:\Python27\lib\site-packages\wx-2.9.2-msw\wx\_misc.py", line 1213, in __init__
    _misc_.NotificationMessage_swiginit(self,_misc_.new_NotificationMessage(*args))
TypeError: in method 'new_NotificationMessage', expected argument 3 of type 'wxWindow *'

这是怎么回事?如果我去掉那个参数,我就得不到想要的结果;如果我加上那个参数,我又会出错!我该怎么用wx.NotificationMessage和wx.TaskBarIcon一起使用呢?

请帮帮我!我希望我提供的信息足够了。如果你需要更多,请评论!

2 个回答

7

TaskBarIcon里,有一个没有文档说明的隐藏方法,叫做ShowBalloon,这个方法只在Windows系统上能用。

来自源代码

def ShowBalloon(*args, **kwargs):
    """
    ShowBalloon(self, String title, String text, unsigned int msec=0, int flags=0) -> bool

    Show a balloon notification (the icon must have been already
    initialized using SetIcon).  Only implemented for Windows.

    title and text are limited to 63 and 255 characters respectively, msec
    is the timeout, in milliseconds, before the balloon disappears (will
    be clamped down to the allowed 10-30s range by Windows if it's outside
    it) and flags can include wxICON_ERROR/INFO/WARNING to show a
    corresponding icon

    Returns True if balloon was shown, False on error (incorrect parameters
    or function unsupported by OS)

    """
    return _windows_.TaskBarIcon_ShowBalloon(*args, **kwargs)

我在Windows上用wxPython 2.9.4.0测试过这个方法,效果很好。

11

我不太建议现在就使用2.9版本。我在试用的时候遇到了一些奇怪的bug。

其实在2.8版本中也能实现相同的功能。我现在用的是我之前找到的一些稍微修改过的代码。

import wx, sys

try:
    import win32gui #, win32con
    WIN32 = True
except:
    WIN32 = False

class BalloonTaskBarIcon(wx.TaskBarIcon):
    """
    Base Taskbar Icon Class
    """
    def __init__(self):
        wx.TaskBarIcon.__init__(self)
        self.icon = None
        self.tooltip = ""

    def ShowBalloon(self, title, text, msec = 0, flags = 0):
        """
        Show Balloon tooltip
         @param title - Title for balloon tooltip
         @param msg   - Balloon tooltip text
         @param msec  - Timeout for balloon tooltip, in milliseconds
         @param flags -  one of wx.ICON_INFORMATION, wx.ICON_WARNING, wx.ICON_ERROR
        """
        if WIN32 and self.IsIconInstalled():
            try:
                self.__SetBalloonTip(self.icon.GetHandle(), title, text, msec, flags)
            except Exception:
                pass # print(e) Silent error

    def __SetBalloonTip(self, hicon, title, msg, msec, flags):

        # translate flags
        infoFlags = 0

        if flags & wx.ICON_INFORMATION:
            infoFlags |= win32gui.NIIF_INFO
        elif flags & wx.ICON_WARNING:
            infoFlags |= win32gui.NIIF_WARNING
        elif flags & wx.ICON_ERROR:
            infoFlags |= win32gui.NIIF_ERROR

        # Show balloon
        lpdata = (self.__GetIconHandle(),   # hWnd
                  99,                       # ID
                  win32gui.NIF_MESSAGE|win32gui.NIF_INFO|win32gui.NIF_ICON, # flags: Combination of NIF_* flags
                  0,                        # CallbackMessage: Message id to be pass to hWnd when processing messages
                  hicon,                    # hIcon: Handle to the icon to be displayed
                  '',                       # Tip: Tooltip text
                  msg,                      # Info: Balloon tooltip text
                  msec,                     # Timeout: Timeout for balloon tooltip, in milliseconds
                  title,                    # InfoTitle: Title for balloon tooltip
                  infoFlags                 # InfoFlags: Combination of NIIF_* flags
                  )
        win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, lpdata)

        self.SetIcon(self.icon, self.tooltip)   # Hack: because we have no access to the real CallbackMessage value

    def __GetIconHandle(self):
        """
        Find the icon window.
        This is ugly but for now there is no way to find this window directly from wx
        """
        if not hasattr(self, "_chwnd"):
            try:
                for handle in wx.GetTopLevelWindows():
                    if handle.GetWindowStyle():
                        continue
                    handle = handle.GetHandle()
                    if len(win32gui.GetWindowText(handle)) == 0:
                        self._chwnd = handle
                        break
                if not hasattr(self, "_chwnd"):
                    raise Exception
            except:
                raise Exception, "Icon window not found"
        return self._chwnd

    def SetIcon(self, icon, tooltip = ""):
        self.icon = icon
        self.tooltip = tooltip
        wx.TaskBarIcon.SetIcon(self, icon, tooltip)

    def RemoveIcon(self):
        self.icon = None
        self.tooltip = ""
        wx.TaskBarIcon.RemoveIcon(self)

# ===================================================================
app = wx.PySimpleApp()

class TestTaskBarIcon(BalloonTaskBarIcon):

    def __init__(self):
        wx.TaskBarIcon.__init__(self)
        # create a test icon
        bmp = wx.EmptyBitmap(16, 16)
        dc = wx.MemoryDC(bmp)
        dc.SetBrush(wx.RED_BRUSH)
        dc.Clear()
        dc.SelectObject(wx.NullBitmap)

        testicon = wx.EmptyIcon()
        testicon.CopyFromBitmap(bmp)

        self.SetIcon(testicon)
        self.Bind(wx.EVT_TASKBAR_LEFT_UP, lambda e: (self.RemoveIcon(),sys.exit()))

        self.ShowBalloon("", "Hello world!")

icon = TestTaskBarIcon()
app.MainLoop()

撰写回答