目录
目录
Posts List
  1. 自定义控件封装
  2. 事件QEvent
    1. 鼠标事件
    2. 定时器
  3. 事件分发器
  4. 事件过滤器

Qt学习 P4:自定义控件封装,事件QEvent

自定义控件封装

项目中新建文件,选择模板—Qt—Qt设计师界面类。这样可以得到一个带ui文件的类。

新建一个自定义的smallWidget类,在其中设计好需要封装的控件。

在main.ui设计界面创建一个Widget,右键—提升为..—新建提升的类—smallWidget—添加(勾选全局包含)—提升。

1
2
3
4
5
6
7
8
9
10
11
12
13
smallwidget.cpp

smallWidget::smallWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::smallWidget)
{
ui->setupUi(this);

void (QSpinBox::*spSignal)(int) = &QSpinBox::valueChanged;
connect(ui->spinBox, spSignal, ui->horizontalSlider, &QSlider::setValue);

connect(ui->horizontalSlider, &QSlider::valueChanged, ui->spinBox, &QSpinBox::setValue);
}

设置数字显示和进度条的关联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
smallwidget.h

class smallWidget : public QWidget
{
Q_OBJECT

public:
explicit smallWidget(QWidget *parent = 0);
~smallWidget();

void setNum(int num);

int getNum();

private:
Ui::smallWidget *ui;
};

-----
smallwidget.cpp

void smallWidget::setNum(int num) {
ui->spinBox->setValue(num);
}

int smallWidget::getNum() {
return ui->spinBox->value();
}

-----
widget.cpp

connect(ui->btn_get, &QPushButton::clicked, [=]() {
QMessageBox::information(this, "当前数值", QString::number(ui->smallwidget->getNum()));
});

connect(ui->btn_set, &QPushButton::clicked, [=]() {
ui->smallwidget->setNum(50);
});

为自定义控件提供接口。

事件QEvent

鼠标事件

QWidget类提供QWidget::enterEvent(QEvent *event)捕捉鼠标进入事件,函数类型为virtual protected

新建类myLabel,继承自QWidget,重载函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
myLabel.cpp

void myLabel::enterEvent(QEvent *event) {
qDebug() << "cursor enter!";
}

void myLabel::leaveEvent(QEvent *event) {
qDebug() << "cursor leave!";
}

void myLabel::mousePressEvent(QMouseEvent *ev) {
qDebug() << "mouse pressed!";
}

void myLabel::mouseReleaseEvent(QMouseEvent *ev) {
qDebug() << "mouse released!";
}

void myLabel::mouseMoveEvent(QMouseEvent *ev) { // 捕捉鼠标按下后的移动
qDebug() << "mouse moved!";
}

QMouseEvent *ev捕获点击的位置等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void myLabel::mousePressEvent(QMouseEvent *ev) {
if(ev->button() == Qt::LeftButton) { // 仅当左键按下
QString str = QString("mouse pressed! x = %1 y = %2").arg(ev->x()).arg(ev->y()); // QString的格式化
qDebug() << str;
// x(), y() : 以控件为基准;globalX(), globalY() : 以屏幕为基准
}
}

void myLabel::mouseMoveEvent(QMouseEvent *ev) { // 捕捉鼠标按下后的移动
if(ev->buttons() & Qt::LeftButton) { // 当左键有按下
qDebug() << "mouse moved";
}
// button()只捕获触发的行为;buttons()提供左中右键的整体状态
}

在构造函数中设置setMouseTracking(true);可以使在没有按下鼠标键的情况下mouseMoveEvent()也能被触发。

定时器

[virtual protected] void QObject::timerEvent(QTimerEvent *event)提供定时器事件。

构造函数中启动定时器:

1
startTimer(1000); // 间隔

重写定时器事件函数:

1
2
3
4
void Widget::timerEvent(QTimerEvent *ev) {
static int num = 1;
ui->label->setText(QString::number(num++));
}

可以启动多个定时器,由startTimer()返回值和ev->timerId()区分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);

id1 = startTimer(1000);
id2 = startTimer(2000);
}

void Widget::timerEvent(QTimerEvent *ev) {
if (ev->timerId() == id1) {
static int num = 1;
ui->label->setText(QString::number(num++));
}
if (ev->timerId() == id2) {
static int num2 = 1;
ui->label_2->setText(QString::number(num2++));
}
}

定时器的第二种实现方式:类QTimer

1
2
3
4
5
6
7
8
9
10
11
12
QTimer *timer = new QTimer(this);
timer->start(500);

connect(timer, &QTimer::timeout, [=]() {
static int num3 = 1;
ui->label_3->setText(QString::number(num3++));
});

connect(ui->btn_pause, &QPushButton::clicked, [=]() {
if (timer->isActive()) timer->stop();
else timer->start();
});

事件分发器

程序接收到事件之后,会由bool event(QEvent *ev)事件分发器将事件下发到具体的函数,如enterEvent(QEvent*)等。

我们可以通过事件分发器对事件进行拦截。

1
2
3
4
5
6
7
8
9
bool myLabel::event(QEvent *event) { // 重写事件分发器
if (event->type() == QEvent::MouseButtonPress) { // 拦截鼠标单击事件
QMouseEvent *ev = static_cast<QMouseEvent*>(event);
QString str = QString("In func event: mouse pressed! x = %1 y = %2").arg(ev->x()).arg(ev->y());
qDebug() << str;
return true; // 表示由用户处理,不向下分发
}
return QWidget::event(event); // 其余事件交由父类处理
}

事件过滤器

通过事件过滤器,可以在事件分发器之前再对事件做一次拦截。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
widget.cpp

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 (watched == ui->widget) {
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *ev = static_cast<QMouseEvent*>(event);
QString str = QString("In event filter: mouse pressed! x = %1 y = %2").arg(ev->x()).arg(ev->y()); // QString的格式化
qDebug() << str;
return true;
}
}
return QWidget::eventFilter(watched, event);
}