菜单Menu

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
#include <QKeySequence>
#include <QMenuBar>
#include <QDebug>
mainWindow_1::mainWindow_1(QMainWindow* parent)
: QMainWindow(parent) {
QMenuBar* menu = new QMenuBar(this);
// 将菜单栏添加到主窗口中
this->setMenuBar(menu);
// 定义菜单
QMenu* file = new QMenu("文件", this);
QMenu* edit = new QMenu("编辑", this);
// 在菜单栏添加菜单
menu->addMenu(file);
menu->addMenu(edit);
// 定义菜单项
QAction* open_file = new QAction("open file", this);
QAction* new_file = new QAction("new file", this);
// 添加菜单项到菜单
file->addAction(open_file);
// 插入一条分分隔线
file->addSeparator();
file->addAction(new_file);
// 给菜单项open_file设置快捷键
new_file->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
open_file->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
// 让菜单项触发动作
connect(new_file, &QAction::triggered, [=]() {
qDebug() << "新建文件成功" << Qt::endl;
});
}

工具栏 ToolBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 创建一个工具栏
QToolBar* toolBar = new QToolBar(this);
// 将工具栏添加到主窗口
this->addToolBar(toolBar);
// 定义工具项
QAction* tool_copy = new QAction("copy", this);
// 将工具项添加到工具栏
toolBar->addAction(tool_copy);
// 将菜单项放入工具栏中
toolBar->addSeparator();
toolBar->addAction(new_file);
toolBar->addAction(open_file);
// 工具栏默认允许浮动 false不允许浮动
toolBar->setFloatable(false);
// 设置工具栏允许的区域
toolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);

状态栏

1
2
3
4
5
6
// 创建lable
QLabel* lable_left = new QLabel("左侧提示信息", this);
QLabel* lable_right = new QLabel("右侧提示信息", this);
// 添加左侧和右侧控件
statusBar->addWidget(lable_left);
statusBar->addPermanentWidget(lable_right);

控件 DockWidget

铆接部件

1
2
3
4
5
6
7
8
9
10
11
// 创建一个铆接部件
QDockWidget* dockwidget = new QDockWidget("dockwidget", this);
// 添加铆接部件
this->addDockWidget(Qt::TopDockWidgetArea, dockwidget);
// 设置铆接部件停靠位置
dockwidget->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
// 创建中心部件
// 创建文本域
QTextEdit* text_edit = new QTextEdit(this);
text_edit->setText("这是一个文本域");
this->setCentralWidget(text_edit);

列表控件 ListWidget

List_widget

1
2
3
4
5
6
7
8
9
10
11
// 列表控件
// 添加item到列表控件
QListWidgetItem* item = new QListWidgetItem("this is a listWidgetitem");
ui->listWidget_1->addItem(item);
// 利用QStringList添加内容到ListWidget
QStringList list;
list << "this is test 1" << "this is test 2" << "this is test 3";
ui->listWidget_1->addItems(list);
connect(ui->listWidget_1, &QListWidget::itemClicked, [](QListWidgetItem* item){
qDebug() << item->text();
})

运行结果

list_widgwt_resoult

树控件 TreeWidget

通过UI界面设计

TreeWidget_1

不能在运行时添加项目值

通过代码设计

image-20230131204239500
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
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QStringList>
#include <QTreeWidgetItem>
#include <QList>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设置树控件头信息
QStringList list;
list << "Country" << "Language";
ui->treeWidget->setHeaderLabels(list);
// 添加顶层控件
QTreeWidgetItem* item_China = new QTreeWidgetItem(QStringList() << "China" << "汉语");
QTreeWidgetItem* item_Japan = new QTreeWidgetItem(QStringList() << "Japan" << "Japanese");
QTreeWidgetItem* item_Han = new QTreeWidgetItem(item_China, QStringList() << "汉族" << "汉语");
ui->treeWidget->addTopLevelItems(QList<QTreeWidgetItem*>({item_China, item_Japan}));
// 信息输出
connect(ui->treeWidget, &QTreeWidget::itemClicked, [](QTreeWidgetItem* item, int column){
qDebug() << item->text(column);
});
}

表格控件 TableWidget

TableWidget
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
40
41
42
43
44
45
46
47
48
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QStringList>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
int c = 3;
int r = 5;
// 设置列数
ui->tableWidget->setColumnCount(c);
// 设置行数
ui->tableWidget->setRowCount(r);
// 设置水平表头
// QTableWidgetItem* name = new QTableWidgetItem("Name");
// ui->tableWidget->setHorizontalHeaderItem(0, name);
// QTableWidgetItem* gender = new QTableWidgetItem("Gender");
// ui->tableWidget->setHorizontalHeaderItem(1, gender);
// QTableWidgetItem* age = new QTableWidgetItem("Age");
// ui->tableWidget->setHorizontalHeaderItem(2, age);
// 垂直表头一般数字序列,无需设置, 如需设置,用setVerticalHeaderItem(int row, QTableWidgetItem *item) 或
// setVerticalHeaderLabels(const QStringList &labels)
// 添加信息或控件到表格
// QTableWidgetItem* neo = new QTableWidgetItem("Neo");
// ui->tableWidget->setItem(0, 0, neo);
// 以上都可以通过链表容器用setVerticalHeaderLabels(const QStringList &labels) 或
// setHorizontalHeaderLabels(const QStringList &labels) 添加
QStringList listHeaderItem({"Name", "Gender", "Age"});
ui->tableWidget->setHorizontalHeaderLabels(listHeaderItem);
QStringList listNames({"Neo", "Bill", "Rose", "Joke", "Tom"});
QStringList listGender({"Male", "Male", "Female", "Male", "Male"});
for (int i = 0; i < r; ++i) {
ui->tableWidget->setItem(i, 0, new QTableWidgetItem(listNames[i]));
ui->tableWidget->setItem(i, 1, new QTableWidgetItem(listGender[i]));
ui->tableWidget->setItem(i, 2, new QTableWidgetItem(QString::number(i + 30)));
}
// 信息获取
connect(ui->tableWidget, &QTableWidget::itemClicked, [](QTableWidgetItem* item){
qDebug() << item->text();
});
connect(ui->tableWidget, &QTableWidget::cellClicked, [](int row, int column){
qDebug() << "row = " << row << " column = " << column;
});
}

QLable控件

获取QLable文本

1
ui->label->text();

设置图片setPixmap

1
2
QPixmap pix("://src_img/icon_1.png");
ui->label_2->setPixmap(pix);

设置动画并播放

1
2
3
4
5
6
7
//    设置动画
QMovie* movie_gif = new QMovie("://src_img/coffee-3567_512.gif");
ui->label_2->setMovie(movie_gif);
// 播放动画
connect(ui->pushButtonPlay, &QPushButton::clicked, movie_gif, &QMovie::start);
// 暂停播放
connect(ui->pushButtonPause, &QPushButton::clicked, movie_gif, &QMovie::stop);

自定义控件

  1. 定义一个自定义控件

    customWidget_1.png C:/0_WorkSpace/myGithub/hexo/source/_posts/IT/Qt%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0.assets/custom_widget_2.png
  2. 自行添加控件

    image-20230202153236586
  3. 在其他界面添加容器存放自定义控件

    image-20230202153511577
  4. 提升控件到自定义控件

    image-20230202154151346
  5. 运行结果

    image-20230202162052667
  6. 让界面动起来,spinBox数值改变,让horizontalSlider进度条同步运动,反之,spinBox数值也要同步改变

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void (QSpinBox:: *p)(int) = &QSpinBox::valueChanged;  // 因为函数被重载
    // spinBox数值改变,让Slider同步改变
    connect(ui->spinBox, p, [=](int ti){
    ui->horizontalSlider->setValue(ti);
    });
    // Slider被滑动时同步改变SpinBox的值
    connect(ui->horizontalSlider, &QSlider::sliderMoved, [=](int val){
    ui->spinBox->setValue(val);
    });
    // Slider的值被改变时同步改变SpinBox的值
    connect(ui->horizontalSlider, &QSlider::valueChanged, [=](int val){
    ui->spinBox->setValue(val);
    });

自定义控件提供外部接口

  1. 在自定义控件头文件定义公共函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class myWidget : public QWidget
    {
    Q_OBJECT

    public:
    explicit myWidget(QWidget *parent = nullptr);
    ~myWidget();
    // 定义外部接口
    // 定义设置slider值的外部接口
    void func_setSliderVal(int val);
    // 定义获取slider值的外部接口
    int func_SliderVal(void);

    private:
    Ui::myWidget *ui;
    };
  2. 在自定义控件函数实现cpp文件实现函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 设置进度条Slider的值
    void myWidget::func_setSliderVal(int val) {
    ui->horizontalSlider->setValue(val);
    }

    // 获取进度条Slider的值
    int myWidget::func_SliderVal(void) {
    return ui->horizontalSlider->value();
    }
  3. 调用公共函数接口

    1
    2
    3
    4
    5
    6
    connect(ui->pushButtonSetHalf, &QPushButton::clicked, [=](){
    ui->widget->func_setSliderVal(50);
    });
    connect(ui->pushButtonPrintSliderVal, &QPushButton::clicked, [=](){
    qDebug() << ui->widget->func_SliderVal();
    });

对话框 QtDialog

模态对话框 (独占)Qdialog

1
2
3
4
5
6
 #include <QDialog>
//模态对话框
QDialog* dlg = new QDialog(this);
dlg->resize(300, 300);
dlg->exec();
qDebug() << "显示模态对话框" << Qt::endl;

非模态对话框 (非独占)QDialog

1
2
3
4
5
6
#include <QDialog>
//非模态对话框
QDialog* dlg = new QDialog(this);
dlg->resize(300, 300);
dlg->show();
qDebug() << "显示模态对话框" << Qt::endl;

消息对话框 QMessageBox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <QMessageBox>
// 属于模态对话框o
// QMessageBox::critical(this, "error", "this is a error");
// QMessageBox::information(this, "information", "this is a information");
// QMessageBox::question(this, "question", "this is a question");
// 修改button名字
// QMessageBox::question(this, "question", "this is a question",
// QMessageBox::Save | QMessageBox::Cancel);
// 修改默认选中的button
QMessageBox::StandardButton ret;
ret = QMessageBox::question(this, "question", "this is a question",
QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Cancel);
if (ret == QMessageBox::Save) {
qDebug() << "files saved!";
} else if (ret == QMessageBox::Cancel) {
qDebug() << "cancel save file";
}

字体对话框 QFontDialog

1
2
3
4
5
6
7
8
9
#include <QFont>
#include <QFontDialog>
// 字体对话框
bool yes;
QFont font;
font = QFontDialog::getFont(&yes, QFont("宋体"), this);
if (yes == true){
qDebug() << "the font is :" << font.families() << " , font size is :" << font.pointSize() << Qt::endl;
}

颜色对话框 QColorDialog

1
2
3
4
5
6
#include <QColorDialog>
#include <QColor>
// 颜色对话框
QColor color;
color = QColorDialog::getColor();
qDebug() << "red color is " << color.red() << Qt::endl;

文件对话框 QFileDialog

1
2
3
4
5
6
7
#include <QFileDialog>
// 文件对话框
QString fileName;
// fileName = QFileDialog::getOpenFileName();
// 打开指定路径文件
fileName = QFileDialog::getOpenFileName(this, "files", "F:/Pictures", "*.png");
qDebug() << "The file is " << fileName << Qt::endl;

布局管理器

Qt 提供的布局中以下三种是我们最常用的 :

  1. 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
    2.  ```QVBoxLayout```:按照竖直方向从上到下布局
    3. ```QGridLayout```:在一个网格中进行布局 ,类似HTML 的 table

    <img src="https://huiblog.oss-cn-hangzhou.aliyuncs.com/img/note/Qt/Qt_note_layout.png" style="zoom: 67%;" />

    **让界面动起来**

    ```c++
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QPushButton>
    MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    // 单击登录获取用户名
    connect(ui->pushButtonLogIn, &QPushButton::clicked, [=](){
    qDebug() << "User name is " << ui->lineEditUserName->text();
    qDebug() << "Password is " << ui->lineEditPassWord->text();
    });
    connect(ui->pushButtonExit, &QPushButton::clicked, [=](){
    this->close();
    });
    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }

容器Container

image-20230131153231917

组容器 Group Box

image-20230131155448922

1
2
3
4
5
// 设置单选框是否被选中
ui->radioButtonMale->setChecked(true);
connect(ui->radioButtonMale, &QPushButton::clicked, [=](){
qDebug() << "your gender choice is " << ui->radioButtonMale->text() << Qt::endl;
});

下拉列表框 ComboBox

ComboBox_1

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
#include "widget.h"
#include "ui_widget.h"
#include <QStringList>
#include <QComboBox>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 给下拉列表框添加选项
QStringList list_options(QList<QString>({"New file", "New folder", "Save file"}));
ui->comboBox->addItems(list_options);
// 设置默认选项
ui->comboBox->setCurrentIndex(2);
// 获去选项信息
// 信号函数被重载,用函数指针匹配
void (QComboBox::* p)(int) = &QComboBox::currentIndexChanged;
connect(ui->comboBox, p, [=](int index){
qDebug() << "Index = " << index << " text = " << ui->comboBox->currentText();
});
}

Widget::~Widget()
{
delete ui;
}

Qt消息机制和事件

Qt事件的概述和定义

Qt 中的事件是通过事件处理系统定义的。事件处理系统是 Qt 中一种基于对象的系统,提供了一种方法来处理 GUI 中的用户输入和窗口系统事件。

在 Qt 中,事件是由 QEvent 类定义的,QEvent 类是所有事件类的基类。每个事件类都有自己的类型和数据,如 QMouseEvent 类,它记录了鼠标事件的信息,如鼠标位置和按键状态。

在 Qt 中,通常是通过继承 QObject 类并重写事件处理方法来定义事件处理。例如,要处理鼠标按下事件,可以重写 QWidget 的 mousePressEvent() 方法。在事件发生时,Qt 会自动调用该方法并将事件传递给它。

事件处理方法的名称通常是以 “event” 为后缀的,如 mousePressEvent(),它们通常是用于处理特定类型的事件的虚函数。

例子:用QLabel展示鼠标相关事件

首先,我们从 QLable继承了一个类,并重写了 enterEvent() 和 leaveEvent() 方法。当鼠标进入窗口时,enterEvent() 方法将会被调用,打印 “Mouse entered the label” 字符串;当鼠标离开窗口时,leaveEvent() 方法将会被调用,打印 “Mouse left the label” 字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 头文件
#ifndef MYLABEL_H
#define MYLABEL_H

#include <QLabel>
class MyLabel : public QLabel
{
Q_OBJECT
protected:
// 鼠标进入事件
void enterEvent(QEnterEvent *event);
// 鼠标离开事件
void leaveEvent(QEvent *event);
// 鼠标移动事件
void mouseMoveEvent(QMouseEvent* event);
// 鼠标按下事件
void mousePressEvent(QMouseEvent* event);
public:
explicit MyLabel(QWidget *parent = nullptr);

signals:

};
#endif // MYLABEL_H
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
40
#include "mylabel.h"
#include <QPoint>
#include <QCursor>
#include <QMouseEvent>

MyLabel::MyLabel(QWidget *parent)
: QLabel{parent}
{
//设置鼠标跟踪功能,无需按下鼠标按键, 默认关闭
// this->setMouseTracking(true);
}

void MyLabel::enterEvent(QEnterEvent *event) { // QEnterEvent是个坑,需要注意
Q_UNUSED(*event);
qDebug() << "Mouse entered the label";
}

void MyLabel::leaveEvent(QEvent *event) {
Q_UNUSED(*event);
qDebug() << "Mouse left the label";
}

void MyLabel::mouseMoveEvent(QMouseEvent* event) {
Q_UNUSED(event);
// 按下鼠标按键移动并获取坐标
// 坐标是相对Mylabel控件里面的局部坐标
qDebug() << "The mouse is pressed and moved in the Label, the position is x = "
<< event->x() << " y = " << event->y();
}

void MyLabel::mousePressEvent(QMouseEvent* event) {
Q_UNUSED(event);
QPoint p = QCursor::pos(); // 获取鼠标全局坐标
//获取鼠标按键并且输出
if (event->button() == Qt::LeftButton) {
qDebug() << "The left mouse button is pressed and position is " << p;
}else if (event->button() == Qt::RightButton){
qDebug() << "The right mouse button is pressed and position is " << p;
}
}

“protected” 是 C++ 中的一个修饰符,用于限制类成员的访问权限。

当一个类的成员被声明为 “protected” 时,它只能在这个类的内部和它的派生类(子类)中被访问。这意味着,在这个类的外部,它不能被直接访问。但是,如果你继承这个类,并且创建了一个子类,那么在子类中,你可以通过对象访问这个 protected 成员。

使用 protected 修饰符可以使继承关系更加紧密,并且防止其他代码意外地修改类的内部数据。

Q_UNUSED 宏用于告诉编译器一个变量是未使用的,以避免编译器发出未使用变量的警告。

事件函数一般都是虚函数,所以都需要重写

事件分发器

Qt 中的事件分发器是用于处理应用程序中的事件的机制。它从窗口系统接收事件(例如键盘输入、鼠标点击等),并将它们分配给相应的 GUI 对象。

Qt 使用事件循环和事件分发器(QCoreApplication)来管理事件。每个 Qt 应用程序都有一个主事件循环,它可以接收和分派事件。您可以在主函数中使用 QCoreApplication::exec() 函数来启动事件循环。

事件分发器会传递事件给与之相关的 GUI 对象。每个 GUI 对象都可以重写 QObject 类的 event() 函数,以处理特定类型的事件。

重写事件分发器

例如,你可以在 QWidget 子类中重写 event() 函数,以处理鼠标点击事件:

1
2
3
4
5
6
7
8
bool MyWidget::event(QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress) {
// handle mouse button press event
return true;
}
return QWidget::event(event);
}

如果您处理了事件并返回了 true,则表示该事件已被处理,不再需要传递给其他对象。如果您未处理事件并返回 false,则该事件将继续传递给其他对象。

其他事件处理器

此外,Qt 还提供了一些其他方法来处理事件,例如:

  • 信号与槽:您可以在一个对象上定义信号,并在另一个对象上连接该信号以处理特定事件。
  • 事件过滤器:您可以在程序中安装事件过滤器,以在事件分发之前对事件进行处理。
  • 事件处理器:您可以定义事件处理器来处理特定类型的事件。

Qt 事件分发器是一个灵活的机制,你可以根据你的应用程序需求使用不同的方法处理事件。

事件过滤器

Qt 事件过滤器是一种处理事件的方法,可以在事件分发之前对事件进行处理。您可以在程序中安装事件过滤器,以捕获应用程序中的事件。事件过滤器是一个 QObject 的子类,可以实现 eventFilter 函数来处理事件。

使用事件过滤器的示例

示例一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class EventFilter : public QObject
{
public:
bool eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
// handle key press event
return true;
}
return false;
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
EventFilter filter;
a.installEventFilter(&filter);
// ...
return a.exec();
}

在上面的代码中,我们定义了一个名为 EventFilter 的类,该类继承自 QObject 并实现了 eventFilter 函数。我们在主函数中安装了该事件过滤器,并通过 QCoreApplication::installEventFilter 函数实现。现在,当有任何事件发生时,该事件将先到达事件过滤器,该过滤器可以处理事件,并返回 true 或 false 来指示是否处理了该事件。如果事件被处理,则不会传递给其他对象;如果未被处理,则该事件将继续分发。

示例二

在下面代码中,我们首先在自己类中的构造函数部分安装或者加载事件过滤器

1
2
3
4
5
6
MyLabel::MyLabel(QWidget *parent)
: QLabel{parent}
{
// Installs an event filter filterObj on this object.
this->installEventFilter(this);
}

在类头文件声明事件过滤器函数

1
2
3
4
// 	filter function declaration
// watched: The widget that generates the event.
// event: Description of what happened.
bool eventFilter(QObject *watched, QEvent *event) override;

具体函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
// override event filter
bool MyLabel::eventFilter(QObject *watched, QEvent *event)
{
if (watched == this) {
if (event->type() == QEvent::MouseButtonPress) {
// handle mouse button press event
qDebug() << "A mouse press event occurs";
return true;
}
}
// Return other unhandled events
return QLabel::eventFilter(watched, event);
}

注意事项

事件过滤器和被安装过滤器的组件必须在同一线程 ,否则 ,过滤器将不起作用。另外 ,如果在安装过滤器之后 ,这两个组件到了不同的线程 , 那么 ,只有等二者重新回到同一线程的时候过滤器才会有效 。

这种全局的事件过滤器将会 在所有其它特性对象的事件过滤器之前调用 。 尽管很强大 , 但这种行为会严重降低整个应用程序的事件分发效率 。

定时器控件 QTimer

Qt提供了一个定时器类QTimer,它可以在指定的时间间隔内触发信号,从而实现定时任务。可以使用QTimer::start()方法启动定时器,并通过setInterval()方法设置时间间隔。定时器可以在单次触发或多次触发信号,可以通过setSingleShot()方法控制。

案例一 函数中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <QTimer>
#include <QDebug>

int main(int argc, char *argv[])
{
QTimer timer;
timer.setInterval(1000); // 设置时间间隔为1000ms
QObject::connect(&timer, &QTimer::timeout, [](){
qDebug() << "timeout";
});
timer.start(); // 启动定时器
return 0;
}

以上代码将每隔1秒输出一次”timeout”。

案例二 启动一个定时器

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
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

protected:
// timerEvent function declaration and override
virtual void timerEvent(QTimerEvent *event) override;

private:
Ui::Widget *ui;
};
#endif // WIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 启动定时器
this->startTimer(1000);
}

Widget::~Widget()
{
delete ui;
}

void Widget::timerEvent(QTimerEvent *event)
{
static int timeCount;
ui->label_timer->setText(QString::number(timeCount++));
}

案例二 启动多个定时器

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
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

protected:
// timerEvent function declaration and override
virtual void timerEvent(QTimerEvent *event) override;

private:
Ui::Widget *ui;
int timerId_1;
int timerId_2;
};
#endif // WIDGET_H
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
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 启动定时器
// 开启两个定时器
this->timerId_1 = this->startTimer(1000);
this->timerId_2 = this->startTimer(2000);
}

Widget::~Widget()
{
delete ui;
}

void Widget::timerEvent(QTimerEvent *event)
{
static int timeCount_1;
static int timeCount_2;
if (event->timerId() == this->timerId_1) {
ui->label_timer_1->setText("Timer_1 : " + QString::number(timeCount_1++));
}
if (event->timerId() == this->timerId_2) {
ui->label_timer_2->setText("Timer_2 : " + QString::number(timeCount_2++));
}
}

案例三 通过定时器对象启动

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
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

protected:
// timerEvent function declaration and override
virtual void timerEvent(QTimerEvent *event) override;

private:
Ui::Widget *ui;
};
#endif // WIDGET_H
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
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 定义定时器对象
QTimer* timer_test = new QTimer(this);
// 定时器超时便设置文本
static int timeCount_3;
connect(timer_test, &QTimer::timeout, [=](){
ui->label_timer_3->setText(QString::number(timeCount_3++));
});
// 启动定时器
connect(ui->pushButton_timerStart, &QPushButton::clicked, [=](){
timer_test->start(3000);
});
// 暂停定时器
connect(ui->pushButton_timerStop, &QPushButton::clicked, [=](){
timer_test->stop();
});
}

Widget::~Widget()
{
delete ui;
}

Qt绘图:QPainter、QPainterDevice、QPainterEngine

Qt绘图系统基于QPainterQPainterDeviceQPaintEngine的三个类**QPainter(画家)使用QPaintEngine(绘图引擎)QPainterDevice(绘图设备)**上在进行绘画。

案例一 绘制窗口背景图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

protected:
// 重写绘图事件
virtual void paintEvent(QPaintEvent* event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
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
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPixmap>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(768, 512);
}

Widget::~Widget()
{
delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
// 定义一个画家
QPainter* painter = new QPainter(this);
// 定义一个图片控件
QPixmap pix_png("://img/test_img.png");
// 修改图片大小与窗口尺寸一致
pix_png.scaled (this->width (), this->height ());
// 画家进行绘画
painter->drawPixmap (0, 0, this->width (), this->height (), pix_png);
}

案例二 通过update()函数重载绘图事件

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
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

protected:
// 重写绘图事件
virtual void paintEvent(QPaintEvent* event);
// 重写窗口resizeEvent()事件
virtual void resizeEvent(QResizeEvent* event);
private:
void updateButtonPosition();
Ui::Widget *ui;
};
#endif // WIDGET_H

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPixmap>
#include <QPushButton>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(768, 512);
connect(ui->pushButton_picMove, &QPushButton::clicked, [&](){
// 单击按钮重载绘图事件
this->update();
});
}

Widget::~Widget()
{
delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
// 定义一个画家
QPainter* painter = new QPainter(this);
// 定义一个图片控件
QPixmap pix_png("://img/test_img.png");
// 修改图片大小与窗口尺寸一致
pix_png.scaled (this->width (), this->height ());
// 画家进行绘画
static int x = 0;
painter->drawPixmap (x, 0, pix_png.width(), pix_png.height(), pix_png);
x += 20;
if (x >= this->width()) {
x = 0;
}
}

void Widget::resizeEvent(QResizeEvent *event)
{
// 保持按钮处于窗口底部中间位置
updateButtonPosition();
QWidget::resizeEvent(event);
}

// 更新窗口底部按钮位置
void Widget::updateButtonPosition()
{
int x = (this->width() - ui->pushButton_picMove->width()) * 0.5;
int y = this->height() - ui->pushButton_picMove->height();
ui->pushButton_picMove->move(x, y);
}

画家QInpainter的其他绘图函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

protected:
// 重写绘图事件
virtual void paintEvent(QPaintEvent* event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
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
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(800, 600);
}

Widget::~Widget()
{
delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
qDebug() << "this is a test";
// 定义一个画家
QPainter* painter = new QPainter(this);
// 画线
painter->drawLine(0, 0, 300, 300);
// 画矩形
// 设置画笔颜色
painter->setPen(Qt::red);
painter->drawRect(30, 30, 50, 80);
// 画圆
painter->drawEllipse(400, 300, 200, 200);
// 画椭圆
painter->drawEllipse(200, 200, 300, 200);
}

绘图设备 QPaintDivice

概述

Qt6 的绘图设备是 QPaintDevice 类。QPaintDevice 是所有 Qt 绘图类的基类,包括 QPixmap、QImage、QPicture、QWidget、QPrinter 等。它定义了一个公共的接口,所有的绘图类都必须实现它。

QPaintDevice 包含了一些关于绘图的基本信息,例如:分辨率、每英寸的像素数等。它也提供了一个关于绘图的状态的接口,例如:画家,画笔,画刷等。

在绘图时,通过在画家上设置合适的画笔、画刷等,并调用画家的绘图函数,就可以在 QPaintDevice 上绘图了。

QBitmap

QBitmap 是 Qt GUI 框架中的 QPixmap 的子类。它用于表示单色(1位深度)位图。与基于 QImage 类并支持全彩图像的 QPixmap 不同,QBitmap 仅支持二进制图像。使用 QBitmap 而不是 QImage 的优点在于,QBitmap 针对更快的渲染进行了优化,与 QImage 相比,内存使用更少。它通常用于图像需要作为掩码使用的情况,例如创建自定义光标或突出显示部件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(800, 600);
// 定义一个绘图设备
QBitmap bitMap(300, 300);
// 定义一个画家
QPainter bitMapPainter(&bitMap);
// 画一个圆
bitMapPainter.drawEllipse(QPoint(150, 150), 100, 100);
// 保存为图片
bitMap.save("C:\\0_WorkSpace\\QT_Learning\\test_QInpainter\\test.png");
}

QImage

QImage 是 Qt GUI 框架中的图像操作类。它用于表示和操作数字图像。与 QPixmap 不同,后者针对屏幕显示进行了优化,QImage 针对图像处理进行了优化,并提供了许多图像操作函数,如缩放、裁剪、颜色校正等。QImage 支持广泛的图像格式,包括 BMP、GIF、JPEG、PNG 等,可以用于在屏幕上显示图像或将图像保存到磁盘。QImage 中的图像数据以与用于显示图像的格式无关的格式存储,使得更容易在内存中处理和操作图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(800, 600);
// 定义一个绘图设备
QImage image;
image.load("://src_img/icon_1.png");
// 定义一个画家
QPainter imagePainter(&image);
// 画一个圆
// imagePainter.drawEllipse(QPoint(150, 150), 100, 100);
// 修改图片像素颜色
for (int i = 100; i < 150; ++i) {
for (int j = 100; j < 150; ++j) {
int value = qRgb(0, 255, 0);
image.setPixel(i, j, value);
}
}
// 保存为图片
image.save("C:\\0_WorkSpace\\QT_Learning\\test_QInpainter\\test.png");
}

QPicture

QPicture 是 Qt GUI 框架中的矢量图形类。它用于存储并重现 QPainter 绘制的图形。QPicture 和 QPixmap、QImage 不同,它不用于直接显示图形,而是用于存储绘制命令以及绘制时所需的数据,适用于需要高效存储和重现图形的应用场景,例如图形序列化。QPicture 可以使用 QPainter 记录,也可以加载和保存为二进制文件。当 QPicture 被重现时,QPainter 可以将其绘制到其他绘图设备,例如 QPixmap、QImage、QPrinter、QOpenGLPaintDevice 等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

protected:
// 重写绘图事件
virtual void paintEvent(QPaintEvent* event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
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
40
41
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPicture>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(800, 600);
// 定义绘图设备
QPicture picture;
// 定义画家
QPainter picturePainter;
// 记录绘图指令
picturePainter.begin(&picture);
picturePainter.drawEllipse(50, 50, 100, 100);
// 结束绘图指令
picturePainter.end();
// 保存绘图指令
picture.save("C:\\0_WorkSpace\\QT_Learning\\test_QInpainter\\steps.st");
}

Widget::~Widget()
{
delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
// 定义绘图设备
QPicture picture;
// 定义画家
QPainter picturePainter(this);
// 加载绘图指令
picture.load("C:\\0_WorkSpace\\QT_Learning\\test_QInpainter\\steps.st");
// 画家重现绘图指令
picturePainter.drawPicture(100, 100, picture);
}