如何在pyqt中实现带动画的动态QMenu
弹出菜单的视觉效果
QLineEdit 原生的菜单弹出效果十分生硬,而且样式很丑。所以照着Groove中单行输入框弹出菜单的样式和动画效果写了一个可以实现动态变化Item的弹出菜单,根据剪贴板的内容是否为文本、编辑框是否有文本以及是否有选中文本分为6种情况,大体效果如下所示(ヾ(๑╹◡╹)ノ" 硝子依旧如此迷人:

具体实现流程
Menu 类
Menu 继承自 QMenu,在这个类中通过调用自定义类 WindowEffect 的方法来调用win10的api从而实现Aero效果和阴影效果,定义WindowEffect的代码放在了文末的链接中,可以自取,而Aero效果的实现方法不妨先康康 《如何在pyqt中实现窗口磨砂效果》,需要指出的是要想实现这两种效果需要事先安装好 MSVC (不安装 MSVC 的解决方案参见 (《如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果》)。Menu 类的具体代码如下:
import sys
from ctypes.wintypes import HWND
from PyQt5.QtCore import QAbstractAnimation, QEasingCurve, QEvent, Qt, QPropertyAnimation, QRect
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QApplication, QGraphicsDropShadowEffect, QMenu
from window_effect import WindowEffect
class Menu(QMenu):
""" 自定义菜单 """
windowEffect = WindowEffect()
def __init__(self, string='', parent=None):
super().__init__(string,parent)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup | Qt.NoDropShadowWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground | Qt.WA_StyledBackground)
self.setQss()
def event(self, e: QEvent):
if e.type() == QEvent.WinIdChange:
self.hWnd = HWND(int(self.winId()))
self.setMenuEffect()
return QMenu.event(self, e)
def setMenuEffect(self):
""" 添加特效 """
self.windowEffect.setAeroEffect(self.hWnd)
self.windowEffect.addShadowEffect(True,self.hWnd)
def setQss(self):
""" 设置层叠样式 """
with open('menu.qss', encoding='utf-8') as f:
self.setStyleSheet(f.read())
LineEditMenu 类
LineEditMenu 继承自 Menu,需要注意的是这个类对 parent 的是有要求的,具体要求见代码:
class LineEditMenu(Menu):
""" 单行输入框右击菜单 """
def __init__(self, parent):
super().__init__('', parent)
# 实例化动画,注意不能直接改width这个只读的属性
self.animation = QPropertyAnimation(self, b'geometry')
self.initWidget()
def initWidget(self):
""" 初始化小部件 """
self.setObjectName('lineEditMenu')
# 设置动画持续时间
self.animation.setDuration(300)
# 设置插值方式
self.animation.setEasingCurve(QEasingCurve.OutQuad)
def createActions(self):
# 创建动作
self.cutAct = QAction(
QIcon('images\\黑色剪刀.png'), '剪切', self, shortcut='Ctrl+X', triggered=self.parent().cut)
self.copyAct = QAction(
QIcon('images\\黑色复制.png'), '复制', self, shortcut='Ctrl+C', triggered=self.parent().copy)
self.pasteAct = QAction(
QIcon('images\\黑色粘贴.png'), '粘贴', self, shortcut='Ctrl+V', triggered=self.parent().paste)
self.cancelAct = QAction(
QIcon('images\\黑色撤销.png'), '取消操作', self, shortcut='Ctrl+Z', triggered=self.parent().undo)
self.selectAllAct = QAction('全选', self, shortcut='Ctrl+A', triggered=self.parent().selectAll)
# 创建动作列表
self.action_list = [self.cutAct, self.copyAct, self.pasteAct, self.cancelAct, self.selectAllAct]
def exec_(self, pos):
""" 重写exec_() """
# 删除所有动作
self.clear()
# clear会直接delete之前的动作故需重新创建
self.createActions()
# 初始化属性,本来是在没有添加动画时为qss设置的,设置动画之后这个属性没什么用
self.setProperty('hasCancelAct', 'false')
width = 176
# 本来是在后面调用columnCount()来计算item个数的,结果算出来是1,所以手动创建一个变量来记录Item个数
actionNum = len(self.action_list)
# 访问系统剪贴板
self.clipboard = QApplication.clipboard()
# 根据剪贴板内容是否为text分两种情况讨论
if self.clipboard.mimeData().hasText():
# 再根据3种情况分类讨论
if self.parent().text():
self.setProperty('hasCancelAct', 'true')
width = 213
if self.parent().selectedText():
self.addActions(self.action_list)
else:
self.addActions(self.action_list[2:])
actionNum -= 2
else:
self.addAction(self.pasteAct)
actionNum = 1
else:
if self.parent().text():
self.setProperty('hasCancelAct', 'true')
width = 213
if self.parent().selectedText():
self.addActions(self.action_list[:2] + self.action_list[3:])
actionNum -= 1
else:
self.addActions(self.action_list[3:])
actionNum -= 3
else:
return
# 每个item的高度为38px,10为上下的内边距和
height = actionNum * 38 + 10
# 不能把初始的宽度设置为0px,不然会报警
self.animation.setStartValue(
QRect(pos.x(), pos.y(), 1, height))
self.animation.setEndValue(
QRect(pos.x(), pos.y(), width, height))
self.setStyle(QApplication.style())
# 开始动画
self.animation.start()
super().exec_(pos)
源代码和dll
动图中用到的编辑框是可以实现对音频文件标签信息的写入的,具体实现方法放在了我的github仓库中,喜欢的话可以给我点个小星星,下面是这次用到的代码、dll以及资源文件的网盘链接(提取码:fl4m):链接
如何在pyqt中实现带动画的动态QMenu的更多相关文章
- 如何在pyqt中在实现无边框窗口的同时保留Windows窗口动画效果(一)
无边框窗体的实现思路 在pyqt中只要 self.setWindowFlags(Qt.FramelessWindowHint) 就可以实现边框的去除,但是没了标题栏也意味着窗口大小无法改变.窗口无法拖 ...
- 如何在pyqt中自定义无边框窗口
前言 之前写过很多关于无边框窗口并给窗口添加特效的博客,按照时间线罗列如下: 如何在pyqt中实现窗口磨砂效果 如何在pyqt中实现win10亚克力效果 如何在pyqt中通过调用SetWindowCo ...
- 如何在pyqt中通过调用 SetWindowCompositionAttribute 实现Win10亚克力效果
亚克力效果 在<如何在pyqt中实现窗口磨砂效果>和<如何在pyqt中实现win10亚克力效果>中,我们调用C++ dll来实现窗口效果,这种方法要求电脑上必须装有MSVC.V ...
- 如何在pyqt中给无边框窗口添加DWM环绕阴影
前言 在之前的博客<如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果>中,我们实现了窗口的亚克力效果,同时也用SetWindowC ...
- 如何在 Matlab 中绘制带箭头的坐标系
如何在 Matlab 中绘制带箭头的坐标系 如何在 Matlab 中绘制带箭头的坐标系 实现原理 演示效果 完整代码 实现原理 使用 matlab 的绘制函数时,默认设置为一个方框形的坐标系, 图1 ...
- 如何在pyqt中实现窗口磨砂效果
磨砂效果的实现思路 这两周一直在思考怎么在pyqt上实现窗口磨砂效果,网上搜了一圈,全都是 C++ 的实现方法.正好今天查python的官方文档的时候看到了 ctypes 里面的 HWND,想想倒不如 ...
- 如何在pyqt中实现win10亚克力效果
亚克力效果的实现思路 上一篇博客<如何在pyqt中实现窗口磨砂效果> 中实现了win7中的Aero效果,但是和win10的亚克力效果相比,Aero还是差了点内味.所以今天早上又在网上搜了一 ...
- 如何在 pyqt 中捕获并处理 Alt+F4 快捷键
前言 如果在 Windows 系统的任意一个窗口中按下 Alt+F4,默认行为是关闭窗口(或者最小化到托盘).对于使用了亚克力效果的窗口,使用 Alt+F4 最小化到托盘,再次弹出窗口的时候可能出现亚 ...
- 如何在unity中使用龙骨动画
龙骨 龙骨是Egret公司的一个用来做动画的软件,本文分享一下如何在Unity2D中使用龙骨导出的2D动画 导出动画 在龙骨中文件->导出,导出动画数据和纹理到Unity的项目中,如果打包了的话 ...
随机推荐
- 1421 最大MOD值
1421 最大MOD值 基准时间限制:1 秒 空间限制:131072 KB 有一个a数组,里面有n个整数.现在要从中找到两个数字(可以是同一个) ai,aj ,使得 ai mod aj 最大并且 a ...
- Problem 2236 第十四个目标
Problem 2236 第十四个目标 Accept: 4 Submit: 6Time Limit: 1000 mSec Memory Limit : 32768 KB Problem D ...
- OA系统中手写签批功能的实现
一.需求背景 OA系统审批中,有对word或者pdf文件源文档在指定的位置可以插入相应的文字,其实就是一个审批的功能,到了指定的人那边,他可以进行签批.这个功能一般来说,是针对于领导方面,对于一个事情 ...
- Chapter 10 Random Variability
目录 10.1 Identification versus estimation 10.2 Estimation of causal effects 10.3 The myth of the supe ...
- 利用数学软件Mathematica的三维图像进行建模
前言 最近在使用Mathematica进行二元函数的图像绘制,在导出图像选项中看到了obj,maya等三维格式.其实很早之前我就有过这样的想法,但之前使用的是matlab作图,无法导出三维格式.那么废 ...
- CS5211完全替代兼容LT7211|PS8625|CH7511方案|EDP转LVDS|Capstone CS5211
CH7511|LT7211|PS8625替代方案--Capstone CS5211AN 设计EDP转LVDS优势方案原理图+PCB板设计 CH7511|LT7211|PS8625这三款都是专门用于设计 ...
- Python max()方法扩展:求字典中值最大的键
重要的应该写在前面[捂脸] 场景一:仅求最大值对应的键,代码如下: >>> dic = {'A':4, 'B':2, 'C':3} >>> max_key = ...
- nginx中请求大小的限制的设置
Nginx对客户端请求缓冲区大小有个默认限制,如果超过了该值(比如在上传大文件时),会报500错误. 只需要设置三个值,就可以解决该问题: 1. client_body_buffer_size: 指定 ...
- 一次性删除 .svn 文件夹
方法一 (Windows 7; Python 3.5.2) import os for (p,d,f) in os.walk(r"G:\qycache\test"): if p.f ...
- 小程序云开发请求第三方http或https接口
1.新建http云函数,选中http云函数,右键,打开终端,安装依赖: npm install request-promise 2.http.js引入request-promise用于做网络请求 va ...