KS09-15 菜单-动画效果
本帖最后由 baizy77 于 2019-7-11 21:16 编辑版权声明----------------------------------------------------------------------------------------------------------------------------该文章原创于Qter开源社区(www.qter.org)作者: 女儿叫老白转载请注明出处!----------------------------------------------------------------------------------------------------------------------------课程目录: 【独家连载】《Qt入门与提高-GUI产品开发》目录
网页版课程源码 提取码:1uy7
引言------------------------------------------------------------------------------------我们经常在网页上看到有些菜单弹出时带有动画效果,看上去让人耳目一新,那么在Qt中能否实现类似效果呢?本节我们为读者介绍如何实现菜单的动画弹出效果。
正文------------------------------------------------------------------------------------菜单的动画效果可以依赖Qt的动画机制实现。Qt提供了QPropertyAnimation类,该类依赖Qt的property机制实现动画。Qt的类体系中,只要从QObject类派生而来,就都支持属性访问,比如下面的QMenu类:代码清单09-15-01qmenu.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-02qwidget.hclass 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-03mainwindow.hclass 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.cppCMainWindow::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-05mainwindow.cppvoid 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.cppvoid 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-07mainwindow.cppclass QPropertyAnimation;
class CMainWindow : public QMainWindow
{
// ......
QPropertyAnimation* m_pAnimaMenuShow; /// 菜单动画
bool m_bShowMenu; /// 显示菜单
};
在代码清单09-15-07中,我们在第1行对类QPropertyAnimation进行声明,因为后面要用该类定义对象。使用声明而非引入头文件的目的是减少头文件依赖,只有在对象真正构造时才需要引入头文件; 在第5行,定义QPropertyAnimation对象; 第6行定义一个bool量用来表明现在处于显示菜单状态还是隐藏菜单状态。
代码清单09-15-08
mainwindow.cppCMainWindow::CMainWindow(QWidget* parent) :
QMainWindow(parent),
m_pAnimaMenuShow(NULL),
m_bShowMenu(true)
{
// ......
}
在代码清单09-15-08中,在CMainWindow的构造函数的初始化列表中对新增的两个变量初始化。代码清单09-15-09
mainwindow.cppvoid 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.cppvoid CMainWindow::slot_animationMenuFinished()
{
if (!m_bShowMenu) {
m_pFileMenu->hide();
}
m_bShowMenu = !m_bShowMenu;
}
代码清单09-15-10是动画结束时触发的槽函数,在该槽函数中,首先判断是否需要将菜单隐藏,然后将变量m_bShowMenu取反,以便达到显示-隐藏-显示-隐藏的循环效果。
最后,为了让界面看起来更酷,我们使用了样式文件,样式不是本节的重点,我们仅列出代码:代码清单09-15-11
main.cppint 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对象的构造、设置起止参数、设置持续时间等。
页:
[1]