设为首页 收藏本站
查看: 902|回复: 0

[经验分享] Python:桌面气泡提示功能实现

[复制链接]

尚未签到

发表于 2017-5-3 11:22:17 | 显示全部楼层 |阅读模式
  在写桌面软件时,通常会使用到托盘上的泡泡提示功能,让我们来看看使用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 ()  效果:
DSC0000.png

  二、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 ctypesfrom ctypes.wintypes import RECT, DWORDuser = ctypes.windll.user32MONITORINFOF_PRIMARY = 1HMONITOR = 1class MONITORINFO(ctypes.Structure):_fields_ = [('cbSize', DWORD),('rcMonitor', RECT),('rcWork', RECT),('dwFlags', DWORD)]taskbarSide = "bottom"taskbarOffset = 30info = MONITORINFO()info.cbSize = ctypes.sizeof(info)info.dwFlags =  MONITORINFOF_PRIMARYuser.GetMonitorInfoW(HMONITOR, ctypes.byref(info))if info.rcMonitor.bottom != info.rcWork.bottom:taskbarOffsety = info.rcMonitor.bottom - info.rcWork.bottomif info.rcMonitor.top != info.rcWork.top:taskbarSide = "top"taskbarOffsety = info.rcWork.top - info.rcMonitor.topif info.rcMonitor.left != info.rcWork.left:taskbarSide = "left"taskbarOffsetx = info.rcWork.left - info.rcMonitor.leftif 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 thereis no human intervention. This can be overridden temporarily by passinga 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_xself.size_y = -1 if (size_y == None): passelse:size_yself.timeout = timeoutself.corner = cornerself.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 andthe bottom of the stack.`edge_offset_x` : distance from the right edge of the screen andthe side of the stack.`max_popups` : The maximum number of popups to be shown on the screenat 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.Pixmapor 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 = 0self.edge_offset_y = 0self.max_popups = 5self.fg_color = Noneself.bg_color = Noneself.bg_pixmap = Noneself.bg_mask = Noneself.show_timeout = Falseself.close_but = Trueself.fontdesc = ("Sans Bold 14", "Sans 12", "Sans 10")self._notify_stack = []self._offset = 0def 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].ydef destroy_popup_cb(self, popup):self._notify_stack.remove(popup)#move popups down if requiredoffset = 0for note in self._notify_stack:offset = note.reposition(offset, self)self._offset = offsetclass Popup(gtk.Window):def __init__(self, stack, title, message, image, leftCb, middleCb, rightCb):gtk.Window.__init__(self, type=gtk.WINDOW_POPUP)self.leftclickCB = leftCbself.middleclickCB = middleCbself.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_cbif type(stack.fontdesc) == tuple or type(stack.fontdesc) == list:fontH, fontM, fontC = stack.fontdescelse:fontH = fontM = fontC = stack.fontdescmain_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 eheader_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 eself.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 eself.timeout = stack.timeoutbody_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_timeoutself.hover = Falseself.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 += taskbarOffsetxelif not stack.corner[0] and taskbarSide == 'right':stack.edge_offset_x += taskbarOffsetxif stack.corner[1] and taskbarSide == "top":stack.edge_offset_x += taskbarOffsetyelif not stack.corner[1] and taskbarSide == 'bottom':stack.edge_offset_x += taskbarOffsetyif stack.corner[0]:posx = stack.edge_offset_xelse:posx = gtk.gdk.screen_width() - self.x - stack.edge_offset_xsep_y = 0 if (stack._offset == 0):passelse:stack.sep_yself.y += sep_yif stack.corner[1]:posy = stack._offset + stack.edge_offset_y + sep_yelse:posy = gtk.gdk.screen_height()- self.y - stack._offset - stack.edge_offset_yself.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_xelse:posx = gtk.gdk.screen_width() - self.x - stack.edge_offset_xif stack.corner[1]:posy = offset + stack.edge_offset_ynew_offset = self.y + offsetelse:            new_offset = self.y + offsetposy = gtk.gdk.screen_height() - new_offset - stack.edge_offset_y + stack.sep_yself.move(posx, posy)return new_offsetdef fade_in(self):opacity = self.get_opacity()opacity += 0.15if opacity >= 1:self.wait_timer = gobject.timeout_add(1000, self.wait)return Falseself.set_opacity(opacity)return Truedef wait(self):if not self.hover:self.timeout -= 1if 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 Falsereturn Truedef fade_out(self):opacity = self.get_opacity()opacity -= 0.10if opacity <= 0:self.in_progress = Falseself.hide_notification()return Falseself.set_opacity(opacity)return Truedef on_hover(self, window, event, hover):"""Starts/Stops the notification timer on a mouse in/out event"""self.hover = hoverdef hide_notification(self, *args):"""Destroys the notification and tells the stack to move theremaining 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 Truedef 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 usagedef 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 = 20notifier.edge_offset_y = 30notifier.new_popup("Windows上的泡泡提示", "NND,比Linux下复杂多了,效果还不怎么样", image=image)return Truedef 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()
效果如下:
DSC0001.png

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-372519-1-1.html 上篇帖子: 抓取黄历,Python写的一个脚本 下篇帖子: python iterator的一些具体应用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表