本帖最后由 baizy77 于 2019-7-11 21:16 编辑
版权声明 ---------------------------------------------------------------------------------------------------------------------------- 作者: 女儿叫老白 转载请注明出处! ----------------------------------------------------------------------------------------------------------------------------
网页版课程源码 提取码:1uy7
引言 ------------------------------------------------------------------------------------ 我们经常在网页上看到有些菜单弹出时带有动画效果,看上去让人耳目一新,那么在Qt中能否实现类似效果呢?本节我们为读者介绍如何实现菜单的动画弹出效果。
正文 ------------------------------------------------------------------------------------ 菜单的动画效果可以依赖Qt的动画机制实现。Qt提供了QPropertyAnimation类,该类依赖Qt的property机制实现动画。Qt的类体系中,只要从QObject类派生而来,就都支持属性访问,比如下面的QMenu类: 代码清单09-15-01 qmenu.h
- class Q_WIDGETS_EXPORT QMenu : public QWidget
- {
- private:
- Q_OBJECT
- Q_DECLARE_PRIVATE(QMenu)
- Q_PROPERTY(bool tearOffEnabled READ isTearOffEnabled WRITE setTearOffEnabled)
- Q_PROPERTY(QString title READ title WRITE setTitle)
- Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
- Q_PROPERTY(bool separatorsCollapsible READ separatorsCollapsible WRITE setSeparatorsCollapsible)
- Q_PROPERTY(bool toolTipsVisible READ toolTipsVisible WRITE setToolTipsVisible)
- // ......
- };
复制代码代码清单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 - class Q_WIDGETS_EXPORT QWidget : public QObject, public QPaintDevice
- {
- Q_OBJECT
- Q_DECLARE_PRIVATE(QWidget)
- Q_PROPERTY(bool modal READ isModal)
- Q_PROPERTY(Qt::WindowModality windowModality READ windowModality WRITE setWindowModality)
- Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
- Q_PROPERTY(QRect geometry READ geometry WRITE setGeometry)
- Q_PROPERTY(QRect frameGeometry READ frameGeometry)
- Q_PROPERTY(QRect normalGeometry READ normalGeometry)
- Q_PROPERTY(int x READ x)
- Q_PROPERTY(int y READ y)
- Q_PROPERTY(QPoint pos READ pos WRITE move DESIGNABLE false STORED false)
- // ......
- };
复制代码
要实现菜单的动画效果,我们还要把菜单从menubar()移除并且改为使用一个按钮弹出和隐藏菜单(见图09-15-01中的file Menu按钮)。 本节分为两步介绍菜单的动画效果。
首先,构造菜单、触发菜单的按钮(file Menu按钮) 构造菜单和按钮跟普通的主窗体程序开发基本一样,只是构造菜单时为其指定的parent为NULL。 我们先来看头文件。 代码清单09-15-03 mainwindow.h - class CMainWindow : public QMainWindow
- {
- private slots:
- void slot_fileMenu();
- void slot_openFile();
- void slot_saveFile();
- void slot_closeFile();
- void slot_exit();
- private:
- void createMenus(); /// 构建菜单
- private:
- QMenu *m_pFileMenu; /// 文件菜单
- QAction *m_pFileMenuAct; /// 文件菜单
- QAction *m_pOpenFileAct; /// 打开文件
- QAction *m_pSaveFileAct; /// 保存文件
- QAction *m_pCloseFileAct; /// 关闭文件
- QAction *m_pExitAct; /// 退出
- };
复制代码在代码清单09-15-03中,我们只列出了新增菜单、菜单项目、触发按钮的定义(粗体、斜体部分的代码)。 其中第12行是文件菜单对象(也就是要执行动画效果的菜单); 第13行是触发文件菜单显示、隐藏的按钮对应的QAction对象; 第14~17行是文件菜单的菜单项,用来演示; 第4行是触发按钮单击时对应的槽函数; 第5~8行是各个菜单项对应的槽函数; 第10行是构建菜单的接口;
接着,看一下CMainWindow实现。
代码清单09-15-04
mainwindow.cpp - CMainWindow::CMainWindow(QWidget* parent) :
- QMainWindow(parent),
- m_pAnimaMenuShow(NULL),
- m_bShowMenu(true)
- {
- initialize();
- createActions();
- createMenus();
- createToolBars();
- createStatusBar();
- connectSignalAndSlot();
- setWindowTitle(tr("Demo"));
- setMinimumSize(160, 160);
- //resize(480, 320);
- showMaximized();
- }
复制代码在代码清单09-15-04的构造函数中,我们在第9行增加了构建菜单接口createMenus()的调用。 封掉了第16行的resize()调用,改为第17行的最大化显示窗口。最大化的目的是为了美观,否则一个非最大化的窗体使用动画弹出一个菜单感觉有点怪,当然,其实最主要的目的是为了简化坐标的计算,这一点后面我们会介绍。
代码清单09-15-05 mainwindow.cpp - void CMainWindow::createActions()
- {
- // ......
- // file operation action
- m_pFileMenuAct = new QAction(tr("file Menu"), this);
- m_pFileMenuAct->setStatusTip(tr("file Menu"));
- connect(m_pFileMenuAct, &QAction::triggered, this, &CMainWindow::slot_fileMenu);
-
- // file operation action
- m_pOpenFileAct = new QAction(tr("openFile"), this);
- m_pOpenFileAct->setStatusTip(tr("openFile"));
- connect(m_pOpenFileAct, &QAction::triggered, this, &CMainWindow::slot_openFile);
- m_pSaveFileAct = new QAction(tr("saveFile"), this);
- m_pSaveFileAct->setStatusTip(tr("saveFile"));
- connect(m_pSaveFileAct, &QAction::triggered, this, &CMainWindow::slot_saveFile);
-
- m_pCloseFileAct = new QAction(tr("closeFile"), this);
- m_pCloseFileAct->setStatusTip(tr("closeFile"));
- connect(m_pCloseFileAct, &QAction::triggered, this, &CMainWindow::slot_closeFile);
-
- m_pExitAct = new QAction(tr("exit"), this);
- m_pExitAct->setStatusTip(tr("exit"));
- connect(m_pExitAct, &QAction::triggered, this, &CMainWindow::slot_exit);
- }
- void CMainWindow::createToolBars()
- {
- // ......
- m_pEditToolBar->addAction(m_pFileMenuAct);
- // ......
- }
- void CMainWindow::createMenus()
- {
- m_pFileMenu = new QMenu(tr("&File"), this);
- m_pFileMenu->setMouseTracking(true); //接收鼠标事件
- //flag设置为tool和无边框,消除qmenu的popup效果
- m_pFileMenu->setWindowFlags(Qt::CustomizeWindowHint | Qt::Tool | Qt::FramelessWindowHint);
- m_pFileMenu->addAction(m_pOpenFileAct);
- m_pFileMenu->addAction(m_pCloseFileAct);
- m_pFileMenu->addAction(m_pSaveFileAct);
- m_pFileMenu->addAction(m_pExitAct);
- }
复制代码代码清单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 - void CMainWindow::slot_openFile()
- {
- QMessageBox::information(this, "menu", "openFile triggered!");
- }
- void CMainWindow::slot_saveFile()
- {
- }
- void CMainWindow::slot_closeFile()
- {
- }
- void CMainWindow::slot_exit()
- {
- }
复制代码
在代码清单09-15-06中,我们提供了各个菜单项对应的槽函数,显然这些槽函数大部分都是假的(空实现)。 至此为止,我们的第一步构建菜单的工作结束。下面进入第二步。
然后,利用QPropertyAnimation实现动画效果
代码清单09-15-07 mainwindow.cpp - class QPropertyAnimation;
- class CMainWindow : public QMainWindow
- {
- // ......
- QPropertyAnimation* m_pAnimaMenuShow; /// 菜单动画
- bool m_bShowMenu; /// 显示菜单
- };
复制代码 在代码清单09-15-07中,我们在第1行对类QPropertyAnimation进行声明,因为后面要用该类定义对象。使用声明而非引入头文件的目的是减少头文件依赖,只有在对象真正构造时才需要引入头文件; 在第5行,定义QPropertyAnimation对象; 第6行定义一个bool量用来表明现在处于显示菜单状态还是隐藏菜单状态。
代码清单09-15-08
mainwindow.cpp - CMainWindow::CMainWindow(QWidget* parent) :
- QMainWindow(parent),
- m_pAnimaMenuShow(NULL),
- m_bShowMenu(true)
- {
- // ......
- }
复制代码在代码清单09-15-08中,在CMainWindow的构造函数的初始化列表中对新增的两个变量初始化。 代码清单09-15-09
mainwindow.cpp - void CMainWindow::slot_fileMenu()
- {
- static int s_MenubarHeight = menuBar()->height();
- int s_MenuWidth = m_pFileMenu->width();
- if (NULL == m_pAnimaMenuShow) {
- m_pAnimaMenuShow = new QPropertyAnimation(m_pFileMenu, "pos");
- connect(m_pAnimaMenuShow, SIGNAL(finished()), this, SLOT(slot_animationMenuFinished()));
- m_pAnimaMenuShow->setDuration(400);
- m_pAnimaMenuShow->setEasingCurve(QEasingCurve::Linear);
- }
- if (m_bShowMenu) { // 单击时弹出菜单
- m_pFileMenu->show();
- int offsetY = s_MenubarHeight + m_pEditToolBar->height();
- m_pAnimaMenuShow->setStartValue(QPoint(x()- s_MenuWidth, y()+offsetY));
- m_pAnimaMenuShow->setEndValue(QPoint(x(), y() + offsetY));
- m_pAnimaMenuShow->start();
- }
- else { // 再次单击时隐藏菜单
- int offsetY = s_MenubarHeight + m_pEditToolBar->height();
- m_pAnimaMenuShow->setStartValue(QPoint(x(), y() + offsetY));
- m_pAnimaMenuShow->setEndValue(QPoint(x()- s_MenuWidth, y()+offsetY));
- m_pAnimaMenuShow->start();
- }
- }
复制代码
代码清单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 - void CMainWindow::slot_animationMenuFinished()
- {
- if (!m_bShowMenu) {
- m_pFileMenu->hide();
- }
- m_bShowMenu = !m_bShowMenu;
- }
复制代码 代码清单09-15-10是动画结束时触发的槽函数,在该槽函数中,首先判断是否需要将菜单隐藏,然后将变量m_bShowMenu取反,以便达到显示-隐藏-显示-隐藏的循环效果。
最后,为了让界面看起来更酷,我们使用了样式文件,样式不是本节的重点,我们仅列出代码: 代码清单09-15-11
main.cpp - int main(int argc, char * argv[])
- {
- // ......
- QFile file(":/qss/black.qss");
- bool bok = file.open(QFile::ReadOnly);
- if (bok) {
- QString styleSheet = QString::fromLatin1(file.readAll());
- qApp->setStyleSheet(styleSheet);
- }
- file.close();
- return app.exec();
- }
复制代码
结语 ------------------------------------------------------------------------------------
本文介绍了使用QPropertyAnimation实现菜单以动画效果显示、隐藏的方法,重点在代码清单09-15-09中:QPropertyAnimation对象的构造、设置起止参数、设置持续时间等。
|