python实现桌面气泡提示功能
作者:socrates 发布时间:2023-07-13 06:37:13
标签:python,气泡提示
在写桌面软件时,通常会使用到托盘上的泡泡提示功能,让我们来看看使用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 ()
效果:
二、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()
效果如下:
来源:https://blog.csdn.net/dyx1024/article/details/7447443


猜你喜欢
- Kubernetes的控制器模式是其非常重要的一个设计模式,整个Kubernetes定义的资源对象以及其状态都保存在etcd数据库中,通过a
- 描述Python rindex() 方法返回子字符串最后一次出现在字符串中的索引位置,该方法与 rfind() 方法一样
- 前言:什么是分布式事务?银行跨行转账业务是一个典型分布式事务场景,假设A需要跨行转账给B,那么就涉及两个银行的数据,无法通过一个数据库的本地
- pycharm下载库时出现Failed to install package怎么解决?奶奶都会解决的那种。一、当前现状 看看你是否也是这种情
- Python 内置的四种常用数据结构:列表(list)、元组(tuple)、字典(dict)以及集合(set)。这四种数据结构一但都可用于保
- Linux安装MySQL笔记1、在安装MySQL数据库服务器前,确保你的linux系统是可以连接网络的,下面我们将通过源码方式来安装mysq
- 在生产环境下,有时公司客服反映网页半天打不到,除了在浏览器按F12的Network响应来排查,确定web服务器无故障后。就需要检
- 注:IE8以前的版本均不支持该特性为了向文档中插入生成内容,可以使用:before与:after伪元素。如,我想在所有链接的后面加上&quo
- 如果我们的语句为:IF NOT EXISTS(SELECT 1 FROM sys.views WHERE name='Report_
- Python web应用想要发布使用iis发布有两种方式,这篇文章就为大家介绍一下这两种方式的具体实现:1.配置HttpPlatform程序
- 前些天写一个存储过程,存储过程中使用了事务,后来我把一些代码注释掉来进行调试找错,突然发现一张表被锁住了,原来是创建事务的代码忘记注释掉。本
- 方式一:图片+文字row = 0 # 行号col = 1 # 列号icon = QTableWidgetItem(QIcon(".
- 这里提供在使用python进行开发中常使用到的方法技巧,如有不对欢迎批评指正。要点:开发中类、变量特性查询,类型就是类,断言的使用,深浅复制
- 需要用到2个库,requestsocks5-http-client/lib/Agent/或socks5-https-client/lib/A
- 1、导入第三方库import urllib.request,urllib.error #请求网页from bs4 import
- ajax缓存和编码问题不难解决,下面是解决方法。编码问题默认使用UTF-8,如果一旦发现对象找不到的情况,可能js中输入了中文,同时js的编
- /*存储过程*/ sp_databases --列出服务器上的所有数据库 sp_server_info --列出服务器信息,如字符集,版本和
- 一个什么都不懂的家伙非跟我要个sql查询器 随便写了一个,当然为了数据安全,要过滤掉一个sql关键词和系统中的一些表了 哦,对了,里面的一些
- 最近临时工作要生成xml报名,通过MQ接口发送。简单小程序。自增长拼成xml报文Test_001.py# encoding=utf-8imp
- 先看示例i=1menu=["B超室","化验室"]user="张来,黎明,常冒