1、信号与槽
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton button("Quit");
QObject::connect(&button, &QPushButton::clicked, &app, &QApplication::quit);
button.show();
return app.exec();
}
connect()函数会创建一种连接,将发送者的信号和接收者的槽函数连接起来。 connect()函数最常用的一般形式:
connect(sender, signal, receiver, slot);
参数:
- sender:发出信号的对象
- signal:发送对象发出的信号
- receiver:接收信号的对象
- slot:接收对象在接收到信号之后所需要调用的函数
信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
自定义信号和槽
使用connect()可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还会允许我们自己设计自己的信号和槽。
例,创建一个工程,包含两个窗口,一个主窗口和一个子窗口,连接子窗口的自定义信号和主窗口的自定义槽函数。子窗口按键按下发出自定义信号,主窗口接收到信号后,调用主窗口的自定义槽函数。
自定义信号注意点:
- 信号必须有signals关键字来声明
-
信号没有返回值,可以有参数
- 信号就是函数的声明,没有定义
-
使用: emit mySignal(); 调用函数前加emit关键字。emit 是 Qt 对 C++ 的扩展,是一个关键字(其实也是一个宏)。emit 的含义是发出,也就是发出newPaper()信号
lambed表达式
// Lambda表达式
// C++11新特性, 项目文件 添加:CONFIG += C++11
// Qt配合信号一起使用
QPushButton *b4 = new QPushButton(this);
b4->setText("Lambed表达式");
b4->move(100,50);
connect(b4,&QPushButton::clicked,
// = :把外部所有局部变量、类中的所有成员以值传递方式
// this:类中所有成员以值传递方式
// & :把外部所有局部变量, 引用方式
[=]()
{
qDebug() << "11111" << endl;
b4->setText("擦仨4");
}
);
2、常用控件
// 菜单栏 获取菜单栏
QMenuBar *mBar = menuBar();
setMenuBar(mBar);
//添加菜单
QMenu *pFileMenu = mBar->addMenu("文件");
// 添加菜单项
QAction *pNew = pFileMenu->addAction("新建");
QAction *pOpen = pFileMenu->addAction("打开");
QAction *pSave = pFileMenu->addAction("保存");
pFileMenu->addSeparator(); // 添加分割线
QAction *pExit = pFileMenu->addAction("退出");
QMenu *pHelpMenu = mBar->addMenu("帮助");
QAction *pHelp = pHelpMenu->addAction("帮助");
QAction *pAbout = pHelpMenu->addAction("关于");
connect(pExit,&QAction::triggered,this,&MainWindow::close);
connect(pSave,&QAction::triggered,
[=]()
{
qDebug() << "保存~~~~~";
}
);
// 工具栏 添加工具栏
QToolBar *pToolbar = this->addToolBar("工具栏");
pToolbar->addAction(pNew);
pToolbar->addAction(pOpen);
pToolbar->addAction(pSave);
QPushButton *pButton = new QPushButton(this);
pButton->setText("退出");
pToolbar->addWidget(pButton);
connect(pButton,&QPushButton::clicked,this,&MainWindow::close);
QToolBar *pToolbar1 = this->addToolBar("工具栏");
pToolbar1->addAction(pNew);
// 状态栏 获取状态栏
QStatusBar *pStatus = this->statusBar();
QLabel *pLabel = new QLabel(this);
pLabel->setText("第 1 行 , 第 1 列");
pStatus->addPermanentWidget(pLabel); // 从右往左添加
pStatus->addWidget(new QLabel("text",this));
pStatus->addWidget(new QLabel("edit",this));
// 核心控件
QTextEdit *pText = new QTextEdit(this);
setCentralWidget(pText);
// 浮动窗口
QDockWidget *pDock = new QDockWidget(this);
addDockWidget(Qt::RightDockWidgetArea,pDock);
QTextEdit *pTextDock = new QTextEdit(this);
pDock->setWidget(pTextDock);
// 模态对话框
connect(pNew,&QAction::triggered,
[=]()
{
QDialog pDial;
pDial.exec();
qDebug() << "新建~~~~~";
}
);
// 非模态对话框
connect(pHelp,&QAction::triggered,
[=]()
{
QDialog *pDial = new QDialog;
pDial->setAttribute(Qt::WA_DeleteOnClose);
pDial->show();
qDebug() << "帮助~~~~~";
}
);
// 标准对话框
connect(pAbout,&QAction::triggered,
[=]()
{
QMessageBox::about(this,"关于本应用","作为测试使用\n 作者:SZA");
qDebug() << "关于~~~~~";
}
);
QAction *pQuestion = pHelpMenu->addAction("提问");
connect(pQuestion,&QAction::triggered,
[=]()
{
int ret = QMessageBox::question(this,"提问","Are u ok?",QMessageBox::Ok,QMessageBox::Yes,QMessageBox::No);
switch(ret){
case QMessageBox::Yes:
qDebug() << "I'm ok.(answer yes)";
break;
case QMessageBox::No:
qDebug() << "I'm not.(answer no)";
break;
case QMessageBox::Ok:
qDebug() << "I'm ok.(answer ok)";
break;
default:
break;
}
}
);
//文件对话框
connect(pOpen,&QAction::triggered,
[=]()
{
QString sPath = QFileDialog::getOpenFileName(this,"open","../",
"Source (*.cpp *.h);;"
"Text files (*.txt);;"
"All(*.*)");
qDebug() << "打开~~~~~";
if(sPath != nullptr)
{
qDebug() << sPath;
}
}
);
3、事件
事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler)。
在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如
- keyPressEvent()
- keyReleaseEvent()
- mouseDoubleClickEvent()
- mouseMoveEvent()
- mousePressEvent()
- mouseReleaseEvent() 等。
这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。例如时间事件。如下程序中设置定时器,每过1000ms触发一次事件。
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
public:
QPushButton *button;
public slots:
void button_slot();
protected:
void timerEvent(QTimerEvent *event);
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QTimer *timer = new QTimer(this);
this->startTimer(1000);
}
void MainWindow::timerEvent(QTimerEvent *event)
{
qDebug() << "timer";
}
4、资源文件的使用
Qt 资源系统是一个跨平台的资源机制,用于将程序运行时所需要的资源以二进制的形式存储于可执行文件内部。如果你的程序需要加载特定的资源(图标、文本翻译等),那么,将其放置在资源文件中,就再也不需要担心这些文件的丢失。也就是说,如果你将资源以资源文件形式存储,它是会编译到可执行文件内部。
使用 Qt Creator 可以很方便地创建资源文件。我们可以在工程上点右键,选择“添加新文件…”,可以在 Qt 分类下找到“Qt 资源文件”:
右侧的编辑区有个“添加”,我们首先需要添加前缀,比如我们将前缀取名为 /。然后选中这个前缀,继续点击添加文件,可以找到我们所需添加的文件。这里,我们选择 pic1.jpg 文件。那么就可以使用 :/pictures/pic1.jpg找到这个文件。
5、画图
Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于QPainter,QPainterDevice和QPaintEngine三个类。我们可以把QPainter理解成画笔;把QPaintDevice理解成使用画笔的地方,比如纸张、屏幕等;QPaintEngine提供了画笔(QPainter)在不同的设备上进行绘制的统一的接口,一般不用关心。
绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和 QPicture。其中,
- QPixmap专门为图像在屏幕上的显示做了优化
- QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
- QImage专门为图像的像素级访问做了优化。
- QPicture则可以记录和重现QPainter的各条命令。
void MainWindow::paintEvent(QPaintEvent *event)
{
QPixmap pix1(":/pictures/pic1.jpg");
QPainter painter(this);
painter.drawPixmap(0,0,pix1);
}