06-QT事件学习

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());
}

 

二. 事件过滤器和事件分发器

将事件过滤器和事件分发器中做拦截时的返回值注释掉,可以观察到输出打印的顺序:事件分拦截器 -> 事件分发器 -> 重写的具体事件中的输出。06-QT事件学习_第1张图片

事件过滤器:
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窗口上。
}

 

你可能感兴趣的:(QT)