看了一些QT的教程和视频,当时个人感觉都听懂了,但真正开发过程中好像不知道去用它的一些特性和库,或者说又忘记怎么使用了~这几年的开发经验告诉我,代码放在具体项目中才能记忆深刻(是一长串“无情”的代码直观呢?还是一个绚丽的界面和功能呢?)
刚好QT本身提供了非常多的示例源码,于是乎突发奇想,是不是把这些示例代码都搞懂,算不算熟悉QT了呢~(因为我之前有4年多的WPF和C++开发经验,所以示例代码看上去不是特别吃力,学习中总感觉QT是WPF的C++版,果然好的框架的设计模式都是潜移默化的)。但愿此系列可以坚持更新,fighting!!!
Movie Example(导入视频并控制播放)
1、功能描述:
如上图所示,下面一排控件有:视频导入、播放、暂停、重置、删除的功能;上面依次是视频播放窗口、是否FitToWindow、Current frame(播放条与视频同步);
2、代码实现:
从主函数开始看比较简单
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MoviePlayer player;
player.show();
return app.exec();
}
QApplication接受命令行参数,MoviePlayer生成实例show出来后,调用QApplication的exec方法循环~
MoviePlayer是本实例中比较关键的类:不仅要完成功能逻辑,还有布局代码;它的头文件比较清晰
#ifndef MOVIEPLAYER_H//预编译头
#define MOVIEPLAYER_H
#include <QWidget>
QT_BEGIN_NAMESPACE //使用QT的命名空间,类的前置声明以减少编译依赖
class QCheckBox;
class QGridLayout;
class QHBoxLayout;
class QLabel;
class QMovie;
class QSlider;
class QSpinBox;
class QToolButton;
class QVBoxLayout;
QT_END_NAMESPACE
class MoviePlayer : public QWidget
{
Q_OBJECT
public:
MoviePlayer(QWidget *parent = 0);
void openFile(const QString &fileName);
private slots:
void open();
void goToFrame(int frame);
void fitToWindow();
void updateButtons();
void updateFrameSlider();
private:
void createControls();
void createButtons();
/*
类的成员变量 略...
*/
};
#endif
比起原生的C++,上述代码中多了slots关键字,还有一个宏Q_OBJECT。在Qt的程序中如果使用了信号与反应槽就必须在类的定义中声明这个宏,不过如果你声明了该宏但在程序中并没有信号与槽,对程序也不会有任何影响(同样必须继承QObject,本例中的QWdiget是QObject的子类),这样修饰有什么用 这就不得不提QT中很重要的机制
信号与槽函数
信号(Signal)就是在特定情况下被发射的事件。
槽(Slot)就是对信号响应的函数。与一般的C++函数是一样的,可以定义在类的任何部分(public、private 或 protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
信号与槽函数怎么关联呢?QT中用QObject::connect() 函数实现
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
其中,sender 是发射信号的对象的名称,signal() 是信号名称。信号可以看做是特殊的函数,需要带括号,有参数时还需要指明参数。receiver 是接收信号的对象名称,slot() 是槽函数的名称,需要带括号,有参数时还需要指明参数。
SIGNAL 和 SLOT 是 Qt 的宏,用于指明信号和槽,并将它们的参数转换为相应的字符串。
回归示例
巴拉巴拉说了很多, 具体怎么使用到咱们的功能中呢?例如,上述功能中有一个是导入本地视频(我们想要点击文件夹Button弹出文件框供我们导入),有前端开发经验的不难想到,是不是可以捕获Button的点击事件,再执行相应逻辑;本例中open函数就是导入button的点击事件所关联的槽函数。代码如下添加
connect(openButton, SIGNAL(clicked()), this, SLOT(open()));
//open函数处理openButton实例发出的点击事件
//open槽函数的处理
void MoviePlayer::open()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open a Movie"),
currentMovieDirectory);
if (!fileName.isEmpty())
openFile(fileName);
}
按照这个套路或者说这种机制,我们可以先把所有需要关注的事件都与声明的槽函数关联,就有了如下代码
//movie的frameChanged与stateChanged事件
connect(movie, SIGNAL(frameChanged(int)), this, SLOT(updateFrameSlider()));
connect(movie, SIGNAL(stateChanged(QMovie::MovieState)),
this, SLOT(updateButtons()));
connect(fitCheckBox, SIGNAL(clicked()), this, SLOT(fitToWindow()));
//frameSlider speedSpinBox的valueChanged事件
connect(frameSlider, SIGNAL(valueChanged(int)), this, SLOT(goToFrame(int)));
connect(speedSpinBox, SIGNAL(valueChanged(int)),
movie, SLOT(setSpeed(int)));
//一些点击事件
connect(playButton, SIGNAL(clicked()), movie, SLOT(start()));
connect(stopButton, SIGNAL(clicked()), movie, SLOT(stop()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));
一切看上去都是那么合理~ 但有一个问题,button的点击事件我们很容易想到,但像movie的frameChanged与stateChanged事件从何得知呢?以我4年WPF的开发经验,这些类有哪些事件,哪些事件又是对我们有用的?这类问题没有很好的办法,只有多看帮助文档、多在项目中用到才能知道。
重要的类:首先是视频窗口类QMovie,构造函数形参是父控件指针;使用到的接口,
setCacheMode:设置缓存类型(QMovie::CacheAll与QMovie::None)
stop():顾名思义,停止
start():开始
state():当前状态
isValid():视频是否合法
setFileName:设置打开的文件路径
currentFrameNumber:当前帧数
goToFrame(int frame):调到第几帧
用于打开文件窗口QFileDialog::getOpenFileName
QString QFileDialog::getOpenFileName (
QWidget * parent = 0, //用于指定父组件
const QString & caption = QString(), //对话框的标题
const QString & dir = QString(), //对话框显示时默认打开的目录
const QString & filter = QString(), //对话框的后缀名过滤器
QString * selectedFilter = 0, //默认选择的过滤器
Options options = 0 ); //对话框的一些参数设定,比如只显示文件夹等
OK,可以具体看下槽函数的功能,点击open Button触发click事件,走到open()函数,打开文件Dialog,用户选择视频文件后走入openFile(name)函数,对QMovie对象设置新的属性后播放,更新FrameSlider与其他状态按钮。一条工作流END 还有一个功能fitToWindow,这里把QMovie包在movieLabel中,设置QLabel的setScaledContents属性即可设置是否自适应~
3、其他
读过设计模式的小伙伴应该熟悉,QT中的信号和槽其实是观察者模式(发布\订阅),观察者模式定义了一对多的依赖关系,当一个对象改变时,它的所有依赖者都会收到通知并自动更新。类似很多人订阅了一个公众号,一旦公众号发送消息,订阅的人都会收到消息。这样做的好处:减少了耦合,让代码结构更加的清晰~
WPF中常用的事件也是用的这种机制,当然QT中也有事件的概念,大同小异。上面示例代码看着不是很舒服,主要是它把布局代码与逻辑写在一起了(如何将设计与逻辑分开,类似WPF中的xmal),QT有自己的前台qml,继续学习吧~
个人感觉WPF很多设计和库都挺好用的,但跨平台做得不好再加上用的是C#,所以在国内不火。。。