PyQt设置右下角弹窗
参考github
效果:
运行代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import webbrowser
from PyQt5.QtCore import Qt, QPropertyAnimation, QPoint, QTimer, pyqtSignal
from PyQt5.QtWidgets import QWidget, QPushButton
from Lib.UiNotify import Ui_NotifyForm # @UnresolvedImport
__version__ = "0.0.1"
class WindowNotify(QWidget, Ui_NotifyForm):
SignalClosed = pyqtSignal() # 弹窗关闭信号
def __init__(self, title="", content="", timeout=5000, *args, **kwargs):
super(WindowNotify, self).__init__(*args, **kwargs)
self.setupUi(self)
self.setTitle(title).setContent(content)
self._timeout = timeout
self._init()
def setTitle(self, title):
if title:
self.labelTitle.setText(title)
return self
def title(self):
return self.labelTitle.text()
def setContent(self, content):
if content:
self.labelContent.setText(content)
return self
def content(self):
return self.labelContent.text()
def setTimeout(self, timeout):
if isinstance(timeout, int):
self._timeout = timeout
return self
def timeout(self):
return self._timeout
def onView(self):
print("onView")
webbrowser.open_new_tab("http://alyl.vip")
def onClose(self):
#点击关闭按钮时
print("onClose")
self.isShow = False
QTimer.singleShot(100, self.closeAnimation)#启动弹回动画
def _init(self):
# 隐藏任务栏|去掉边框|顶层显示
self.setWindowFlags(Qt.Tool | Qt.X11BypassWindowManagerHint |
Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
# 关闭按钮事件
self.buttonClose.clicked.connect(self.onClose)
# 点击查看按钮
self.buttonView.clicked.connect(self.onView)
# 是否在显示标志
self.isShow = True
# 超时
self._timeouted = False
# 桌面
self._desktop = QApplication.instance().desktop()
# 窗口初始开始位置
self._startPos = QPoint(
self._desktop.screenGeometry().width() - self.width() - 5,
self._desktop.screenGeometry().height()
)
# 窗口弹出结束位置
self._endPos = QPoint(
self._desktop.screenGeometry().width() - self.width() - 5,
self._desktop.availableGeometry().height() - self.height() - 5
)
# 初始化位置到右下角
self.move(self._startPos)
# 动画
self.animation = QPropertyAnimation(self, b"pos")
self.animation.finished.connect(self.onAnimationEnd)
self.animation.setDuration(1000) # 1s
# 弹回定时器
self._timer = QTimer(self, timeout=self.closeAnimation)
def show(self, title="", content="", timeout=5000):
self._timer.stop() # 停止定时器,防止第二个弹出窗弹出时之前的定时器出问题
self.hide() # 先隐藏
self.move(self._startPos) # 初始化位置到右下角
super(WindowNotify, self).show()
self.setTitle(title).setContent(content).setTimeout(timeout)
return self
def showAnimation(self):
print("showAnimation isShow = True")
# 显示动画
self.isShow = True
self.animation.stop()#先停止之前的动画,重新开始
self.animation.setStartValue(self.pos())
self.animation.setEndValue(self._endPos)
self.animation.start()
# 弹出5秒后,如果没有焦点则弹回去
self._timer.start(self._timeout)
# QTimer.singleShot(self._timeout, self.closeAnimation)
def closeAnimation(self):
print("closeAnimation hasFocus", self.hasFocus())
# 关闭动画
if self.hasFocus():
# 如果弹出后倒计时5秒后还有焦点存在则失去焦点后需要主动触发关闭
self._timeouted = True
return # 如果有焦点则不关闭
self.isShow = False
self.animation.stop()
self.animation.setStartValue(self.pos())
self.animation.setEndValue(self._startPos)
self.animation.start()
def onAnimationEnd(self):
# 动画结束
print("onAnimationEnd isShow", self.isShow)
if not self.isShow:
print("onAnimationEnd close()")
self.close()
print("onAnimationEnd stop timer")
self._timer.stop()
print("onAnimationEnd close and emit signal")
self.SignalClosed.emit()
def enterEvent(self, event):
super(WindowNotify, self).enterEvent(event)
# 设置焦点(好像没啥用,不过鼠标点击一下后,该方法就有用了)
print("enterEvent setFocus Qt.MouseFocusReason")
self.setFocus(Qt.MouseFocusReason)
def leaveEvent(self, event):
super(WindowNotify, self).leaveEvent(event)
# 取消焦点
print("leaveEvent clearFocus")
self.clearFocus()
if self._timeouted:
QTimer.singleShot(1000, self.closeAnimation)
if __name__ == "__main__":
import sys
from PyQt5.QtWidgets import QApplication, QHBoxLayout
app = QApplication(sys.argv)
window = QWidget()
notify = WindowNotify(parent=window)
layout = QHBoxLayout(window)
b1 = QPushButton(
"弹窗1", window, clicked=lambda: notify.show(content=b1.text()).showAnimation())
b2 = QPushButton(
"弹窗2", window, clicked=lambda: notify.show(content=b2.text()).showAnimation())
layout.addWidget(b1)
layout.addWidget(b2)
window.show()
sys.exit(app.exec_())
效果2:
运行代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Created on 2018年1月27日
@author: Irony."[讽刺]
@site: https://pyqt5.com , https://github.com/892768447
@email: 892768447@qq.com
@file: BubbleTips
@description:
'''
import sys
from PyQt5.QtCore import QRectF, Qt, QPropertyAnimation, pyqtProperty, \
QPoint, QParallelAnimationGroup, QEasingCurve
from PyQt5.QtGui import QPainter, QPainterPath, QColor, QPen
from PyQt5.QtWidgets import QLabel, QWidget, QVBoxLayout, QApplication,\
QLineEdit, QPushButton
__Author__ = "By: Irony.\"[讽刺]\nQQ: 892768447\nEmail: 892768447@qq.com"
__Copyright__ = "Copyright (c) 2018 Irony.\"[讽刺]"
__Version__ = "Version 1.0"
class BubbleLabel(QWidget):
BackgroundColor = QColor(195, 195, 195)
BorderColor = QColor(150, 150, 150)
def __init__(self, *args, **kwargs):
text = kwargs.pop("text", "")
super(BubbleLabel, self).__init__(*args, **kwargs)
# 设置无边框置顶
self.setWindowFlags(
Qt.Window | Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint)
# 设置最小宽度和高度
self.setMinimumWidth(200)
self.setMinimumHeight(48)
self.setAttribute(Qt.WA_TranslucentBackground, True)
layout = QVBoxLayout(self)
# 左上右下的边距(下方16是因为包括了三角形)
layout.setContentsMargins(8, 8, 8, 16)
self.label = QLabel(self)
layout.addWidget(self.label)
self.setText(text)
# 获取屏幕高宽
self._desktop = QApplication.instance().desktop()
def setText(self, text):
self.label.setText(text)
def text(self):
return self.label.text()
def stop(self):
self.hide()
self.animationGroup.stop()
self.close()
def show(self):
super(BubbleLabel, self).show()
# 窗口开始位置
startPos = QPoint(
self._desktop.screenGeometry().width() - self.width() - 100,
self._desktop.availableGeometry().height() - self.height())
endPos = QPoint(
self._desktop.screenGeometry().width() - self.width() - 100,
self._desktop.availableGeometry().height() - self.height() * 3 - 5)
print(startPos, endPos)
self.move(startPos)
# 初始化动画
self.initAnimation(startPos, endPos)
def initAnimation(self, startPos, endPos):
# 透明度动画
opacityAnimation = QPropertyAnimation(self, b"opacity")
opacityAnimation.setStartValue(1.0)
opacityAnimation.setEndValue(0.0)
# 设置动画曲线
opacityAnimation.setEasingCurve(QEasingCurve.InQuad)
opacityAnimation.setDuration(4000) # 在4秒的时间内完成
# 往上移动动画
moveAnimation = QPropertyAnimation(self, b"pos")
moveAnimation.setStartValue(startPos)
moveAnimation.setEndValue(endPos)
moveAnimation.setEasingCurve(QEasingCurve.InQuad)
moveAnimation.setDuration(5000) # 在5秒的时间内完成
# 并行动画组(目的是让上面的两个动画同时进行)
self.animationGroup = QParallelAnimationGroup(self)
self.animationGroup.addAnimation(opacityAnimation)
self.animationGroup.addAnimation(moveAnimation)
self.animationGroup.finished.connect(self.close) # 动画结束时关闭窗口
self.animationGroup.start()
def paintEvent(self, event):
super(BubbleLabel, self).paintEvent(event)
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing) # 抗锯齿
rectPath = QPainterPath() # 圆角矩形
triPath = QPainterPath() # 底部三角形
height = self.height() - 8 # 往上偏移8
rectPath.addRoundedRect(QRectF(0, 0, self.width(), height), 5, 5)
x = self.width() / 5 * 4
triPath.moveTo(x, height) # 移动到底部横线4/5处
# 画三角形
triPath.lineTo(x + 6, height + 8)
triPath.lineTo(x + 12, height)
rectPath.addPath(triPath) # 添加三角形到之前的矩形上
# 边框画笔
painter.setPen(QPen(self.BorderColor, 1, Qt.SolidLine,
Qt.RoundCap, Qt.RoundJoin))
# 背景画刷
painter.setBrush(self.BackgroundColor)
# 绘制形状
painter.drawPath(rectPath)
# 三角形底边绘制一条线保证颜色与背景一样
painter.setPen(QPen(self.BackgroundColor, 1,
Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
painter.drawLine(x, height, x + 12, height)
def windowOpacity(self):
return super(BubbleLabel, self).windowOpacity()
def setWindowOpacity(self, opacity):
super(BubbleLabel, self).setWindowOpacity(opacity)
# 由于opacity属性不在QWidget中需要重新定义一个
opacity = pyqtProperty(float, windowOpacity, setWindowOpacity)
class TestWidget(QWidget):
def __init__(self, *args, **kwargs):
super(TestWidget, self).__init__(*args, **kwargs)
layout = QVBoxLayout(self)
self.msgEdit = QLineEdit(self, returnPressed=self.onMsgShow)
self.msgButton = QPushButton("显示内容", self, clicked=self.onMsgShow)
layout.addWidget(self.msgEdit)
layout.addWidget(self.msgButton)
def onMsgShow(self):
msg = self.msgEdit.text().strip()
if not msg:
return
if hasattr(self, "_blabel"):
self._blabel.stop()
self._blabel.deleteLater()
del self._blabel
self._blabel = BubbleLabel()
self._blabel.setText(msg)
self._blabel.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
w = TestWidget()
w.show()
sys.exit(app.exec_())
效果3:
代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import base64
from PyQt5.QtCore import Qt, QRectF, QSize, pyqtSignal, QTimer
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPainterPath,\
QColor
from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout,\
QGridLayout, QSpacerItem, QSizePolicy, QGraphicsDropShadowEffect,\
QListWidget, QListWidgetItem
class NotificationIcon:
Info, Success, Warning, Error, Close = range(5)
Types = {
Info: None,
Success: None,
Warning: None,
Error: None,
Close: None
}
@classmethod
def init(cls):
cls.Types[cls.Info] = QPixmap(QImage.fromData(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC5ElEQVRYR8VX0VHbQBB9e/bkN3QQU0FMBSEVYFcQ8xPBJLJ1FWAqOMcaxogfTAWQCiAVRKkgTgfmM4zRZu6QhGzL0p0nDPr17e7bt7tv14RX/uiV48MJgAon+8TiAMRtMFogaqUJxADPwRRzg67kl8+xbWJWANR40iPQSSFgtX/mGQkaDr56V3VAKgGos4s2JXwJoF3naMPvMS+SrpTHs032GwGkdF+DsFMVnJm/oyGGeHico0EjIjpYes+YMyVd6R/flfkpBWCCQ9zaZM2LZDfLMGXsZ5kdI/lYBmINgHHyyLd1mWdBbAFAM/GY7K2WYx1AeB4T6L1N9umbGxZ0qktATaEAdCps48D39oq/LwEw3U5CN92LfczJoewfT7MAywDCaEbAuxeLrh0zz4L+0e4aAJfGy+sP3IMxlH1vpMJoSMCJDXgWtJeJVc6ACs9HBBrYODCJAFdYvAmkPJxnNqMwYht7Bn+T/lGg3z4DGEd3RPhQ54DBvwAOVkeqagRXfTLjh+x7+8sALOtfHLuiYzWOAiLoKbD58mnIGbCmLxUepS6NQmYlUGE0JeCTTXT9JvA9E9sZgO5iIpoyc6/YzcqSwQzgGgBXB7oXpH9klpRSkxY1xW/b7Iu2zk34PILPnazCqEPAtTWA8iZ0HsOu9L0bw4DzCJeNocMGNDpQ3IKO+6NUiJ4ysZNiBv5I3zPnmJmG5oM+wbS+9+qkvGi7NAXGmeUy0ioofa+XA0jH0UaMKpdRWs/adcwMqfV/tenqpqHY/Znt+j2gJi00RUzA201dXaxh9iZdZloJS+9H1otrkbRrD5InFqpPskxEshJQ468CkSmJC+i1HigaaxCAuCljgoDhwPdOjf7rFVxxuJrMkXScjtKc1rOLNpJk6nii5XmYzbngzlZn+RIb40kPJPTBYXUt6VEDJ8Pi6bWpNFb/jFYY6YGpDeKdjBmTKdMcxDGEmP73v2a2Gr/NOycGtglQZ/MPzEqCMLGckJEAAAAASUVORK5CYII=')))
cls.Types[cls.Success] = QPixmap(QImage.fromData(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACZUlEQVRYR8VXS3LTQBDtVsDbcAPMCbB3limkcAKSG4QFdnaYE2BOQLKzxSLJCeAGSUQheSnfwLmB2VJhXmpExpFHI2sk2RWv5FJPv9evP9NieuIfPzE+VSJw8qt3IMDvmahDoDYxt2UAACXMWIIowR5ffn8TJbaBWRE4CXvHAH9RgKXOgQUI48CfXZbZbiTw8Xe/w3d0zkydMkem91IZpyWOJu5sUXS+kEAqt3B+MNOLOuDqDEBLxxFHk7eza5MfIwEJDjhXTYD1s8zinYlEjsCD7FdNI9cJpEq0RFdPR47AMOzLCn69zegz6UgCP+pmfa8RSKudnPNdgCufTOLDxJtdPP7PoA1Cd8HEL5sSUCCD0B0x8bc1f8Bi6sevcgS2VXh6hMOwDz0gsUddNaxWKRjeuKfE/KlJ9Dq4UYH/o/Ns6scj+bgiMAjdayb26xLQwTfVEwg3gRcf6ARq578KuLo7VDc8psCQqwfjr4EfjYvkrAquFJ56UYpdSkAZSmNd1rrg0leOQFELgvA58OJTxVyRaAJORPOpF6UXnFUR5sDiXjs7UqsOMGMRlrWhTkJXpFL3mNrQZhA1lH3F0TiI5FurUQyMpn58VjhkSqQA4Tbw4nSVW6sBU5VXktXSeONlJH3s8jrOVr9RgVSFuNcWfzlh5n3LoKzMAPxxWuiULiQpiR2sZNnCyzIuWUr5Z1Ml0sgdHFZaShVDuR86/0huL3VXtDk/F4e11vKsTHLSCeKx7bYkW80hjLOrV1GhWH0ZrSlyh2MwdZhYfi8oZeYgLBmUiGd8sfVPM6syr2lUSYGaGBuP3QN6rVUwYV/egwAAAABJRU5ErkJggg==')))
cls.Types[cls.Warning] = QPixmap(QImage.fromData(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACmElEQVRYR8VXTW7TUBD+xjYSXZFukOIsSE9AskNJJMoJmq4r7OYEwAkabhBOkB/Emt4gVIojdpgbpIumEitX6gKB7UHPkauXxLHfc4F6Z3l+vvnmm/fGhAd+6IHzQwvA9cfOITMfAdQAcx1EdVEAM/tEFADsWyaPn57MfdXClABcT1qnzHSWJiwMzrwgoF91vXGRbS6AH59ajd8hDYmoURQo67tgxoij42rv62KX/04Agu44xmciVMokT32YERgGjquvZ1+y4mQCWPUa0/sk3vQlwqssEFsAVrQbU4XKL/ai2+5PPK6waQ4AOsoDnDARh83NdmwBuJq0fQI9L6p+L7rd3+/5gbAToMPI+FbkIzRRc72mbLcGIFE7jGFRIPHddmZrvstJh1X8CHGv6sxHqe1GkPYCoGcqgcoCAPPCdr2DLQC6wqMoPEj7qdqCNKllxs30sLpjYDluDUDGG5XqhY2sal3w4PiD7c7fJnHShMtJR8zpy/8CALiwndnhBgD1/t+XAXkaZAaUVHwnHulg0W6BNEWlAQD8zna8gQB0Ne70iXCm2j55jCUAei1gxvuaO+uXAcDg7zXHSy640iKUAehOEDJFqDmGQkiPLO5Fv+KADXOqvCuIsrPGsIyQdHou22YeRMJgOdHTQTkAfGk7XrLKrWlAvOhcRgBfWiZ3RQti0zxXuUFXCXMuo0TRitfxugjbIxC5RYzI6s9kIGFh+KLOpiW22id5AUuI8IaisFG4kCQg/sFKJgtPLix3KWXGeRETRbQDuCFCV2spTYMm+2FEI1WBbYIRPTeiqFtqLZeDraaD+qrbkpgQAvfl1WsXU0p/RjIjYYhTkNFgcCVlRlRKoAAc+5aF0V//NVPoc2kTLQZKZ8lx/AMXBmMwuXUwOAAAAABJRU5ErkJggg==')))
cls.Types[cls.Error] = QPixmap(QImage.fromData(base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACrklEQVRYR82XW27aQBSG/4PtiNhIpStouoImKwjZAV1B07coWCpZQcgK6kh2lLeSFZSsIOwgdAdkBaUSEBQDpxpjU9vM+EJR03nDzJz/mzm3GcIrD3plfZQCeD47O1ho2jERNRmoE9AQG2BgBGBAwIiZe5Zh3JPjiG+5oxCAEF5q2iWITnMtRhOYu5XF4mr/9naYtSYXYGLbHQCXhYVTEwlom657rVqvBOB2uz71/a+ldq1SYe6ahnEhc4sSYGzbfQKOt915eh0D/ZrrnqS/SwEmrVYXRJ92Jb4OC+C65rrtuN0NgIltNwF837V4zN5Hy3V70e9NgFZrCKJ3CQDmJ9MwDsW36XzeB/AhA/CHqeuN2WxWX2paX2JraHneeynA+Pz8lCqVbxLjV5brimxAEJxqiEA8CjZVBvFy+bl2c9MV9hInoAw85qFpGEeRYQVEQjzMokcQHWxsiPne8jzh6j8AodGfyqNlHpiGcaKAkIk/gChwm2yYuv5W2FqfwLNtN5bAQ2bwySB83zENo50A8/1McaFRAU72XVek+mpk+D/JlIKI/xkee654uCbIhjVAqZIrgSgpLhiCwN4OAEj4vEB2yDybBCjsAol4ZD0nRdMQSRcUCsKUeNSw4o2mKMRGEOamoVx8FXDZKVosDYNMUHXAsBRnppo8RQcbpTgIGEkhykpFjnWxzGhPQYxt2yHgS/oIlKVYTJxImpG482nz+VG1Wh1N84pMCCGa0ULXHwmoJwCYnyzPW5fn/68dh7EgPbrMMl3gz7gro+n/7EoWD7w4a96l1NnJ1Yz5Lt6wCgFEk0r1CIkbiPnC9DxH5aHcd4FYGD5MOqVOg/muslh0/vphkm63k5eXZvA0I6qD+ZCI3jDzLxANiHn1NNvb6+30aVYgwLeeUsgFW1svsPA3Ncq4MHzVeO8AAAAASUVORK5CYII=')))
cls.Types[cls.Close] = QPixmap(QImage.fromData(base64.b64decode(
'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAeElEQVQ4T2NkoBAwUqifgboGzJy76AIjE3NCWmL0BWwumzV/qcH/f38XpCfHGcDkUVwAUsDw9+8GBmbmAHRDcMlheAGbQnwGYw0DZA1gp+JwFUgKZyDCDQGpwuIlrGGAHHAUGUCRFygKRIqjkeKERE6+oG5eIMcFAOqSchGwiKKAAAAAAElFTkSuQmCC')))
@classmethod
def icon(cls, ntype):
return cls.Types.get(ntype)
class NotificationItem(QWidget):
closed = pyqtSignal(QListWidgetItem)
def __init__(self, title, message, item, *args, ntype=0, callback=None, **kwargs):
super(NotificationItem, self).__init__(*args, **kwargs)
self.item = item
self.callback = callback
layout = QHBoxLayout(self, spacing=0)
layout.setContentsMargins(0, 0, 0, 0)
self.bgWidget = QWidget(self) # 背景控件, 用于支持动画效果
layout.addWidget(self.bgWidget)
layout = QGridLayout(self.bgWidget)
layout.setHorizontalSpacing(15)
layout.setVerticalSpacing(10)
# 标题左边图标
layout.addWidget(
QLabel(self, pixmap=NotificationIcon.icon(ntype)), 0, 0)
# 标题
self.labelTitle = QLabel(title, self)
font = self.labelTitle.font()
font.setBold(True)
font.setPixelSize(22)
self.labelTitle.setFont(font)
# 关闭按钮
self.labelClose = QLabel(
self, cursor=Qt.PointingHandCursor, pixmap=NotificationIcon.icon(NotificationIcon.Close))
# 消息内容
self.labelMessage = QLabel(
message, self, cursor=Qt.PointingHandCursor, wordWrap=True, alignment=Qt.AlignLeft | Qt.AlignTop)
font = self.labelMessage.font()
font.setPixelSize(20)
self.labelMessage.setFont(font)
self.labelMessage.adjustSize()
# 添加到布局
layout.addWidget(self.labelTitle, 0, 1)
layout.addItem(QSpacerItem(
40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 2)
layout.addWidget(self.labelClose, 0, 3)
layout.addWidget(self.labelMessage, 1, 1, 1, 2)
# 边框阴影
effect = QGraphicsDropShadowEffect(self)
effect.setBlurRadius(12)
effect.setColor(QColor(0, 0, 0, 25))
effect.setOffset(0, 2)
self.setGraphicsEffect(effect)
self.adjustSize()
# 5秒自动关闭
self._timer = QTimer(self, timeout=self.doClose)
self._timer.setSingleShot(True) # 只触发一次
self._timer.start(5000)
def doClose(self):
try:
# 可能由于手动点击导致item已经被删除了
self.closed.emit(self.item)
except:
pass
def showAnimation(self, width):
# 显示动画
pass
def closeAnimation(self):
# 关闭动画
pass
def mousePressEvent(self, event):
super(NotificationItem, self).mousePressEvent(event)
w = self.childAt(event.pos())
if not w:
return
if w == self.labelClose: # 点击关闭图标
# 先尝试停止计时器
self._timer.stop()
self.closed.emit(self.item)
elif w == self.labelMessage and self.callback and callable(self.callback):
# 点击消息内容
self._timer.stop()
self.closed.emit(self.item)
self.callback() # 回调
def paintEvent(self, event):
# 圆角以及背景色
super(NotificationItem, self).paintEvent(event)
painter = QPainter(self)
path = QPainterPath()
path.addRoundedRect(QRectF(self.rect()), 6, 6)
painter.fillPath(path, Qt.white)
class NotificationWindow(QListWidget):
_instance = None
def __init__(self, *args, **kwargs):
super(NotificationWindow, self).__init__(*args, **kwargs)
self.setSpacing(20)
self.setMinimumWidth(412)
self.setMaximumWidth(412)
QApplication.instance().setQuitOnLastWindowClosed(True)
# 隐藏任务栏,无边框,置顶等
self.setWindowFlags(self.windowFlags() | Qt.Tool |
Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
# 去掉窗口边框
self.setFrameShape(self.NoFrame)
# 背景透明
self.viewport().setAutoFillBackground(False)
self.setAttribute(Qt.WA_TranslucentBackground, True)
# 不显示滚动条
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# 获取屏幕高宽
rect = QApplication.instance().desktop().availableGeometry(self)
self.setMinimumHeight(rect.height())
self.setMaximumHeight(rect.height())
self.move(rect.width() - self.minimumWidth() - 18, 0)
def removeItem(self, item):
# 删除item
w = self.itemWidget(item)
self.removeItemWidget(item)
item = self.takeItem(self.indexFromItem(item).row())
w.close()
w.deleteLater()
del item
@classmethod
def _createInstance(cls):
# 创建实例
if not cls._instance:
cls._instance = NotificationWindow()
cls._instance.show()
NotificationIcon.init()
@classmethod
def info(cls, title, message, callback=None):
cls._createInstance()
item = QListWidgetItem(cls._instance)
w = NotificationItem(title, message, item, cls._instance,
ntype=NotificationIcon.Info, callback=callback)
w.closed.connect(cls._instance.removeItem)
item.setSizeHint(QSize(cls._instance.width() -
cls._instance.spacing(), w.height()))
cls._instance.setItemWidget(item, w)
@classmethod
def success(cls, title, message, callback=None):
cls._createInstance()
item = QListWidgetItem(cls._instance)
w = NotificationItem(title, message, item, cls._instance,
ntype=NotificationIcon.Success, callback=callback)
w.closed.connect(cls._instance.removeItem)
item.setSizeHint(QSize(cls._instance.width() -
cls._instance.spacing(), w.height()))
cls._instance.setItemWidget(item, w)
@classmethod
def warning(cls, title, message, callback=None):
cls._createInstance()
item = QListWidgetItem(cls._instance)
w = NotificationItem(title, message, item, cls._instance,
ntype=NotificationIcon.Warning, callback=callback)
w.closed.connect(cls._instance.removeItem)
item.setSizeHint(QSize(cls._instance.width() -
cls._instance.spacing(), w.height()))
cls._instance.setItemWidget(item, w)
@classmethod
def error(cls, title, message, callback=None):
cls._createInstance()
item = QListWidgetItem(cls._instance)
w = NotificationItem(title, message, item,
ntype=NotificationIcon.Error, callback=callback)
w.closed.connect(cls._instance.removeItem)
width = cls._instance.width() - cls._instance.spacing()
item.setSizeHint(QSize(width, w.height()))
cls._instance.setItemWidget(item, w)
if __name__ == '__main__':
import sys
import cgitb
sys.excepthook = cgitb.Hook(1, None, 5, sys.stderr, 'text')
from PyQt5.QtWidgets import QApplication, QPushButton
app = QApplication(sys.argv)
w = QWidget()
layout = QHBoxLayout(w)
def callback():
print('回调点击')
layout.addWidget(QPushButton(
'Info', w, clicked=lambda: NotificationWindow.info('提示', '这是一条会自动关闭的消息', callback=callback)))
layout.addWidget(QPushButton(
'Success', w, clicked=lambda: NotificationWindow.success('提示', '这是一条会自动关闭的消息', callback=callback)))
layout.addWidget(QPushButton(
'Warning', w, clicked=lambda: NotificationWindow.warning(
'提示',
'这是提示文案这是提示文案这是提示文案这是提示文案。',
callback=callback)))
layout.addWidget(QPushButton(
'Error', w, clicked=lambda: NotificationWindow.error(
'提示',
'<html><head/><body><p><span style=" font-style:italic; color:teal;">这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案</span></p></body></html>',
callback=callback)))
w.show()
# NotificationIcon.init()
# ww = NotificationItem('提示', '<html><head/><body><p><span style=" font-style:italic; color:teal;">这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案</span></p></body></html>', None,
# ntype=NotificationIcon.Error)
# ww.bgWidget.setVisible(True)
# ww.show()
sys.exit(app.exec_())