找回密码
 立即注册
收起左侧

KS09-15 菜单-动画效果

0
回复
336
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2019-7-7 10:11:35 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 baizy77 于 2019-7-11 21:16 编辑

版权声明
----------------------------------------------------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白
转载请注明出处!
----------------------------------------------------------------------------------------------------------------------------

网页版课程源码 提取码:1uy7
引言
------------------------------------------------------------------------------------
我们经常在网页上看到有些菜单弹出时带有动画效果,看上去让人耳目一新,那么在Qt中能否实现类似效果呢?本节我们为读者介绍如何实现菜单的动画弹出效果。

正文
------------------------------------------------------------------------------------
菜单的动画效果可以依赖Qt的动画机制实现。Qt提供了QPropertyAnimation类,该类依赖Qt的property机制实现动画。Qt的类体系中,只要从QObject类派生而来,就都支持属性访问,比如下面的QMenu类:
代码清单09-15-01
qmenu.h

  1. class Q_WIDGETS_EXPORT QMenu : public QWidget
  2. {
  3. private:
  4.     Q_OBJECT
  5.     Q_DECLARE_PRIVATE(QMenu)

  6.     Q_PROPERTY(bool tearOffEnabled READ isTearOffEnabled WRITE setTearOffEnabled)
  7.     Q_PROPERTY(QString title READ title WRITE setTitle)
  8.     Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
  9.     Q_PROPERTY(bool separatorsCollapsible READ separatorsCollapsible WRITE setSeparatorsCollapsible)
  10. Q_PROPERTY(bool toolTipsVisible READ toolTipsVisible WRITE setToolTipsVisible)
  11. // ......
  12. };
复制代码
代码清单09-15-01摘自Qt的头文件qmenu.h。可以看出,第7~14行用宏Q_PROPERTY描述了QMenu类的属性,比如第7行的tearOffEnabled属性是个bool值,它的读访问接口为isTearOffEnabled,它的写访问接口为setTearOffEnabled。当然,QMenu还有继承自父类QWidget的属性,在此我们不再一一介绍。
Qt的动画类QPropertyAnimation就是通过修改类的属性值达到动画效果。比如在本节中我们如果想实现菜单动画弹出和动画退出的效果,就要修改它的pos属性,该属性在QWidget中提供,见代码清单09-15-02的第15行。
代码清单09-15-02
qwidget.h
  1. class Q_WIDGETS_EXPORT QWidget : public QObject, public QPaintDevice
  2. {
  3.     Q_OBJECT
  4.     Q_DECLARE_PRIVATE(QWidget)

  5.     Q_PROPERTY(bool modal READ isModal)
  6.     Q_PROPERTY(Qt::WindowModality windowModality READ windowModality WRITE setWindowModality)
  7.     Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
  8.     Q_PROPERTY(QRect geometry READ geometry WRITE setGeometry)
  9.     Q_PROPERTY(QRect frameGeometry READ frameGeometry)
  10.     Q_PROPERTY(QRect normalGeometry READ normalGeometry)
  11.     Q_PROPERTY(int x READ x)
  12.     Q_PROPERTY(int y READ y)
  13.     Q_PROPERTY(QPoint pos READ pos WRITE move DESIGNABLE false STORED false)
  14.     // ......
  15. };
复制代码

要实现菜单的动画效果,我们还要把菜单从menubar()移除并且改为使用一个按钮弹出和隐藏菜单(见图09-15-01中的file Menu按钮)。
本节分为两步介绍菜单的动画效果。

首先,构造菜单、触发菜单的按钮(file Menu按钮)
    构造菜单和按钮跟普通的主窗体程序开发基本一样,只是构造菜单时为其指定的parent为NULL。
    我们先来看头文件。
代码清单09-15-03
mainwindow.h
  1. class CMainWindow : public QMainWindow
  2. {        
  3. private slots:
  4.     void slot_fileMenu();
  5.     void slot_openFile();
  6.     void slot_saveFile();
  7.     void slot_closeFile();
  8.     void slot_exit();
  9. private:
  10.         void createMenus();                        /// 构建菜单
  11. private:
  12.         QMenu *m_pFileMenu;                        /// 文件菜单
  13.         QAction *m_pFileMenuAct;        /// 文件菜单
  14.         QAction *m_pOpenFileAct;        /// 打开文件
  15.     QAction *m_pSaveFileAct;        /// 保存文件
  16.     QAction *m_pCloseFileAct;        /// 关闭文件
  17.     QAction *m_pExitAct;            /// 退出
  18. };
复制代码
在代码清单09-15-03中,我们只列出了新增菜单、菜单项目、触发按钮的定义(粗体、斜体部分的代码)。
    其中第12行是文件菜单对象(也就是要执行动画效果的菜单);
    第13行是触发文件菜单显示、隐藏的按钮对应的QAction对象;
    第14~17行是文件菜单的菜单项,用来演示;
    第4行是触发按钮单击时对应的槽函数;
    第5~8行是各个菜单项对应的槽函数;
    第10行是构建菜单的接口;

    接着,看一下CMainWindow实现。

代码清单09-15-04

mainwindow.cpp
  1. CMainWindow::CMainWindow(QWidget* parent) :
  2.     QMainWindow(parent),
  3.     m_pAnimaMenuShow(NULL),
  4.     m_bShowMenu(true)
  5. {
  6.         initialize();

  7.         createActions();
  8.     createMenus();
  9.         createToolBars();
  10.         createStatusBar();
  11.         connectSignalAndSlot();

  12.         setWindowTitle(tr("Demo"));
  13.         setMinimumSize(160, 160);
  14.         //resize(480, 320);
  15.     showMaximized();
  16. }
复制代码
在代码清单09-15-04的构造函数中,我们在第9行增加了构建菜单接口createMenus()的调用。
    封掉了第16行的resize()调用,改为第17行的最大化显示窗口。最大化的目的是为了美观,否则一个非最大化的窗体使用动画弹出一个菜单感觉有点怪,当然,其实最主要的目的是为了简化坐标的计算,这一点后面我们会介绍。
代码清单09-15-05
mainwindow.cpp
  1. void CMainWindow::createActions()
  2. {
  3.    // ......
  4.    // file operation action
  5.     m_pFileMenuAct = new QAction(tr("file Menu"), this);
  6.     m_pFileMenuAct->setStatusTip(tr("file Menu"));
  7.     connect(m_pFileMenuAct, &QAction::triggered, this, &CMainWindow::slot_fileMenu);
  8.    
  9.     // file operation action
  10.     m_pOpenFileAct = new QAction(tr("openFile"), this);
  11.     m_pOpenFileAct->setStatusTip(tr("openFile"));
  12.     connect(m_pOpenFileAct, &QAction::triggered, this, &CMainWindow::slot_openFile);

  13.     m_pSaveFileAct = new QAction(tr("saveFile"), this);
  14.     m_pSaveFileAct->setStatusTip(tr("saveFile"));
  15.     connect(m_pSaveFileAct, &QAction::triggered, this, &CMainWindow::slot_saveFile);
  16.    
  17.     m_pCloseFileAct = new QAction(tr("closeFile"), this);
  18.     m_pCloseFileAct->setStatusTip(tr("closeFile"));
  19.     connect(m_pCloseFileAct, &QAction::triggered, this, &CMainWindow::slot_closeFile);
  20.   
  21.     m_pExitAct = new QAction(tr("exit"), this);
  22.     m_pExitAct->setStatusTip(tr("exit"));
  23.     connect(m_pExitAct, &QAction::triggered, this, &CMainWindow::slot_exit);
  24. }
  25. void CMainWindow::createToolBars()
  26. {
  27.         // ......
  28.     m_pEditToolBar->addAction(m_pFileMenuAct);
  29.     // ......
  30. }
  31. void CMainWindow::createMenus()
  32. {
  33.     m_pFileMenu = new QMenu(tr("&File"), this);
  34.     m_pFileMenu->setMouseTracking(true);  //接收鼠标事件
  35.     //flag设置为tool和无边框,消除qmenu的popup效果
  36.     m_pFileMenu->setWindowFlags(Qt::CustomizeWindowHint | Qt::Tool | Qt::FramelessWindowHint);

  37.     m_pFileMenu->addAction(m_pOpenFileAct);
  38.     m_pFileMenu->addAction(m_pCloseFileAct);
  39.     m_pFileMenu->addAction(m_pSaveFileAct);
  40.     m_pFileMenu->addAction(m_pExitAct);      
  41. }
复制代码
代码清单09-15-05中,我们修改了createActions()接口增加了各个菜单项的构造代码。
    并且,在第34行,我们在createToolBars()中将触发菜单显示、隐藏的按钮对应的m_pFileMenuAct对象添加到pEditToolBar工具栏。
    第37~49行是新增的createMenus(),在该接口中,我们构造了m_pFileMenu并且将各个菜单项追加到菜单m_pFileMenu。需要注意的是在第42行,我们将m_pFileMenu的窗体风格设置为Qt::CustomizeWindowHint | Qt::Tool | Qt::FramelessWindowHint,目的是取消它的Popup属性,防止单击其他位置时导致菜单消失(因为我们希望在单击按钮时才隐藏菜单)。

代码清单09-15-06

mainwindow.cpp
  1. void CMainWindow::slot_openFile()
  2. {
  3.     QMessageBox::information(this, "menu", "openFile triggered!");
  4. }
  5. void CMainWindow::slot_saveFile()
  6. {
  7. }
  8. void CMainWindow::slot_closeFile()
  9. {
  10. }
  11. void CMainWindow::slot_exit()
  12. {
  13. }
复制代码

在代码清单09-15-06中,我们提供了各个菜单项对应的槽函数,显然这些槽函数大部分都是假的(空实现)。
    至此为止,我们的第一步构建菜单的工作结束。下面进入第二步。

然后,利用QPropertyAnimation实现动画效果

代码清单09-15-07
mainwindow.cpp
  1. class QPropertyAnimation;
  2. class CMainWindow : public QMainWindow
  3. {
  4.     // ......
  5.     QPropertyAnimation* m_pAnimaMenuShow; /// 菜单动画
  6.     bool     m_bShowMenu;       /// 显示菜单
  7. };
复制代码
    在代码清单09-15-07中,我们在第1行对类QPropertyAnimation进行声明,因为后面要用该类定义对象。使用声明而非引入头文件的目的是减少头文件依赖,只有在对象真正构造时才需要引入头文件;
    在第5行,定义QPropertyAnimation对象;
    第6行定义一个bool量用来表明现在处于显示菜单状态还是隐藏菜单状态。

代码清单09-15-08

mainwindow.cpp
  1. CMainWindow::CMainWindow(QWidget* parent) :
  2.     QMainWindow(parent),
  3.     m_pAnimaMenuShow(NULL),
  4.     m_bShowMenu(true)
  5. {
  6.     // ......
  7. }
复制代码
在代码清单09-15-08中,在CMainWindow的构造函数的初始化列表中对新增的两个变量初始化。
代码清单09-15-09

mainwindow.cpp
  1. void CMainWindow::slot_fileMenu()
  2. {
  3.     static int s_MenubarHeight = menuBar()->height();
  4.     int s_MenuWidth = m_pFileMenu->width();
  5.     if (NULL == m_pAnimaMenuShow)    {
  6.         m_pAnimaMenuShow = new QPropertyAnimation(m_pFileMenu, "pos");
  7.         connect(m_pAnimaMenuShow, SIGNAL(finished()), this, SLOT(slot_animationMenuFinished()));
  8.         m_pAnimaMenuShow->setDuration(400);
  9.         m_pAnimaMenuShow->setEasingCurve(QEasingCurve::Linear);
  10.     }
  11.     if (m_bShowMenu) { // 单击时弹出菜单
  12.         m_pFileMenu->show();
  13.         int offsetY = s_MenubarHeight + m_pEditToolBar->height();
  14.         m_pAnimaMenuShow->setStartValue(QPoint(x()- s_MenuWidth, y()+offsetY));
  15.         m_pAnimaMenuShow->setEndValue(QPoint(x(), y() + offsetY));
  16.         m_pAnimaMenuShow->start();
  17.    }
  18.     else   { // 再次单击时隐藏菜单
  19.          int offsetY = s_MenubarHeight + m_pEditToolBar->height();
  20.         m_pAnimaMenuShow->setStartValue(QPoint(x(), y() + offsetY));
  21.         m_pAnimaMenuShow->setEndValue(QPoint(x()- s_MenuWidth, y()+offsetY));
  22.         m_pAnimaMenuShow->start();
  23.    }
  24. }
复制代码

代码清单09-15-09是本节的核心。
    第3行的代码是为了保存menubar的高度,方便后续使用:因为计算菜单的弹出位置时需要用到,以便将菜单在menubar和工具栏的下方弹出;
    第4行将菜单的宽度保存到一个变量中,同样是为了后面方便计算;
    第5~11行用来初始构建动画对象,其中:
        第6行构建m_pAnimaMenuShow,并提供了操作对象m_pFileMenu和操作的属性"pos"(也就是位置,我们通过修改菜单的位置实现动画移动菜单的效果)
        第7~8行将动画对象的finished信号绑定到槽函数,以便动画结束时执行额外的操作;
        第9行设置了动画持续时间,单位是ms(毫秒);
        第10行设置了动画进展曲线为QEasingCurve::Linear(与时间成线性关系);
    第12~19行规定了显示菜单时的动画参数:
        第13行先将菜单显示出来(因为,关闭菜单时会将菜单隐藏);
        第14行计算高度偏移量,菜单弹出时应该在menubar(标题栏)和工具栏的下方;
        第15行,设置动画的起始值,也就是菜单的"pos"(构造动画对象时的第二个参数,见第6行),将其设置为菜单默认位置向左、向下移动指定偏移量。读者如果不明白,可以尝试仅使用x()、y()看看效果。
        第17行指明动画结束时,菜单的位置。
    第20~26行设置了菜单隐藏时的动画参数,跟菜单显示时正好相反,在此不再赘述。

    下面,我们看一下动画结束时的槽函数。

代码清单09-15-10

mainwindow.cpp
  1. void CMainWindow::slot_animationMenuFinished()
  2. {
  3.     if (!m_bShowMenu) {
  4.         m_pFileMenu->hide();
  5.     }
  6.     m_bShowMenu = !m_bShowMenu;
  7. }
复制代码
    代码清单09-15-10是动画结束时触发的槽函数,在该槽函数中,首先判断是否需要将菜单隐藏,然后将变量m_bShowMenu取反,以便达到显示-隐藏-显示-隐藏的循环效果。

    最后,为了让界面看起来更酷,我们使用了样式文件,样式不是本节的重点,我们仅列出代码:
代码清单09-15-11

main.cpp
  1. int main(int argc, char * argv[])
  2. {  
  3.     // ......
  4.     QFile file(":/qss/black.qss");
  5.     bool bok = file.open(QFile::ReadOnly);
  6.     if (bok) {
  7.         QString styleSheet = QString::fromLatin1(file.readAll());
  8.         qApp->setStyleSheet(styleSheet);
  9.     }
  10.     file.close();
  11.     return app.exec();
  12. }
复制代码

结语
------------------------------------------------------------------------------------

本文介绍了使用QPropertyAnimation实现菜单以动画效果显示、隐藏的方法,重点在代码清单09-15-09中:QPropertyAnimation对象的构造、设置起止参数、设置持续时间等。

阿里云
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册