前言

Qt 自带的工具提示样式不太好看,就算加了样式表也时不时会失效,同时工具提示没有阴影,看起来就更难受了。所以本篇博客将会介绍自定义工具提示的方法,效果如下图所示:

实现过程

工具提示其实就是一个带了标签的窗口,为了给工具提示加上阴影,只要给窗口设置 QGraphicsShadowEffect 即可。同时 QToolTip 弹出之后不会一直卡在界面上,一段时间后就会消失,所以我们应该给自定义的工具提示加上一个 QTimer,时间溢出之后就隐藏工具提示。

# coding:utf-8
from PyQt5.QtCore import QFile, QPropertyAnimation, QTimer, Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (QApplication, QFrame, QGraphicsDropShadowEffect,
QHBoxLayout, QLabel) class ToolTip(QFrame): def __init__(self, text='', parent=None):
super().__init__(parent=parent)
self.__text = text
self.__duration = 1000
self.timer = QTimer(self)
self.hBox = QHBoxLayout(self)
self.label = QLabel(text, self)
self.ani = QPropertyAnimation(self, b'windowOpacity', self) # set layout
self.hBox.addWidget(self.label)
self.hBox.setContentsMargins(10, 7, 10, 7) # add shadow
self.shadowEffect = QGraphicsDropShadowEffect(self)
self.shadowEffect.setBlurRadius(32)
self.shadowEffect.setColor(QColor(0, 0, 0, 60))
self.shadowEffect.setOffset(0, 5)
self.setGraphicsEffect(self.shadowEffect) self.timer.setSingleShot(True)
self.timer.timeout.connect(self.hide) # set style
self.setAttribute(Qt.WA_StyledBackground)
self.setDarkTheme(False)
self.__setQss() def text(self):
return self.__text def setText(self, text: str):
""" set text on tooltip """
self.__text = text
self.label.setText(text)
self.label.adjustSize()
self.adjustSize() def duration(self):
return self.__duration def setDuration(self, duration: int):
""" set tooltip duration in milliseconds """
self.__duration = abs(duration) def __setQss(self):
""" set style sheet """
f = QFile("resource/tooltip.qss")
f.open(QFile.ReadOnly)
self.setStyleSheet(str(f.readAll(), encoding='utf-8'))
f.close() self.label.adjustSize()
self.adjustSize() def setDarkTheme(self, dark=False):
""" set dark theme """
dark = 'true' if dark else 'false'
self.setProperty('dark', dark)
self.label.setProperty('dark', dark)
self.setStyle(QApplication.style()) def showEvent(self, e):
self.timer.stop()
self.timer.start(self.__duration)
super().showEvent(e) def hideEvent(self, e):
self.timer.stop()
super().hideEvent(e)

工具提示继承自 QFrame 的原因是我们需要设置边框样式,样式表如下所示,支持亮暗两种主题:

ToolTip[dark="false"] {
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 5px;
background-color: rgb(243, 243, 243);
} ToolTip[dark="true"] {
border: 1px solid rgb(28, 28, 28);
border-radius: 5px;
background-color: rgb(43, 43, 43);
} QLabel {
background-color: transparent;
font: 15px 'Segoe UI', 'Microsoft YaHei';
} QLabel[dark="false"] {
color: black;
} QLabel[dark="true"] {
color: white;
}

测试

下述代码的运行效果就是动图中所示的样子,只要给想要设置工具提示的部件安装上事件过滤器,就能将 QToolTip 替换成自定义的工具提示:

# coding:utf-8
import sys
from PyQt5.QtCore import QEvent, QPoint
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout from tool_tip import ToolTip class Demo(QWidget): def __init__(self):
super().__init__()
self.hBox = QHBoxLayout(self)
self.button1 = QPushButton('キラキラ', self)
self.button2 = QPushButton('食べた愛', self)
self._toolTip = ToolTip(parent=self)
# self._tooltip.setDarkTheme(True) self.button1.setToolTip('aiko - キラキラ ')
self.button2.setToolTip('aiko - 食べた愛 ')
self.button1.setToolTipDuration(1000)
self.button2.setToolTipDuration(5000) self.button1.installEventFilter(self)
self.button2.installEventFilter(self) self.hBox.setContentsMargins(30, 30, 30, 30)
self.hBox.setSpacing(20)
self.hBox.addWidget(self.button1)
self.hBox.addWidget(self.button2) self.resize(600, 300)
self._toolTip.hide() with open('resource/demo.qss', encoding='utf-8') as f:
self.setStyleSheet(f.read()) def eventFilter(self, obj, e: QEvent):
if obj is self:
return super().eventFilter(obj, e) tip = self._toolTip
if e.type() == QEvent.Enter:
tip.setText(obj.toolTip())
tip.setDuration(obj.toolTipDuration()) pos = obj.mapTo(self, QPoint(0, 0))
x = pos.x() + obj.width()//2 - tip.width()//2
y = pos.y() - 5 - tip.height() # adjust postion to prevent tooltips from appearing outside the window
x = min(max(5, x), self.width() - tip.width() - 5)
y = min(max(5, y), self.height() - tip.height() - 5) tip.move(x, y)
tip.show()
elif e.type() == QEvent.Leave:
tip.hide()
elif e.type() == QEvent.ToolTip:
return True return super().eventFilter(obj, e) if __name__ == '__main__':
app = QApplication(sys.argv)
w = Demo()
w.show()
app.exec_()

用到的样式文件如下:

QWidget{
background-color: white;
} QPushButton {
background-color: rgb(204, 204, 204);
padding: 10px 64px 10px 64px;
font: 19px 'Microsoft YaHei';
border: transparent;
border-radius: 4px;
} QPushButton:pressed:hover {
background-color: rgb(153, 153, 153);
} QPushButton:hover {
background-color: rgb(230, 230, 230);
} QPushButton:disabled {
background-color: rgb(204, 204, 204);
color: rgb(122, 122, 122);
}

后记

自定义工具提示的方法已经介绍完了,更多好康的自定义小部件参见 GitHub 仓库 https://github.com/zhiyiYo/PyQt-Fluent-Widgets,以上~~

如何在 pyqt 中自定义工具提示 ToolTip的更多相关文章

  1. 如何在pyqt中自定义无边框窗口

    前言 之前写过很多关于无边框窗口并给窗口添加特效的博客,按照时间线罗列如下: 如何在pyqt中实现窗口磨砂效果 如何在pyqt中实现win10亚克力效果 如何在pyqt中通过调用SetWindowCo ...

  2. 如何在pyqt中自定义SwitchButton

    前言 网上有很多 SwitchButton 的实现方式,大部分是通过重写 paintEvent() 来实现的,感觉灵活性不是很好.所以希望实现一个可以联合使用 qss 来更换样式的 SwitchBut ...

  3. 如何在pyqt中通过调用 SetWindowCompositionAttribute 实现Win10亚克力效果

    亚克力效果 在<如何在pyqt中实现窗口磨砂效果>和<如何在pyqt中实现win10亚克力效果>中,我们调用C++ dll来实现窗口效果,这种方法要求电脑上必须装有MSVC.V ...

  4. 如何在pyqt中在实现无边框窗口的同时保留Windows窗口动画效果(一)

    无边框窗体的实现思路 在pyqt中只要 self.setWindowFlags(Qt.FramelessWindowHint) 就可以实现边框的去除,但是没了标题栏也意味着窗口大小无法改变.窗口无法拖 ...

  5. 如何在pyqt中给无边框窗口添加DWM环绕阴影

    前言 在之前的博客<如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果>中,我们实现了窗口的亚克力效果,同时也用SetWindowC ...

  6. 如何在 pyqt 中捕获并处理 Alt+F4 快捷键

    前言 如果在 Windows 系统的任意一个窗口中按下 Alt+F4,默认行为是关闭窗口(或者最小化到托盘).对于使用了亚克力效果的窗口,使用 Alt+F4 最小化到托盘,再次弹出窗口的时候可能出现亚 ...

  7. 如何在pyqt中实现带动画的动态QMenu

    弹出菜单的视觉效果 QLineEdit 原生的菜单弹出效果十分生硬,而且样式很丑.所以照着Groove中单行输入框弹出菜单的样式和动画效果写了一个可以实现动态变化Item的弹出菜单,根据剪贴板的内容是 ...

  8. 6.1 如何在spring中自定义xml标签

    dubbo自定义了很多xml标签,例如<dubbo:application>,那么这些自定义标签是怎么与spring结合起来的呢?我们先看一个简单的例子. 一 编写模型类 package ...

  9. 如何在pyqt中实现窗口磨砂效果

    磨砂效果的实现思路 这两周一直在思考怎么在pyqt上实现窗口磨砂效果,网上搜了一圈,全都是 C++ 的实现方法.正好今天查python的官方文档的时候看到了 ctypes 里面的 HWND,想想倒不如 ...

随机推荐

  1. vue2实现搜索结果中的搜索关键字高亮

    // 筛选变色 brightenKeyword(val, keyword) { val = val + ''; if (val.indexOf(keyword) !== -1 && k ...

  2. 前端每日实战:134# 视频演示如何用 CSS 和 GSAP 创作一个树枝发芽的 loader

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/LJmpXZ 可交互视频 此视频是可 ...

  3. 【Android开发】jarsigner重新打包apk

    签名(sign):在应用程序的特定字段写入特定的标记信息,表示该软件已经通过了签署者的审核. 过程:使用私有密钥数字地签署一个给定的应用程序. 作用: 识别应用程序作者: 检測应用程序是否发生改变: ...

  4. rem,px,em最大的区别;

    px:px像素(Pixel).相对长度单位.像素px是相对于显示器屏幕分辨率而言的.移动端的分辨率很多.所以px不适用移动端em:em的值不固定:其长度继承父级元素的字体大小rem:相对于根元素htm ...

  5. 2020极客大挑战Web题

    前言 wp是以前写的,整理一下发上来. 不是很全. 2020 极客大挑战 WEB 1.sha1碰撞 题目 图片: 思路 题目说,换一种请求方式.于是换成post.得到一给含有代码的图片 图片: 分析该 ...

  6. LC-202

    编写一个算法来判断一个数 n 是不是快乐数. 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和. 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 ...

  7. Visual Studio 打包和安装 exe

    # Visual Studio 打包和安装 exe > **小型项目(无复杂的库)** //VS2022 作为演示平台   > 1.解决方案配置 = Release   > 2.解决 ...

  8. 在非k8s 环境下 的应用 使用 Dapr Sidekick for .NET

    在k8s 环境下,通过Operator 可以管理Dapr sidecar, 在虚拟机环境下,我们也是非常需要这样的一个管理组件,类似下图:在这张图片中,在上图左面,我们看到了"dapr.ex ...

  9. Java学习day27

    今天跟着做了一个模拟龟兔赛跑的程序 只有一条赛道,乌龟和兔子在同一条赛道上比赛,使用了多线程 为了实现兔子睡觉,在run方法内增加了当前奔跑者是否是兔子的判断且当前奔跑步数是否是10的整数倍的判断,如 ...

  10. UnrealEngine创建自定义资产类型

    导语 这篇文章记录了将UObject实例保存在Asset文件的方法,用这个方法可以将自定义的UObject数据序列化保存到文件,可以用于自定义UE资源类型. 创建UObject类 这一步比较简单,按照 ...