QT中的所有事件都是基于QEvent的。例如:QMouseEvent继承自QInputEvent,QInputEvent继承自QEvent。
一. 重写QT事件
1. 以Label做示范,在帮助手册中查询QLabel,搜索event,找到Label带有的事件,我们可以自定义Label然后重写这些事件。
2. 创建myLabel类,随意继承一个父类,然后在代码中将父类改为QLabel(此时要改三个位置:1. 导入的头文件; 2. 继承的父类; 3. cpp中的父类构造)。
3. 在ui中,右键点击Label,将其提升为我们自己的Label(先点击添加,后点击提升),之后就会看到右侧label对象所对应的类更改为myLabel。
4. 为了使Label显示得更直观,我们可以将QFrame中的frameShape从NoShape做修改,可以改为Box等。
5. 在mylabel.h里重写5个事件,并且在mylabel.cpp中实现,事件触发后打印到控制台。
6. QMouseEvent提供了鼠标在窗体内的x、y坐标以及全局的x、y坐标。
7. 可以通过ev->button()来判断按键;可以使用“==”也可以使用“&”;ev->button()可以返回组合件信息。
//鼠标进入
void enterEvent(QEvent *event);
//鼠标离开
void leaveEvent(QEvent *event);
//鼠标按下
void mousePressEvent(QMouseEvent *ev);
//鼠标松开
void mouseReleaseEvent(QMouseEvent *ev);
//鼠标移动
void mouseMoveEvent(QMouseEvent *ev);
//鼠标进入
void myLabel::enterEvent(QEvent *) {
qDebug() << "鼠标进入";
}
//鼠标离开
void myLabel::leaveEvent(QEvent *) {
qDebug() << "鼠标离开";
}
//鼠标按下
void myLabel::mousePressEvent(QMouseEvent *ev) {
qDebug() << "鼠标按下";
if (Qt::LeftButton & ev->button())
qDebug() << QString("x = %1, y = %2, global_x = %3, global_y = %4")
.arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
}
//鼠标松开
void myLabel::mouseReleaseEvent(QMouseEvent *ev) {
qDebug() << "鼠标松开";
if (Qt::LeftButton == ev->button())
qDebug() << QString("x = %1, y = %2, global_x = %3, global_y = %4")
.arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
}
//鼠标移动
void myLabel::mouseMoveEvent(QMouseEvent *ev) {
qDebug() << "鼠标移动";
qDebug() << QString("x = %1, y = %2, global_x = %3, global_y = %4")
.arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
}
二. 事件过滤器和事件分发器
将事件过滤器和事件分发器中做拦截时的返回值注释掉,可以观察到输出打印的顺序:事件分拦截器 -> 事件分发器 -> 重写的具体事件中的输出。
事件过滤器:
1. 要在Widget中进行编写,先返回QWidget中的eventFilter方法,否则就会拦截全部事件。
2. 使用ui->label->installEventFilter(this);安装事件过滤器。
3. 在重写的过滤器方法中使用obj参数与ui->label进行比较,判断事件是谁的。
4. 将QEvent强转为QMouseEvent来判断事件类型。
5. 调用ev->button()判断具体事件,返回true表示对事件进行拦截。
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
ui->label->installEventFilter(this);
}
//事件过滤器
bool Widget::eventFilter(QObject *watched, QEvent *event) {
if (obj == ui->label) { //判断事件是谁
if (QEvent::MouseButtonPress == e->type()) { //调用type方法判断事件类型
QMouseEvent *ev = static_cast(e);
if (Qt::LeftButton == ev->button()) {
qDebug() << "事件分发器里,捕获鼠标左键按下事件";
//return true;
}
}
}
return QWidget::eventFilter(watched, event); //需要调用父类的方法,否则就会将所有的事件全部过滤掉。
}
事件分发器:内容与上面代码相似,不多做赘述。
//事件分发器
bool myLabel::event(QEvent *e) {
if (QEvent::MouseButtonPress == e->type()) {
QMouseEvent *ev = static_cast(e);
if (Qt::LeftButton == ev->button()) {
qDebug() << "事件分发器里,捕获鼠标左键按下事件";
return true;
}
}
return QLabel::event(e); //调用父类的事件分发器正常分发事件
}
三. 定时器事件
定时器是操作提供提供的一个事件,不需要用户的操作来触发。在QT中,每个控件都有一个定时器事件,我们可以通过重写QWidget的定时器事件来事件现有控件的定时计划。
1. 使用定时器事件实现定时器;继承自QEvent,属于事件。
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void timerEvent(QTimerEvent *e);
static int num1;
static int num2;
int timerId1 = 0;
int timerId2 = 0;
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QFont ft;
ft.setPointSize(32);
ui->label->setFont(ft);
ui->label_2->setFont(ft);
this->timerId1 = this->startTimer(1000); //启动定时器
this->timerId2 = this->startTimer(2000);
}
int Widget::num1 = 0;
int Widget::num2 = 0;
Widget::~Widget()
{
delete ui;
}
void Widget::timerEvent(QTimerEvent *e) {
if (e->timerId() == this->timerId1) {
ui->label->setText(QString::number(Widget::num1++));
}
if (e->timerId() == this->timerId2) {
ui->label_2->setText(QString::number(Widget::num2++));
}
}
2. 使用定时器类实现定时器(使用较多);不属于事件,直接继承自QObject。
QTimer *timer = new QTimer(this);
timer->start(500);
connect(timer, &QTimer::timeout, [=](){
ui->label_3->setText(QString::number(Widget::num3++));
});
注意:
1. 可以设置图形凹陷和突出:frameShadow
2. QT全局变量类内全局变量需要在类外的全局区域内赋值,否则会报“未定义引用”的错误。
四. 绘图事件
绘图事件与定时器事件类似,一般也是由系统自动触发的。
1. 以下为画图事件的基本用法:画家、画笔、画刷。
void Widget::paintEvent(QPaintEvent *e) {
QPainter p(this); //请画家,在this窗口上进行图形绘制
QPen pen; //创建画笔
pen.setColor(QColor(255, 0, 0));
pen.setWidth(2);
pen.setStyle(Qt::DashLine);
p.setPen(pen); //画家拿笔
QBrush brush(Qt::green); //创建画刷
// brush.setColor(Qt::green);
brush.setStyle(Qt::Dense4Pattern);
p.setBrush(brush); //画家拿刷
p.drawLine(0, 0, 150, 150); //画直线
p.drawEllipse(QPoint(150, 150), 80, 50); //画椭圆
p.drawRect(30, 30, 60, 80); //画矩形
p.drawText(QRect(200, 200, 150, 50), "好好学习,天天向上"); //画字
}
2. 抗锯齿:效果好,性能差。
QPainter p(this); //请画家
p.drawEllipse(QPoint(100, 150), 80, 80);
p.setRenderHint(QPainter::Antialiasing); //抗锯齿
p.drawEllipse(QPoint(300, 150), 80, 80);
3. 画家位置:移动、保存与还原。
p.drawRect(30, 50, 50, 50);
p.translate(100 ,0); //移动画家
p.save(); //保存画家位置
p.drawRect(30, 50, 50, 50);
p.translate(100 ,0);
p.restore(); //恢复画家位置
p.drawRect(30, 50, 50, 50); //与第二个圆位置重合
4. 点击按钮图片移动:需要调用窗口的update函数来刷新窗口以触发绘图事件;绘图事件也会在窗口最小化和还原的时候触发。
connect(ui->pushButton, &QPushButton::clicked, [=](){
this->posX += 20;
if (this->posX > this->width()) {
this->posX = 0;
}
this->update();
});
void Widget::paintEvent(QPaintEvent *e) {
QPainter p(this); //请画家
QPixmap pix; //创建图片对象
pix.load(":/Image/dg.PNG"); //加载图片
p.drawPixmap(posX, 50, 100, 100, pix); //画图片
}
五. 绘图设备
画家(QPainter)通过绘图引擎(QPaintEngine)向绘图设备(QPaintDevice)上绘制图片。
1. QWidget也是一个绘图设备,它继承自QObject和QPaintDevice。
2. QT中还提供了4个绘图设备类,分别是:QPixmap、QBitmap、QImage、QPicture。
QPixmap:专门为图像在屏幕上的显示做了优化。
QPixmap pix(300, 300); //创建绘图设备
pix.fill(Qt::white); //北京填充
QPainter p(&pix); //创建画家
QPen pen(Qt::red);
pen.setWidth(2);
p.setPen(pen);
p.drawEllipse(QPoint(150, 150), 100, 100); //画圆
pix.save("C:\\Users\\lenovo\\Desktop\\pix.png"); //保存pix
QBitmap: 是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
QBitmap bix(300, 300); //创建绘图设备
bix.fill(Qt::white); //北京填充
QPainter p(&bix); //创建画家
QPen pen(Qt::red);
pen.setWidth(2);
p.setPen(pen);
p.drawEllipse(QPoint(150, 150), 100, 100); //画圆
bix.save("C:\\Users\\lenovo\\Desktop\\bix.png"); //保存bix
QImage:专门为图像的像素级访问做了优化。
QImage img(300, 300, QImage::Format_RGB32); //创建绘图设备
img.fill(Qt::white); //北京填充
QPainter p(&img); //创建画家
QPen pen(Qt::red);
pen.setWidth(2);
p.setPen(pen);
p.drawEllipse(QPoint(150, 150), 100, 100); //画圆
img.save("C:\\Users\\lenovo\\Desktop\\img.png"); //保存img
QImage img;
img.load("C:\\Users\\lenovo\\Desktop\\img.png"); //加载图片
QRgb value = qRgb(0, 255, 0); //红 绿 蓝
for (int x = 30; x < 70; x++) {
for (int y = 130; y < 170; y++) {
img.setPixel(x, y, value); //修改像素点 x、y、颜色
}
}
img.save("C:\\Users\\lenovo\\Desktop\\img_change.png");
QPicture:可以记录和重现QPainter的各条命令。
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QPicture pic;
QPainter p;
p.begin(&pic); //开始记录
QPen pen(Qt::red);
pen.setWidth(2);
p.setPen(pen);
p.drawEllipse(QPoint(150, 150), 100, 100);
p.end(); //结束记录
pic.save("C:\\Users\\lenovo\\Desktop\\img_change.png");
}
void Widget::paintEvent(QPaintEvent *e) {
QPainter p(this);
QPicture pic;
pic.load("C:\\Users\\lenovo\\Desktop\\img_change.png"); //加载记录命令的绘图文件。
p.drawPicture(0, 0, pic); //复现pic里记录的绘图指令,绘制到widget窗口上。
}