如何为QDialog实现缩放动画?现有方案模态问题求解决
QDialog平滑缩放弹出动画实现方案
针对你遇到的QDialog缩放动画无法使用模态exec()的问题,提供两种可行方案:
方案一:修复QGraphicsView的模态问题
如果你想保留QGraphicsView的实现方式,可以通过设置模态属性+事件循环来模拟exec()的效果:
修改后代码
from PyQt6.QtWidgets import (QApplication, QDialog, QStyleFactory, QGraphicsView, QGraphicsScene, QGraphicsProxyWidget) from PyQt6.QtCore import Qt, QPropertyAnimation, QEasingCurve, QEventLoop from PyQt6 import uic import sys import os class ExpandingDialog(QDialog): def __init__(self): super().__init__() uic.loadUi("Error.ui", self) self.setWindowFlag(Qt.WindowType.FramelessWindowHint) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self.hide() # 隐藏原Dialog,不直接显示 # 创建Graphics组件 self.scene = QGraphicsScene() self.view = QGraphicsView(self.scene) self.view.setWindowFlags(Qt.WindowType.FramelessWindowHint) self.view.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self.view.setStyleSheet("background: transparent; border: none;") self.view.setFixedSize(self.width(), self.height()) # 设置view为应用级模态 self.view.setWindowModality(Qt.WindowModality.ApplicationModal) self.proxy_widget = QGraphicsProxyWidget() self.proxy_widget.setWidget(self) self.scene.addItem(self.proxy_widget) # 同步view和原Dialog的关闭状态 self.view.closeEvent = lambda event: self.close() def showEvent(self, event): self.start_animation() self.view.show() # 启动事件循环等待view关闭,实现模态效果 loop = QEventLoop() self.view.destroyed.connect(loop.quit) loop.exec() def start_animation(self): # 缩放动画 self.scale_animation = QPropertyAnimation(self.proxy_widget, b"scale") self.scale_animation.setDuration(600) self.scale_animation.setStartValue(0.0) self.scale_animation.setEndValue(1.0) self.scale_animation.setEasingCurve(QEasingCurve.Type.OutCubic) # 透明度动画 self.opacity_animation = QPropertyAnimation(self.proxy_widget, b"opacity") self.opacity_animation.setDuration(300) self.opacity_animation.setStartValue(0.0) self.opacity_animation.setEndValue(1.0) self.scale_animation.start() self.opacity_animation.start() if __name__ == "__main__": os.environ['QT_ENABLE_HIGHDPI_SCALING'] = '0' os.environ['QT_FONT_DPI'] = '120' app = QApplication(sys.argv) app.setStyle(QStyleFactory.create('windowsvista')) dialog = ExpandingDialog() dialog.exec()
方案二:无需QGraphicsView,直接在QDialog实现缩放
这种方案更轻量,通过自定义scale属性,在绘制时应用缩放变换,完全保留QDialog的exec()模态特性:
实现代码
from PyQt6.QtWidgets import (QApplication, QDialog, QStyleFactory) from PyQt6.QtCore import Qt, QPropertyAnimation, QEasingCurve, pyqtProperty from PyQt6.QtGui import QTransform, QPainter from PyQt6 import uic import sys import os class ExpandingDialog(QDialog): def __init__(self): super().__init__() uic.loadUi("Error.ui", self) self.setWindowFlag(Qt.WindowType.FramelessWindowHint) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self._scale = 0.0 def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.RenderHint.Antialiasing) # 以窗口中心为原点应用缩放变换 transform = QTransform() center = self.rect().center() transform.translate(center.x(), center.y()) transform.scale(self._scale, self._scale) transform.translate(-center.x(), -center.y()) painter.setTransform(transform) super().paintEvent(event) @pyqtProperty(float) def scale(self): return self._scale @scale.setter def scale(self, value): self._scale = value self.update() def showEvent(self, event): self.start_animation() def start_animation(self): # 缩放动画 self.scale_animation = QPropertyAnimation(self, b"scale") self.scale_animation.setDuration(600) self.scale_animation.setStartValue(0.0) self.scale_animation.setEndValue(1.0) self.scale_animation.setEasingCurve(QEasingCurve.Type.OutCubic) # 透明度动画 self.opacity_animation = QPropertyAnimation(self, b"windowOpacity") self.opacity_animation.setDuration(300) self.opacity_animation.setStartValue(0.0) self.opacity_animation.setEndValue(1.0) self.scale_animation.start() self.opacity_animation.start() if __name__ == "__main__": os.environ['QT_ENABLE_HIGHDPI_SCALING'] = '0' os.environ['QT_FONT_DPI'] = '120' app = QApplication(sys.argv) app.setStyle(QStyleFactory.create('windowsvista')) dialog = ExpandingDialog() dialog.exec()
效果说明
- 两种方案都能实现你展示的中心缩放+淡入效果
- 方案二更简洁,无需额外的Graphics组件,且原生支持
exec()模态 - 替换代码中的
uic.loadUi("Error.ui", self)为你的UI文件路径即可使用
内容的提问来源于stack exchange,提问作者Edward
相关产品推荐
相关产品推荐

