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 文件和路径操作函数小结
Nov 23 Python
python 从远程服务器下载日志文件的程序
Feb 10 Python
Python实现将n个点均匀地分布在球面上的方法
Mar 12 Python
Python实现字符串格式化的方法小结
Feb 20 Python
python中numpy.zeros(np.zeros)的使用方法
Nov 07 Python
使用Python来开发微信功能
Jun 13 Python
Python列表与元组的异同详解
Jul 02 Python
8段用于数据清洗Python代码(小结)
Oct 31 Python
Python3 中sorted() 函数的用法
Mar 24 Python
pycharm设置默认的UTF-8编码模式的方法详解
Jun 01 Python
django创建css文件夹的具体方法
Jul 31 Python
Python实战之大鱼吃小鱼游戏的实现
Apr 01 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与已存在的Java应用程序集成
2006/10/09 PHP
新浪微博API开发简介之用户授权(PHP基础篇)
2011/09/25 PHP
ThinkPHP使用心得分享-分页类Page的用法
2014/05/15 PHP
PHP使用redis实现统计缓存mysql压力的方法
2015/11/14 PHP
php flush无效,IIS7下php实时输出的方法
2016/08/25 PHP
PHP getID3类的使用方法学习笔记【附getID3源码下载】
2019/10/18 PHP
laravel 解决crontab不执行的问题
2019/10/22 PHP
Laravel 集成微信用户登录和绑定的实现
2019/12/27 PHP
javascript 出生日期和身份证判断大全
2008/11/13 Javascript
js面向对象 多种创建对象方法小结
2012/05/21 Javascript
关于js注册事件的常用方法
2013/04/03 Javascript
javascript中RegExp保留小数点后几位数的方法分享
2013/08/13 Javascript
JavaScript数据库TaffyDB用法实例分析
2015/07/27 Javascript
jquery+php实现滚动的数字特效
2015/11/29 Javascript
jQuery基于函数重载实现自定义Alert函数样式的方法
2016/07/27 Javascript
微信小程序 开发之快递查询功能的实现
2017/01/09 Javascript
Vue的事件响应式进度条组件实例详解
2018/02/04 Javascript
微信小程序自定义组件实现tabs选项卡功能
2018/07/14 Javascript
微信小程序点击item使之滚动到屏幕中间位置
2020/03/25 Javascript
OpenLayer3自定义测量控件MeasureTool
2020/09/28 Javascript
Python的批量远程管理和部署工具Fabric用法实例
2015/01/23 Python
Python GUI编程完整示例
2019/04/04 Python
python实现超市商品销售管理系统
2019/11/22 Python
Pytorch 实现sobel算子的卷积操作详解
2020/01/10 Python
Python无头爬虫下载文件的实现
2020/04/02 Python
python调用摄像头的示例代码
2020/09/28 Python
非洲NO.1网上商店:Jumia肯尼亚
2016/08/18 全球购物
英国著名国际平价时尚男装品牌:Topman
2016/08/27 全球购物
Nike爱尔兰官方网站:Nike.com (IE)
2018/03/12 全球购物
小学生开学感言
2014/02/28 职场文书
雏鹰争章活动总结
2014/05/09 职场文书
白鹤梁导游词
2015/02/06 职场文书
党支部鉴定意见
2015/06/02 职场文书
Python 用户输入和while循环的操作
2021/05/23 Python
浅谈Redis跟MySQL的双写问题解决方案
2022/02/24 Redis
Java异常体系非正常停止和分类
2022/06/14 Java/Android