1、自定义控件 提供外部接口
form.h的类中声明
#ifndef FORM_H
#define FORM_H
#include <QWidget>
namespace Ui {
class Form;
}
class Form : public QWidget
{
Q_OBJECT
public:
explicit Form(QWidget *parent = 0);
//提供设置slider的value接口
void mySetValue(int value);
//提供获取slider的value接口
int myGetValue();
~Form();
private:
Ui::Form *ui;
};
#endif // FORM_H
form.cpp
#include "form.h"
#include "ui_form.h"
Form::Form(QWidget *parent) :
QWidget(parent),
ui(new Ui::Form)
{
ui->setupUi(this);
void (QSpinBox::*p)(int i) = &QSpinBox::valueChanged;
connect(ui->spinBox,p,ui->horizontalSlider,&QSlider::setValue);
connect(ui->horizontalSlider,&QSlider::valueChanged,ui->spinBox,&QSpinBox::setValue);
}
void Form::mySetValue(int value)
{
//设置进度条slider的值
ui->horizontalSlider->setValue(value);
}
int Form::myGetValue()
{
//获取进度条的值
return ui->horizontalSlider->value();
}
Form::~Form()
{
delete ui;
}
mywindow.cpp的构造函数
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <form.h>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//单击button 设置一半
connect(ui->pushButton,&QPushButton::clicked,[=](){
ui->widget->mySetValue(50);
});
//单击button 获取进度条的值
connect(ui->pushButton_2,&QPushButton::clicked,[=](){
qDebug() << ui->widget->myGetValue() << endl;
});
}
MainWindow::~MainWindow()
{
delete ui;
}
运行结果:
2、事件的概述
Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler),关于这一点,会在后边详细说明。
用户如果使用该事件,由于事件函数是virtual必须用户实现
3、通过QLabel了解事件的操作
如果重写QLabel事件,需要在自定义的label控件中实现时间的重写
在mylabel.h中修改如下:
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>
class MyLabel : public QLabel
{
Q_OBJECT
public:
explicit MyLabel(QWidget *parent = 0);
signals:
public slots:
};
#endif // MYLABEL_H
在mylabel.cpp修改如下:
#include "mylabel.h"
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
}
提升label的类型为MyLabel
总结:如果像重写某个类的事件,一般情况就要自定义一个类继承于该控件的类型,然后将控件的类提升成自定义的类,这样我们就可以在自定义类中重写控件类型时间函数。
重写事件:
事件资料:
mylabel.h:
运行结果:
重写鼠标按下事件:
mylabel.h:
获取鼠标按下或移动的坐标:
查看QMouseEvent的信息
判断button的返回值:
判断鼠标的哪个按键按下:
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
//ev包含了我们所需要的坐标信息
if(ev->button() == Qt::LeftButton){
qDebug() << "鼠标左键按下了" << endl;
}else if(ev->button() == Qt::RightButton){
qDebug() << "鼠标右键按下了" << endl;
}
}
运行结果:
判断鼠标的坐标:
void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton){
}
qDebug() << "鼠标移动了 x = " << ev->x() << ",y ="<< ev->y()<< endl;
}
运行结果:
鼠标跟踪:(用户不用按下鼠标 也可以获得鼠标移动位置,在QWidget中public functions中找到)
#include "mylabel.h"
#include <QDebug>
#include <QMouseEvent>
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
this->setMouseTracking(true);
}
void MyLabel::enterEvent(QEvent *event)
{
qDebug() << "鼠标进入了" << endl;
}
void MyLabel::leaveEvent(QEvent *event)
{
qDebug() << "鼠标离开了" << endl;
}
void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton){
}
qDebug() << "鼠标移动了 x = " << ev->x() << ",y ="<< ev->y()<< endl;
}
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
//ev包含了我们所需要的坐标信息
if(ev->button() == Qt::LeftButton){
qDebug() << "鼠标左键按下了" << endl;
}else if(ev->button() == Qt::RightButton){
qDebug() << "鼠标右键按下了" << endl;
}
}
运行结果:
4、事件分发器
事件对象创建完毕后,Qt将对这个事件对象传递给QObject的event()的函数。event()函数并不直接处理事件,而是将这些事件按照不同的类型,分发给不同事件处理器(event hanler).
event()函数主要用于事件的分发
如果你希望在事件分发之前作一些操作,就可以重写这个event()函数了
1、如果传入的事件已被识别并且处理,则需要返回true,否则返回false。如果返回值是true,那么Qt会认为这个事件已经处理完毕,不会再将这个事件发送给其他对象,而是会继续处理事件队列中的下一事件。
2、在event()函数中,调用事件对象的accept()和ignore()函数是没有作呕用的,不会影响到事件的传播。
3、不关心的事件,记得用父类的事件分发器进行处理
案件:在事件分发器中,处理鼠标单击事件
1、重写分发器事件(MyLabel)
virtual bool event(QEvent *e)
2、判断初始鼠标单击:
3、mylabel.h中重写事件分发器
//事件分发器,e存放具体的事件
bool MyLabel::event(QEvent *e)
{
//只关心鼠标按下事件
if(e->type() == QEvent::MouseButtonPress){
qDebug() << "在事件分发器中鼠标单击了" << endl;
//注意:QEvent没有x,y
QMouseEvent *ev = (QMouseEvent *)e;
qDebug() << "x ="<< ev->x() << ",y ="<< ev->y()<< endl;
return true;
}
//请调用父类的QLabel的event处理其它事件
return QLabel::event(e);
}
2、重写事件过滤器
通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器
不想让他继续转发,就返回true,否则返回false
在QObject类中Public Functions找到:
1、在MyLabel.cpp的构造函数中加载事件过滤器
2、重写事件过滤器
在mylabel.h中声明事件过滤器函数
在mylabel.cpp中实现 事件过滤器函数
运行结果:
注意:
1、这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。尽管很强大,但这种行为严重降低整个应用程序的事件分发效率。
2、事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两哥组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。
5、定时器—QTimer控件
三种定时器触发方式
1、定时器事件触发
启动定时器
2、定时器对象触发
3、静态成员函数
案例1:定时器事件
QObject中的定时器的使用,需要用到三个函数
int QObject::startTimer ( int interval ) ;
这个是开启一个定时器的函数,他的参数interval是毫秒级别。当开启成功后会返回这个定时器的ID, 并且每隔interval 时间后会进入timerEvent 函数。直到定时器被杀死。
void QObject::timerEvent ( QTimerEvent * event );
当定时器超时后,会进入该事件timerEvent函数,需要重写timerEvent函数,在函数中通过判断event->timerId()来确定定时器,然后执行某个定时器的超时函数
void QObject::killTimer ( int id );
通过从startTimer返回的ID传入killTimer 函数中杀死定时器,结束定时器进入超时处理。
所有QObject的子类都具有定时器的功能
在mainwindow.h中:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
//重写定时器事件
virtual void timerEvent(QTimerEvent *e);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
在mainwindow.cpp中:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
int MainWindow::id = 0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//启动定时器事件
int m = this->startTimer(1000);
}
//重写定时器分发事件
void MainWindow::timerEvent(QTimerEvent *e)
{
static int time = 0;
id++;
//将计数结果在在界面显示
ui->label_2->setText(QString::number(time++));
//在计数次数超过20次时,中断计数
if(id > 20){
this->killTimer(e->timerId());
}
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
qDebug() << "鼠标进入了" << endl;
}
运行结果:
案例2:同时启动多个计时器
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
static int id;
explicit MainWindow(QWidget *parent = 0);
//重写定时器事件
virtual void timerEvent(QTimerEvent *e);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
int id1;
int id2;
};
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
int MainWindow::id = 0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//启动定时器事件
id1 = this->startTimer(1000);
id2 = this->startTimer(2000);
}
//重写定时器分发事件
void MainWindow::timerEvent(QTimerEvent *e)
{
static int time1 = 0;
static int time2 = 0;
//id++;
if(e->timerId() == id1){
//将计数结果在在界面显示
ui->label_2->setText(QString::number(++time1));
}else if(e->timerId() == id2){
ui->label_3->setText(QString::number(++time2));
}
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
qDebug() << "鼠标进入了" << endl;
}
运行结果:
案例3:通过定时器对象定时
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QTimer>
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
static int id;
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::MainWindow *ui;
int id1;
int id2;
//定义一个定时器对象
QTimer *t;
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QTimer>
int MainWindow::id = 0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//实例化QTimer对象
t = new QTimer(this);
static int time2 = 0;
//timeout()为信号函数,定时器超时函数,每超过定时器定义时间,该信号函数执行
connect(t,&QTimer::timeout,[=](){
ui->label_3->setText(QString::number(++time2));
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
//启动定时器
//时间间隔为100ms
//每隔1000ms,定时器myTimer自动触发timeout()
//如果定时器没有激活,才启动,防止多次点击start按钮出现错误
if(t->isActive() == false){
t->start(1000);
}
}
void MainWindow::on_pushButton_2_clicked()
{
//暂停定时器
if(t->isActive() == true){
t->stop();
}
}
运行结果:
案例4:通过静态成员函数singleShot 实现延时的功能
mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
//#include <QTimer>
int MainWindow::id = 0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//实例化QTimer对象
t = new QTimer(this);
static int time2 = 0;
//timeout()为信号函数,定时器超时函数,每超过定时器定义时间,该信号函数执行
connect(t,&QTimer::timeout,[=](){
ui->label_3->setText(QString::number(++time2));
});
QTimer::singleShot(5000,[=](){
ui->label_4->setText("哈哈,我喜欢你");
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
//启动定时器
//时间间隔为100ms
//每隔1000ms,定时器myTimer自动触发timeout()
//如果定时器没有激活,才启动,防止多次点击start按钮出现错误
if(t->isActive() == false){
t->start(1000);
}
}
void MainWindow::on_pushButton_2_clicked()
{
//暂停定时器
if(t->isActive() == true){
t->stop();
}
}
运行结果: