Python:案頭氣泡提示功能實現

來源:互聯網
上載者:User

  在寫案頭軟體時,通常會使用到托盤上的泡泡提示功能,讓我們來看看使用python如何?這個小功能。

一、Linux系統:

  在Linux上,實現一個氣泡提示非常簡單,使用GTK實現的pynotify模組提供了些功能,我的環境是Ubuntu,預設安裝此模組,如果沒有,可從http://home.gna.org/py-notify/下載源檔案編譯安裝一個。實現代碼如下:

#!/usr/bin/python#coding:utf-8import pynotifypynotify.init ("Bubble@Linux")bubble_notify = pynotify.Notification ("Linux上的泡泡提示", "看,比Windows上實現方便多了!")bubble_notify.show ()

效果:

二、Windows下的實現。

  Windows下實現是比較複雜的,沒有pynotify這樣一個模組,找到了一個還算不錯的模組(地址:https://github.com/woodenbrick/gtkPopupNotify,這個類有些文法上的小問題,至少在python2.6下如此,需要修改一下,如下是修改後的代碼),基本可用,代碼如下:

#!/usr/bin/env python# -*- coding: utf-8 -*-#gtkPopupNotify.py## Copyright 2009 Daniel Woodhouse # modified by NickCis 2010 http://github.com/NickCis/gtkPopupNotify# Modifications:#         Added: * Corner support (notifications can be displayed in all corners#                * Use of gtk Stock items or pixbuf to render images in notifications#                * Posibility of use fixed height#                * Posibility of use image as background#                * Not displaying over Windows taskbar(taken from emesene gpl v3)#                * y separation.#                * font description options#                * Callbacks For left, middle and right click##This program is free software: you can redistribute it and/or modify#it under the terms of the GNU Lesser General Public License as published by#the Free Software Foundation, either version 3 of the License, or#(at your option) any later version.##This program is distributed in the hope that it will be useful,#but WITHOUT ANY WARRANTY; without even the implied warranty of#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the#GNU Lesser General Public License for more details.##You should have received a copy of the GNU Lesser General Public License#along with this program.  If not, see <http://www.gnu.org/licenses/>.import osimport gtkimport pangoimport gobject# This code is used only on Windows to get the location on the taskbar# Taken from emesene Notifications (Gpl v3)taskbarOffsety = 0taskbarOffsetx = 0if os.name == "nt":    import ctypes    from ctypes.wintypes import RECT, DWORD    user = ctypes.windll.user32    MONITORINFOF_PRIMARY = 1    HMONITOR = 1    class MONITORINFO(ctypes.Structure):        _fields_ = [            ('cbSize', DWORD),            ('rcMonitor', RECT),            ('rcWork', RECT),            ('dwFlags', DWORD)            ]    taskbarSide = "bottom"    taskbarOffset = 30    info = MONITORINFO()    info.cbSize = ctypes.sizeof(info)    info.dwFlags =  MONITORINFOF_PRIMARY    user.GetMonitorInfoW(HMONITOR, ctypes.byref(info))    if info.rcMonitor.bottom != info.rcWork.bottom:        taskbarOffsety = info.rcMonitor.bottom - info.rcWork.bottom    if info.rcMonitor.top != info.rcWork.top:        taskbarSide = "top"        taskbarOffsety = info.rcWork.top - info.rcMonitor.top    if info.rcMonitor.left != info.rcWork.left:        taskbarSide = "left"        taskbarOffsetx = info.rcWork.left - info.rcMonitor.left    if info.rcMonitor.right != info.rcWork.right:        taskbarSide = "right"        taskbarOffsetx = info.rcMonitor.right - info.rcWork.rightclass NotificationStack:        def __init__(self, size_x=300, size_y=-1, timeout=5, corner=(False, False), sep_y=0):        """        Create a new notification stack.  The recommended way to create Popup instances.          Parameters:            `size_x` : The desired width of the notifications.            `size_y` : The desired minimum height of the notifications. If it isn't set,            or setted to None, the size will automatically adjust            `timeout` : Popup instance will disappear after this timeout if there            is no human intervention. This can be overridden temporarily by passing            a new timout to the new_popup method.            `coner` : 2 Value tuple: (true if left, True if top)            `sep_y` : y distance to separate notifications from each other        """        self.size_x = size_x        self.size_y = -1         if (size_y == None):             pass        else:             size_y        self.timeout = timeout        self.corner = corner        self.sep_y = sep_y        """        Other parameters:        These will take effect for every popup created after the change.                        `edge_offset_y` : distance from the bottom of the screen and            the bottom of the stack.            `edge_offset_x` : distance from the right edge of the screen and            the side of the stack.            `max_popups` : The maximum number of popups to be shown on the screen            at one time.            `bg_color` : if None default is used (usually grey). set with a gtk.gdk.Color.            `bg_pixmap` : Pixmap to use as background of notification. You can set a gtk.gdk.Pixmap            or a path to a image. If none, the color background will be displayed.            `bg_mask` : If a gtk.gdk.pixmap is specified under bg_pixmap, the mask of the pixmap has to be setted here.            `fg_color` : if None default is used (usually black). set with a gtk.gdk.Color.            `show_timeout` : if True, a countdown till destruction will be displayed.            `close_but` : if True, the close button will be displayed.            `fontdesc` : a 3 value Tuple containing the pango.FontDescriptions of the Header, message and counter             (in that order). If a string is suplyed, it will be used for the 3 the same FontDescription.http://doc.stoq.com.br/devel/pygtk/class-pangofontdescription.html        """                self.edge_offset_x = 0        self.edge_offset_y = 0        self.max_popups = 5        self.fg_color = None        self.bg_color = None        self.bg_pixmap = None        self.bg_mask = None        self.show_timeout = False        self.close_but = True        self.fontdesc = ("Sans Bold 14", "Sans 12", "Sans 10")                self._notify_stack = []        self._offset = 0            def new_popup(self, title, message, image=None, leftCb=None, middleCb=None, rightCb=None):        """Create a new Popup instance."""        if len(self._notify_stack) == self.max_popups:            self._notify_stack[0].hide_notification()        self._notify_stack.append(Popup(self, title, message, image, leftCb, middleCb, rightCb))        self._offset += self._notify_stack[-1].y            def destroy_popup_cb(self, popup):        self._notify_stack.remove(popup)        #move popups down if required        offset = 0        for note in self._notify_stack:            offset = note.reposition(offset, self)        self._offset = offset            class Popup(gtk.Window):    def __init__(self, stack, title, message, image, leftCb, middleCb, rightCb):        gtk.Window.__init__(self, type=gtk.WINDOW_POPUP)                self.leftclickCB = leftCb        self.middleclickCB = middleCb        self.rightclickCB = rightCb                        self.set_size_request(stack.size_x, stack.size_y)        self.set_decorated(False)        self.set_deletable(False)        self.set_property("skip-pager-hint", True)        self.set_property("skip-taskbar-hint", True)        self.connect("enter-notify-event", self.on_hover, True)        self.connect("leave-notify-event", self.on_hover, False)        self.set_opacity(0.2)        self.destroy_cb = stack.destroy_popup_cb                if type(stack.fontdesc) == tuple or type(stack.fontdesc) == list:            fontH, fontM, fontC = stack.fontdesc        else:            fontH = fontM = fontC = stack.fontdesc                main_box = gtk.VBox()        header_box = gtk.HBox()        self.header = gtk.Label()        self.header.set_markup("<b>%s</b>" % title)        self.header.set_padding(3, 3)        self.header.set_alignment(0, 0)        try:            self.header.modify_font(pango.FontDescription(fontH))        except Exception, e:            print e        header_box.pack_start(self.header, True, True, 5)        if stack.close_but:            close_button = gtk.Image()                    close_button.set_from_stock(gtk.STOCK_CANCEL, gtk.ICON_SIZE_BUTTON)            close_button.set_padding(3, 3)            close_window = gtk.EventBox()            close_window.set_visible_window(False)            close_window.connect("button-press-event", self.hide_notification)            close_window.add(close_button)            header_box.pack_end(close_window, False, False)        main_box.pack_start(header_box)                body_box = gtk.HBox()        if image is not None:            self.image = gtk.Image()            self.image.set_size_request(70, 70)            self.image.set_alignment(0, 0)            if image in gtk.stock_list_ids():                self.image.set_from_stock(image, gtk.ICON_SIZE_DIALOG)            elif type(image) == gtk.gdk.Pixbuf:                self.image.set_from_pixbuf(image)            else:                self.image.set_from_file(image)            body_box.pack_start(self.image, False, False, 5)        self.message = gtk.Label()        self.message.set_property("wrap", True)        self.message.set_size_request(stack.size_x - 90, -1)        self.message.set_alignment(0, 0)        self.message.set_padding(5, 10)        self.message.set_markup(message)        try:            self.message.modify_font(pango.FontDescription(fontM))        except Exception, e:            print e        self.counter = gtk.Label()        self.counter.set_alignment(1, 1)        self.counter.set_padding(3, 3)        try:            self.counter.modify_font(pango.FontDescription(fontC))        except Exception, e:            print e        self.timeout = stack.timeout                body_box.pack_start(self.message, True, False, 5)        body_box.pack_end(self.counter, False, False, 5)        main_box.pack_start(body_box)        eventbox = gtk.EventBox()        eventbox.set_property('visible-window', False)        eventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK)        eventbox.connect("button_press_event", self.onClick)          eventbox.add(main_box)        self.add(eventbox)        if stack.bg_pixmap is not None:            if not type(stack.bg_pixmap) == gtk.gdk.Pixmap:                stack.bg_pixmap, stack.bg_mask = gtk.gdk.pixbuf_new_from_file(stack.bg_pixmap).render_pixmap_and_mask()            self.set_app_paintable(True)            self.connect_after("realize", self.callbackrealize, stack.bg_pixmap, stack.bg_mask)        elif stack.bg_color is not None:            self.modify_bg(gtk.STATE_NORMAL, stack.bg_color)        if stack.fg_color is not None:            self.message.modify_fg(gtk.STATE_NORMAL, stack.fg_color)            self.header.modify_fg(gtk.STATE_NORMAL, stack.fg_color)            self.counter.modify_fg(gtk.STATE_NORMAL, stack.fg_color)        self.show_timeout = stack.show_timeout        self.hover = False        self.show_all()        self.x, self.y = self.size_request()        #Not displaying over windows bar         if os.name == 'nt':            if stack.corner[0] and taskbarSide == "left":                stack.edge_offset_x += taskbarOffsetx            elif not stack.corner[0] and taskbarSide == 'right':                stack.edge_offset_x += taskbarOffsetx            if stack.corner[1] and taskbarSide == "top":                stack.edge_offset_x += taskbarOffsety            elif not stack.corner[1] and taskbarSide == 'bottom':                stack.edge_offset_x += taskbarOffsety                        if stack.corner[0]:            posx = stack.edge_offset_x        else:            posx = gtk.gdk.screen_width() - self.x - stack.edge_offset_x        sep_y = 0         if (stack._offset == 0):            pass        else:             stack.sep_y        self.y += sep_y        if stack.corner[1]:            posy = stack._offset + stack.edge_offset_y + sep_y        else:            posy = gtk.gdk.screen_height()- self.y - stack._offset - stack.edge_offset_y        self.move(posx, posy)        self.fade_in_timer = gobject.timeout_add(100, self.fade_in)                    def reposition(self, offset, stack):        """Move the notification window down, when an older notification is removed"""        if stack.corner[0]:            posx = stack.edge_offset_x        else:            posx = gtk.gdk.screen_width() - self.x - stack.edge_offset_x        if stack.corner[1]:            posy = offset + stack.edge_offset_y            new_offset = self.y + offset        else:                        new_offset = self.y + offset            posy = gtk.gdk.screen_height() - new_offset - stack.edge_offset_y + stack.sep_y        self.move(posx, posy)        return new_offset        def fade_in(self):        opacity = self.get_opacity()        opacity += 0.15        if opacity >= 1:            self.wait_timer = gobject.timeout_add(1000, self.wait)            return False        self.set_opacity(opacity)        return True                def wait(self):        if not self.hover:            self.timeout -= 1        if self.show_timeout:            self.counter.set_markup(str("<b>%s</b>" % self.timeout))        if self.timeout == 0:            self.fade_out_timer = gobject.timeout_add(100, self.fade_out)            return False        return True              def fade_out(self):        opacity = self.get_opacity()        opacity -= 0.10        if opacity <= 0:            self.in_progress = False            self.hide_notification()            return False        self.set_opacity(opacity)        return True        def on_hover(self, window, event, hover):        """Starts/Stops the notification timer on a mouse in/out event"""        self.hover = hover            def hide_notification(self, *args):        """Destroys the notification and tells the stack to move the        remaining notification windows"""        for timer in ("fade_in_timer", "fade_out_timer", "wait_timer"):            if hasattr(self, timer):                gobject.source_remove(getattr(self, timer))        self.destroy()        self.destroy_cb(self)    def callbackrealize(self, widget, pixmap, mask=False):        #width, height = pixmap.get_size()        #self.resize(width, height)        if mask is not False:            self.shape_combine_mask(mask, 0, 0)        self.window.set_back_pixmap(pixmap, False)        return True    def onClick(self, widget, event):        if event.button == 1 and self.leftclickCB != None:            self.leftclickCB()            self.hide_notification()        if event.button == 2 and self.middleclickCB != None:            self.middleclickCB()            self.hide_notification()        if event.button == 3 and self.rightclickCB != None:            self.rightclickCB()            self.hide_notification()if __name__ == "__main__":    #example usage        def notify_factory():        color = ("green", "blue")        image = "logo1_64.png"        notifier.bg_color = gtk.gdk.Color(color[0])        notifier.fg_color = gtk.gdk.Color(color[1])        notifier.show_timeout = True         notifier.edge_offset_x = 20        notifier.edge_offset_y = 30        notifier.new_popup("Windows上的泡泡提示", "NND,比Linux下複雜多了,效果還不怎麼樣", image=image)        return True    def gtk_main_quit():        print "quitting"        gtk.main_quit()        notifier = NotificationStack(timeout=1)     gobject.timeout_add(4000, notify_factory)    gobject.timeout_add(8000, gtk_main_quit)    gtk.main()

效果如下:

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.