Qt中QLineEdit自带的菜单弹出动画十分生硬,而且自带菜单的阴影效果以及样式很丑。所以照着Groove中单行输入框弹出菜单的样式和动画效果写了一个可以实现动态变化Item的弹出菜单,根据剪贴板的内容是否为文本、编辑框是否有文本以及是否有选中文本分为6种情况,大体效果如下所示(ヾ(๑╹◡╹)ノ"老婆依旧如此迷人:
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())
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)
动图中用到的编辑框是可以实现对音频文件标签信息的写入的,具体实现方法放在了我的github仓库中,喜欢的话可以给我点个小星星,下面是这次用到的代码、dll以及资源文件的网盘链接(提取码:fl4m):链接