python实现桌面气泡提示功能


Posted in Python onJuly 29, 2019

在写桌面软件时,通常会使用到托盘上的泡泡提示功能,让我们来看看使用python如何实现这个小功能。

一、Linux系统

在Linux上,实现一个气泡提示非常简单,使用GTK实现的pynotify模块提供了些功能,我的环境是Ubuntu,默认安装此模块,如果没有,下载源文件编译安装一个。实现代码如下:

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

效果:

python实现桌面气泡提示功能

二、Windows下的实现

Windows下实现是比较复杂的,没有pynotify这样一个模块,找到了一个还算不错的模块(地址),这个类有些语法上的小问题,至少在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 os
import gtk
import pango
import gobject
 
# This code is used only on Windows to get the location on the taskbar
# Taken from emesene Notifications (Gpl v3)
taskbarOffsety = 0
taskbarOffsetx = 0
if 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.right
 
class 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()

效果如下:

python实现桌面气泡提示功能

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python中replace方法实例分析
Aug 20 Python
Python基于sftp及rsa密匙实现远程拷贝文件的方法
Sep 21 Python
Python 类的继承实例详解
Mar 25 Python
Python基于tkinter模块实现的改名小工具示例
Jul 27 Python
Python连接phoenix的方法示例
Sep 29 Python
Python输出由1,2,3,4组成的互不相同且无重复的三位数
Feb 01 Python
对Python 简单串口收发GUI界面的实例详解
Jun 12 Python
Django和Flask框架优缺点对比
Oct 24 Python
通过实例解析Python调用json模块
Dec 11 Python
tensorflow实现残差网络方式(mnist数据集)
May 26 Python
如何完美的建立一个python项目
Oct 09 Python
Python实现区域填充的示例代码
Feb 03 Python
pycharm设置鼠标悬停查看方法设置
Jul 29 #Python
django rest framework vue 实现用户登录详解
Jul 29 #Python
python实现倒计时小工具
Jul 29 #Python
django rest framework 实现用户登录认证详解
Jul 29 #Python
pycharm重命名文件的方法步骤
Jul 29 #Python
PyQt5实现暗黑风格的计时器
Jul 29 #Python
Python Django 实现简单注册功能过程详解
Jul 29 #Python
You might like
PHP初学入门
2006/11/19 PHP
php下一个阿拉伯数字转中文数字的函数
2007/07/16 PHP
Wordpress php 分页代码
2009/10/21 PHP
Laravel 4 初级教程之Pages、表单验证
2014/10/30 PHP
Yii2学习笔记之汉化yii设置表单的描述(属性标签attributeLabels)
2017/02/07 PHP
浅析jquery ajax异步调用方法中不能给全局变量赋值的原因及解决方法
2014/01/10 Javascript
js查找某元素中的所有图片地址的方法
2014/01/16 Javascript
Android中的jQuery:AQuery简介
2014/05/06 Javascript
css与javascript跨浏览器兼容性总结
2014/09/15 Javascript
jQuery中unbind()方法用法实例
2015/01/19 Javascript
jQuery模拟原生态App上拉刷新下拉加载更多页面及原理
2015/08/10 Javascript
内容滑动切换效果jquery.hwSlide.js插件封装
2016/07/07 Javascript
jQuery插件Validation表单验证详解
2018/05/26 jQuery
用npm安装vue和vue-cli,并使用webpack创建项目的方法
2018/09/28 Javascript
详解从vue-loader源码分析CSS Scoped的实现
2019/09/23 Javascript
JS实现点击下拉列表文本框中出现对应的网址,点击跳转按钮实现跳转
2019/11/25 Javascript
JavaScript中的this基本问题实例小结
2020/03/09 Javascript
Node.js中出现未捕获异常的处理方法
2020/06/29 Javascript
[40:04]Secret vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.23
2019/09/05 DOTA
python在多玩图片上下载妹子图的实现代码
2013/08/13 Python
tensorflow识别自己手写数字
2018/03/14 Python
python将txt文件读入为np.array的方法
2018/10/30 Python
python http基本验证方法
2018/12/26 Python
VSCode基础使用与VSCode调试python程序入门的图文教程
2020/03/30 Python
HTML5实现表单自动验证功能实例代码
2017/01/11 HTML / CSS
Spanx塑身衣官网:美国知名内衣品牌
2017/01/11 全球购物
Lululemon英国官网:加拿大瑜伽服装品牌
2019/01/14 全球购物
Perfume’s Club法国站:购买香水和化妆品
2019/05/02 全球购物
三星法国官方网站:Samsung法国
2019/10/31 全球购物
C#里面如何倒序排列一个数组的元素?
2013/06/21 面试题
外科实习自我鉴定
2013/10/06 职场文书
法律进社区实施方案
2014/03/21 职场文书
2014法院干警廉洁警示教育思想汇报
2014/09/13 职场文书
毕业证丢失证明范本
2014/09/20 职场文书
2014年单位法制宣传日活动总结
2014/11/01 职场文书
助学感谢信范文
2015/01/21 职场文书