From 7f0fe2cfe4269a5cea742db6594e5a429166cee7 Mon Sep 17 00:00:00 2001 From: Irony <892768447@qq.com> Date: Mon, 10 Sep 2018 01:21:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=B3=E4=BE=A7=E6=B6=88=E6=81=AF=E6=8F=90?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .settings/org.eclipse.core.resources.prefs | 1 + 气泡提示/Notification.py | 251 +++++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 气泡提示/Notification.py diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 8d18500..f77555f 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -68,6 +68,7 @@ encoding//\u65E0\u8FB9\u6846\u81EA\u5B9A\u4E49\u6807\u9898\u680F\u7A97\u53E3/Tes encoding//\u65E0\u8FB9\u6846\u81EA\u5B9A\u4E49\u6807\u9898\u680F\u7A97\u53E3/win\u65E0\u8FB9\u6846\u8C03\u6574\u5927\u5C0F.py=utf-8 encoding//\u68A6\u5E7B\u6811/DreamTree.py=utf-8 encoding//\u6C14\u6CE1\u63D0\u793A/BubbleTips.py=utf-8 +encoding//\u6C14\u6CE1\u63D0\u793A/Notification.py=utf-8 encoding//\u6D4F\u89C8\u5668\u83B7\u53D6Cookie/WebEngineView.py=utf-8 encoding//\u6D4F\u89C8\u5668\u83B7\u53D6Cookie/WebView.py=utf-8 encoding//\u6D88\u606F\u5BF9\u8BDD\u6846\u5012\u8BA1\u65F6\u5173\u95ED/MessageBox.py=utf-8 diff --git a/气泡提示/Notification.py b/气泡提示/Notification.py new file mode 100644 index 0000000..4c27649 --- /dev/null +++ b/气泡提示/Notification.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2018年9月9日 +@author: Irony +@site: https://github.com/892768447 +@email: 892768447@qq.com +@file: Notification +@description: +""" +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 + + +__Author__ = """By: Irony +QQ: 892768447 +Email: 892768447@qq.com""" +__Copyright__ = 'Copyright (c) 2018 Irony' +__Version__ = 1.0 + + +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) + bgWidget = QWidget(self) + layout.addWidget(bgWidget) + + layout = QGridLayout(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, wordWrap=True, alignment=Qt.AlignLeft | Qt.AlignTop) + self.labelMessage.adjustSize() + font = self.labelMessage.font() + font.setPixelSize(20) + self.labelMessage.setFont(font) + + # 添加到布局 + 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 sizeHint(self): + return QSize(392, 96) + + 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(w.sizeHint()) + 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(w.sizeHint()) + 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(w.sizeHint()) + cls._instance.setItemWidget(item, w) + + @classmethod + def error(cls, title, message, callback=None): + cls._createInstance() + item = QListWidgetItem(cls._instance) + w = NotificationItem(title, message, item, cls._instance, + ntype=NotificationIcon.Error, callback=callback) + w.closed.connect(cls._instance.removeItem) + item.setSizeHint(w.sizeHint()) + 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) + layout.addWidget(QPushButton( + 'Info', w, clicked=lambda: NotificationWindow.info('提示', '这是一条会自动关闭的消息'))) + layout.addWidget(QPushButton( + 'Success', w, clicked=lambda: NotificationWindow.success('提示', '这是一条会自动关闭的消息'))) + layout.addWidget(QPushButton( + 'Warning', w, clicked=lambda: NotificationWindow.warning('提示', '这是一条会自动关闭的消息'))) + layout.addWidget(QPushButton( + 'Error', w, clicked=lambda: NotificationWindow.error('提示', '
这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案
'))) + w.show() + sys.exit(app.exec_())