杨记

碎片化学习令人焦虑,系统化学习使人进步

0%

QT基础入门

学习自黑马程序员&传智教育

添加资源文件,图片视频等:

添加新文件 -》Qt -》 Qt资源文件 -》设定名称如:res

最后在项目栏出现 资源文件,资源下出现 res.qrc文件,右键 Open in Editor -》添加 -》添加前缀 如:/ -》然后就可以添加文件

.pro文件,类似VS项目的.sln文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#-------------------------------------------------
#
# Project created by QtCreator 2022-03-09T11:26:42
#
#-------------------------------------------------

QT += core gui # Qt包含的模块 core 和 gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets # 如果Qt版本大于4,包含模块widgets

TARGET = myWiget # 目标 生成可执行文件的名称
TEMPLATE = app # 模板 应用程序模板Application


SOURCES += main.cpp\ # 源文件名称
mywidget.cpp

HEADERS += mywidget.h # 头文件

FORMS += mywidget.ui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "mywidget.h"
#include <QApplication> //包含一个应用程序类的头文件

//程序入口 argc命令行变量的数量 argv命令行变量的数组
int main(int argc, char *argv[])
{
//a:应用程序对象,在Qt中,应用程序对象有且仅有一个
QApplication a(argc, argv);
//窗口对象,myWidget是QWidge的子类
myWidget w;
//窗口对象默认不会显示,必须调用show方法
//w.show();

//让应用程序对象进入消息循环
return a.exec();
}

QT使用Lambda,在.pro文件中追加 CONFIG += c++11

1
2
3
4
5
6
7
8
9
//lambda表达式
QPushButton *mbtn1 = new QPushButton(this);
QPushButton *mbtn2 = new QPushButton(this);
mbtn1->move(100, 0);
mbtn2->move(100, 50);
int m = 10;
//mutable允许值传递的只读数据在lambda内可修改,但不会导致外部改变
connect(mbtn1, &QPushButton::clicked, this, [m]()mutable{ m = 100 + 10; qDebug() << m; });
connect(mbtn2, &QPushButton::clicked, this, [=](){ qDebug() << m; });

按钮QPushButton

继承关系:QWidget <- QAbstractButton <- QPushButton

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 //创建一个按钮
QPushButton *btn = new QPushButton();
//btn->show(); //show以顶层方式弹出窗口控件
//让btn对象 依赖在主窗口中 设置父亲
btn->setParent(this);
//显示文本
btn->setText("第一个按钮");

//创建第二个按钮,安装控件的大小创建窗口,需要设置窗口的大小
QPushButton *btn2 = new QPushButton("第二个按钮", this);
btn2->move(100, 100);

//按钮重置大小
btn2->resize(50, 50);

对象树

构造重上向下,析构重下向上,一定程度上简化了内存的回收机制,前提要制定父亲setParent()

image-20220309144838633

信号和槽

信号signal和槽slot

1
2
3
//需求 点击我的按钮 关闭窗口
//参数 信号的发送者 发送的信号(函数的地址) 信号的接收者 处理的槽函数
connect(myBtn, &QPushButton::clicked, this, &myWidget::close); //信号连接

自定义信号和槽

自定义信号:写到 signals

  • 返回 void
  • 需要声明,不需要实现
  • 可以有参数,可以重载

自定义槽函数:写到public slot下或 public 或者全局函数

  • 需要声明和实现
  • 可以有参数,可以重载
  • 返回 void
1
2
3
4
5
6
7
8
//Teacher.h
class Teacher : public QObject
{
...
signals:
void hungry();
...
}
1
2
3
4
5
6
7
8
9
10
11
//Student.h
class Student : public QObject
{
...
public slots:
//早期QT版本 必须写道public slots下,高级版本可以写到 public或者全局下
//返回值 void 需要声明和实现
//可以有参数,可以重载
void treat();
...
}
1
2
3
4
5
6
7
//Student.cpp
...
void Student::treat()
{
qDebug() << "请老师吃饭"; //输出
}
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//myWidget.cpp
...
//创建一个老师对象
this->zt = new Teacher(this);
//创建一个学生对象
this->st = new Student(this);
//老师饿了,学生请客的连接 //未重载时的写法
connect(zt, &Teacher::hungry, st, &Student::treat);
classIsOver();
...
void myWidget::classIsOver()
{
//下课函数,调用后 触发老师饿了的信号
emit zt->hungry();
}

自定义信号和槽出现重载时

  • 利用函数指针明确函数地址 void(Teacher::*tSignal)(QString) = &Teacher::hungry()
  • QStringchar* :先转成 QByteArray .toUtf8() 在转 char* .data()
    • foodName.toUtf8().data()
1
2
3
4
5
6
7
8
9
10
11
12
13
//Teacher.h 增加
void hungry(QString foodName);
//Student.h 增加
void treat(QString foodName)
{
qDebug() << "请老师吃:" << foodName.toUtf8().data();
}
//myWidget.cpp
//指针 -> 地址 函数指针 -> 函数地址 //重载后使用函数指针区分
void (Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
void (Student:: *studentSlot)(QString) = &Student::treat;
connect(zt, teacherSignal, st, studentSlot);
classIsOver();

信号可以连接信号

1
2
3
4
void (Teacher:: *teacherSignal2)() = &Teacher::hungry;
void (Student:: *studentSlot2)() = &Student::treat;
connect(zt, teacherSignal2, st, studentSlot2); //老师饿了的信号 连接 学生请客的信号
connect(btn, &QPushButton::clicked, zt, teacherSignal2);//按钮点击的信号 连接 老师饿了的信号

信号可以断开disconnect

1
disconnect(btn, &QPushButton::clicked, zt, teacherSignal2);

拓展

  • 1、信号是可以连接信号
  • 2、一个信号可以连接多个槽函数
  • 3、多个信号 可以连接 同一个槽函数
  • 4、信号和槽函数的参数必须一一对应
  • 5、信号的参数个数 可以多于 槽函数的参数,但前面的类型还是要对应
  • 6、QT4版本的信号连接 connect(zt, SIGNAL(hungry()), st, SLOT(treat()));

窗口控件

菜单栏,工具栏,状态栏,铆接部件,核心部件

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
   //1、菜单栏创建,最多只有一个
QMenuBar *bar = menuBar(); //注意,没有 new
setMenuBar(bar); //注意:是set 不是 add

//创建菜单
QMenu *fileMenu = bar->addMenu("文件");
QMenu *editMenu = bar->addMenu("编辑");

//创建菜单项
QAction *newAction = fileMenu->addAction("新建");
//添加分隔线
fileMenu->addSeparator();
QAction *openAction = fileMenu->addAction("打开");

//2、工具栏,可以有多个
QToolBar *toolBar = new QToolBar(this);
addToolBar(Qt::LeftToolBarArea, toolBar);

//设置移动(总开关)
toolBar->setMovable(false);
//设置停靠范围
toolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
//设置浮动
toolBar->setFloatable(false);

//工具栏中设置内容
toolBar->addAction(newAction);
toolBar->addSeparator();
toolBar->addAction(openAction);
//工具栏中添加控件
QPushButton *btn = new QPushButton("aa", this);
toolBar->addWidget(btn);

//3、状态栏
QStatusBar *stBar = statusBar();
//设置到窗口中
setStatusBar(stBar);
//添加标签控件
QLabel * label = new QLabel("提示信息", this);
stBar->addWidget(label);
QLabel * label2 = new QLabel("最右边", this);
stBar->addPermanentWidget(label2);

//4、铆接部件 浮动窗口 可以多个
QDockWidget *dockWidget = new QDockWidget("浮动", this);
//设置到窗口中
addDockWidget(Qt::BottomDockWidgetArea, dockWidget);
//设置后期停靠区域,只允许上下
dockWidget->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);

//5、设置中心部件,只能一个
QTextEdit *textEdit = new QTextEdit(this);
setCentralWidget(textEdit);

按钮

  • QPushButton:常用按钮
  • QToolButton:工具按钮,用于显示图片
    • 显示文字,修改 toolButtonStytle
    • 凸起风格 autoRaise
  • RadioButton:单选按钮,设置默认 ui->按钮名称->setChecked(true);
  • CheckBox:多选按钮,监听状态,2选择,0未选
    • 设置半选择 tristate 1半选
1
2
3
4
5
6
7
8
9
10
11
12
//设置单选按钮 男默认选中
ui->rBtnMan->setChecked(true);

//选中女后 打印信息
connect(ui->rBtnWoman, &QRadioButton::clicked, [=](){
qDebug() << "选中性别女" <<endl;
});

//多选按钮 2 选中 0 未选中
connect(ui->cBox, &QCheckBox::stateChanged, [=](int state) {
qDebug() << state;
});

QListWidget:列表容器

1
2
3
4
5
6
7
8
9
10
11
//    //利用listWidget写诗
// QListWidgetItem * item = new QListWidgetItem("锄禾日当午");
// //将一行诗放入到listWidget控件中
// ui->listWidget->addItem(item);
// //设置文本水平居中
// item->setTextAlignment(Qt::AlignHCenter);

//QStringList QList<QString>
QStringList list;
list << "锄禾日当午" << "汗滴禾下土" << "谁知盘中餐" << "粒粒皆辛苦" ;
ui->listWidget->addItems(list);

QTreeWidget:树控件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   //设置水平头
ui->treeWidget->setHeaderLabels(QStringList() << "英雄" << "英雄介绍");
//创建根结点
QTreeWidgetItem * liItem = new QTreeWidgetItem(QStringList() << "力量");
QTreeWidgetItem * miItem = new QTreeWidgetItem(QStringList() << "敏捷");
//加载根节点
ui->treeWidget->addTopLevelItem(liItem);
ui->treeWidget->addTopLevelItem(miItem);
//追加子结点
QStringList heroL1, heroL2;
heroL1 << "赵云" << "天美亲儿子";
heroL2 << "李白" << "打野一哥";
QTreeWidgetItem *l1 = new QTreeWidgetItem(heroL1);
QTreeWidgetItem *l2 = new QTreeWidgetItem(heroL2);
liItem->addChild(l1);
liItem->addChild(l2);

QTableWidget:表格控件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   //TableWidget 控件
//设置列数
ui->tableWidget->setColumnCount(3);
//设置水平表头
ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "姓名" << "性别" << "年龄");

//设置行数
ui->tableWidget->setRowCount(5);
//设置正文
//ui->tableWidget->setItem(0, 0, new QTableWidgetItem("亚瑟"));
//批量设置表头
QStringList nameList;
nameList << "亚瑟" << "赵云" << "张飞" << "关羽" << "花木兰";
QList<QString> sexList;
sexList << "男" << "男" << "男" << "男" << "女";
for(int i = 0; i < 5; ++i) {
int col = 0;
ui->tableWidget->setItem(i, col++, new QTableWidgetItem(nameList[i]));
ui->tableWidget->setItem(i, col++, new QTableWidgetItem(sexList[i]));
//int 转 QString
ui->tableWidget->setItem(i, col++, new QTableWidgetItem(QString::number(i+18)));
}

其他控件

  • Stacked Widget 栈控件 ui->stackedWidget->setCurrentIndex(1);
  • combo Box 下拉框 ui->comboBox->addItem("奔驰");
  • QLable 标签
    • 显示图片 ui->label_pic->setPixmap(QPixmap(":/Image/butterfly.png"));
    • 显示动图 QMovie movie = new QMovie("路径"); ui->lable_movie->setMovie(movie); movie->start();
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
//栈控件的使用

//设置默认定位 ScrollArea
ui->stackedWidget->setCurrentIndex(2);

//ScrollArea按钮
connect(ui->btn_ScrollArea, &QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(2);
});

//ToolBox按钮
connect(ui->btn_ToolBox, &QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(1);
});

//TabWidget按钮
connect(ui->btn_TabWidget, &QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(0);
});

//下拉框
ui->comboBox->addItem("奔驰");
ui->comboBox->addItem("宝马");
ui->comboBox->addItem("拖拉机");

//点击按钮 选择拖拉机选项
connect(ui->btn_Select, &QPushButton::clicked, [=](){
//ui->comboBox->setCurrentIndex(2);
ui->comboBox->setCurrentText("拖拉机");
});

//利用QLabel显示图片
ui->label_Pic->setPixmap(QPixmap("E:/QT/HeiMa_jiaocheng/06_QtControl/Image/butterfly.png"));
//利用QLabel显示动图
QMovie * movie = new QMovie("E:/QT/HeiMa_jiaocheng/06_QtControl/Image/mario.gif");
ui->label_movie->setMovie(movie);
//播放动图
movie->start();

对话框

模态对话框、非模态对话框

1
2
3
4
5
6
7
8
9
10
11
//模态对话框  阻塞
QDialog dlg(this);
dlg.resize(200, 100); //默认的对话框窗体很小,输出栏会报错
dlg.exec(); //因为这个函数,阻塞,所以该栈上的内存没被释放

//非模态对话框
//要用指针类型动态分配在堆区,不然的话对话框会在lambda表达式结束后消失 QDialog dlg2(this);不行
QDialog *dlg2 = new QDialog(this);
dlg2->resize(200, 100);
dlg2->show();
dlg2->setAttribute(Qt::WA_DeleteOnClose);//设置对话框在点击关闭按钮时释放堆区对象

消息对话框:模态,QMessageBox

  • 错误对话框 QMessageBox::critical
  • 信息对话框 QMessageBox::information
  • 问题对话框 QMessageBox::question
  • 警告对话框 QMessageBox::warning
1
2
3
4
5
6
7
8
9
10
11
12
//消息对话框:
//参数1 父亲 参数2 标题 参数3 提示内容 参数4 按键类型 参数5 默认关联回车按键 返回值StandardButton
//错误对话框
QMessageBox::critical(this, "critical", "错误");
//信息对话框
QMessageBox::information(this, "info", "信息");
//提问对话框
if(QMessageBox::Save == QMessageBox::question(this, "ques", "提问", QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Cancel)) {
qDebug() << "已保存";
}
//警示对话框
QMessageBox::warning(this, "Warning", "警告");

颜色对话框:QColorDialog

1
2
3
//颜色对话框
QColor color = QColorDialog::getColor(QColor(255, 0, 0)); //设置初始颜色红色
qDebug() << "r = " << color.red() << " g = " << color.green() << " b = " << color.blue() << endl; //输出color接收到的颜色值

文件对话框QFileDialog

1
2
3
4
//文件对话框
//参数1 父亲 参数2 标题 参数3 默认打开路径 参数4 过滤文件格式 //返回值是选取的文件路径
QString str = QFileDialog::getOpenFileName(this, "打开文件", "D:\\MLZ107\\Desktop", "(*.txt)");
qDebug() << str << endl;

字体对话框QFontDialog

1
2
3
4
//字体对话框
bool flag;
QFont font = QFontDialog::getFont(&flag, QFont("仿宋", 14));
qDebug() << "字体:" << font.family() << " 字号:" << font.pointSize() << " 是否加粗:" << font.bold() << " 是否倾斜:" << font.italic();

窗口布局

  • Push Button:按钮
  • Label:标签
  • Line Edit:单行输入
  • Horizontal Spacer:水平弹簧
  • Vertical Spacer:垂直弹簧
  • Vertical Layout:垂直布局
  • Horizontal Layout:水平布局
  • Grid Layout:栅格布局

默认窗口和控件之间有间隙,可以调整

自定义控件封装

添加新文件 -》Qt -》设计师界面类 要设置要继承的类如widget,会生成 (.h .cpp . ui)

.ui文件中该控件的功能,并在.h,.cpp文件中提供接口

主界面的.ui文件中添加widget控件,右键提升为自定义的控件

事件

鼠标事件QMouseEvent

  • 鼠标进入 enterEvent
  • 鼠标离开事件 leaveEvent
  • 鼠标按下 mousePressEvent
  • 鼠标释放 mouseReleaseEvent
  • 鼠标移动 mouseMoveEvent
  • ev->x() 鼠标x的坐标 ev->y() 鼠标y的坐标
  • ev->button()判断按键 Qt::LeftButton Qt::RightButton
  • ev->buttons() 判断组号按键 判断移动时 Qt::LeftButton & Qt::RightButton
  • 格式化字符串 QString("%1 %2").arg(111).arg("我的");
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
//mylabel.cpp

...

//鼠标进入
void myLabel::enterEvent(QEvent *ev)
{
qDebug() << "鼠标进入myLabel";
}

//鼠标离开
void myLabel::leaveEvent(QEvent *ev)
{
qDebug() << "鼠标离开myLabel";
}

//鼠标按下
void myLabel::mousePressEvent(QMouseEvent *ev)
{
//左键按下
if(ev->button() == Qt::LeftButton)
{
qDebug () << "鼠标按下:x = " << ev->x() << " y = " << ev->y() << " globalX = " << ev->globalX() << " globalY = " << ev->globalY();
}
}

//鼠标移动
void myLabel::mouseMoveEvent(QMouseEvent *ev)
{
//左键按下移动
if(ev->buttons() & Qt::LeftButton)
{
qDebug () << "鼠标移动:x = " << ev->x() << " y = " << ev->y() << " globalX = " << ev->globalX() << " globalY = " << ev->globalY();
}
}

//鼠标释放
void myLabel::mouseReleaseEvent(QMouseEvent *ev)
{
//右键释放
if(ev->button() == Qt::RightButton)
{
QString str = QString("鼠标释放:x = %1 y = %2 globalX = %3 globalY = %4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
//qDebug () << "鼠标释放:x = " << ev->x() << " y = " << ev->y() << " globalX = " << ev->globalX() << " globalY = " << ev->globalY();
qDebug() << str;
}
}

定时器事件

  • 重写事件 void timerEvent(QTimerEvent *);
  • 启动定时器 startTimer(1000); 单位:ms返回值int定时器的唯一标识id
  • startTimer返回值 可以和 ev->timeId()比较,选择执行哪个定时事件
  • 关闭定时器killTimer(int id);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

//启动定时器
id1 = startTimer(1000); //ms毫秒
id2 = startTimer(2000);

//定时事件
void Widget::timerEvent(QTimerEvent *ev)
{
if(id1 == ev->timerId()) //比较定时器的id标识
{
static int num1 = 1;
ui->label_2->setText(QString::number(num1++));
}
if(id2 == ev->timerId())
{
static int num2 = 1;
ui->label_3->setText(QString::number(num2++));
}
}

定时器类QTimer:创建定时事件的第二种用法,推荐,定时事件分开,比较独立

  • 创建定时器对象 QTimer * timer = new QTimer(this);
  • 启动定时器 timer->start(1000); 单位:ms
  • 每隔一定时间,发送信号 timeout,监听 connect(timer, &Timer::timeout, 目标函数);,目标函数为每个一定时间想做的事
  • 暂停 timer->stop();
  • isActive() 判断定时器是否已启动,避免重复启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//定时器的第二种方式
QTimer *timer = new QTimer(this); //创建对象
timer->start(500); //定时 0.5s
connect(timer, &QTimer::timeout, [=](){ //每隔0.5s label_4显示的数字加1
static int num = 1;
ui->label_4->setText(QString::number(num++));
});

static bool flag = true;
connect(ui->btn, &QPushButton::clicked, [=](){ //点击按钮,定时器暂停或恢复
if(flag) {
timer->stop();
ui->btn->setText("恢复");
}
else {
timer->start(500);
ui->btn->setText("暂停");
}
flag = !flag;
});

事件分发器:用于事件的分发,可以做拦截操作,不建议

  • type()用于判断时间的类型,如:MouseButtonPress、MouseButtonMove

image-20220312141940198

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//通过event事件分发器 拦截 鼠标按下事件
bool myLabel::event(QEvent *e)
{
//如果是鼠标按下, 在event事件分发中做拦截
if(QEvent::MouseButtonPress == e->type()) {
QMouseEvent *ev = static_cast<QMouseEvent*>(e); //类型转换
QString str = QString("Event函数中:鼠标按下 x = %1 y = %2 globalX = %3 globalY = %4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
return true; //true代表用户自己处理,不向下分发
}

//其他事件 交给父类处理 默认处理
return QLabel::event(e);
}

事件过滤器:在程序将事件分发到事件分发器前,可以利用过滤器做拦截

image-20220312144312959

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.h
...
//重写事件过滤器的事件
bool eventFilter(QObject *, QEvent *);
...

//Widget.cpp
...
//步骤2:重写eventFilter事件
bool Widget::eventFilter(QObject *obj, QEvent *e)
{
if(obj == ui->label)
{
if(e->type() == QEvent::MouseButtonPress)
{
QMouseEvent *ev = static_cast<QMouseEvent*>(e);
QString str = QString("事件过滤器中:鼠标按下 x = %1 y = %2 globalX = %3 globalY = %4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
qDebug() << str;
return true; //true代表用户自己处理,不向下分发
}
}
//其他事件 交给父类处理 默认处理
return QWidget::eventFilter(obj, e);
}

绘图QPainter

  • 绘图事件 void paintEvent(QPaintEvent *); 自动调用
  • 声明一个画家对象 QPainter painter(this); this指定绘图设备
  • 设置画笔 QPen 设置画笔宽度 setWidth 风格 setStyle
  • 设置画刷 QBrush 风格setStyle
  • 画家使用画笔painter.setPen(pen); 使用画刷 painter.setBrush(brush);
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
//绘图事件,自动调用
void Widget::paintEvent(QPaintEvent *)
{
//实例化回家对象
QPainter painter(this); //this指定绘图设备

//设置画笔
QPen pen(QColor(255, 0, 0));
//设置画笔宽度
pen.setWidth(3);
//设置画笔风格
pen.setStyle(Qt::DotLine);

//画刷
QBrush brush(Qt::cyan);
//设置画刷风格
brush.setStyle(Qt::Dense7Pattern);
//让画家使用画刷
painter.setBrush(brush);


//让画家使用该画笔
painter.setPen(pen);

//画线
painter.drawLine(QPoint(0, 0), QPoint(100, 100)); //线段两端点的坐标
//画圆 Ellipse椭圆
painter.drawEllipse(QPoint(100, 100), 50, 50); //圆心 半长轴 和 半短轴
//画矩形
painter.drawRect(QRect(20, 20, 50, 50));
//画文字
painter.drawText(QRect(10, 200, 150, 50), "好好学习,天天向上");
}
  • 抗锯齿 效率低 painter.setRenderHint(QPainter::Antialiasing);
  • 画家移动位置 QPainter.translate(100, 0);
    • 保存状态 save();
    • 还原状态 restore();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this); //创建画家

painter.setRenderHint(QPainter::Antialiasing); //设置抗锯齿

painter.drawEllipse(QPoint(50, 50), 50, 50); //画圆

painter.translate(100, 0); //将画家从当前位置(0,0)移动x:100、y:0的距离 (100,0)
painter.drawRect(20, 20, 50, 50); //画矩形

painter.translate(100, 0); //将画家从当前位置(100,0)移动x:100、y:0的距离 (200,0)
painter.drawRect(20, 20, 50, 50); //画矩形

painter.save(); //保存当前状态
painter.translate(0, 100); //将画家从当前位置(200,0)移动x:0、y:100的距离 (200,100)
painter.restore(); //恢复保存的画家状态
painter.drawEllipse(QPoint(30, 30), 10, 10);
}

利用画家画图片

  • 将图片添加到项目资源中
  • painter.drawPixmap(x, y, QPixmap(":/Image/Luffy.png")); (x,y)绘图起点坐标

  • 手动调用绘图事件进行更新,推荐update(),当然repaint()也可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
	//点击移动按钮,移动图片
connect(ui->btn, &QPushButton::clicked, [=](){
posX += 20;
update(); //手动调用绘图事件,repaint也可以,但推荐update,
});
QTimer *timer = new QTimer(this); //使用QTimer实现图片自动移动
timer->start(500);
connect(timer, &QTimer::timeout, [=](){
posX += 10;
update(); //手动调用绘图事件,repaint也可以,但推荐update,
});

...

void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
//如果超出屏幕,从0开始
if(posX > this->width()) //width()获取窗口宽度,height是高度
{
posX = -QPixmap(":/Image/Luffy.png").width(); //获取图片的宽度
}
painter.drawPixmap(posX, 0, QPixmap(":/Image/Luffy.png"));
}

绘图设备

QPaintDevice的子类在Qt里面有4中:

  • QPixmap:专门为图像在屏幕上的显示做优化

  • QBitmap:是QPixmap的一个子类,它的色度限定为1(黑白色),可以使用QPixmapisQBitmap()函数来确定这个QPixmap是不是一个QBitmap

  • QImage:专门为图像的像素级访问做优化
  • QPicture:可以记录和重现QPainter的各条命令

前例中在QWidget能画图是因为QWidget继承自QPaintDevice

QPixmap

  • 创建QPixmap pix(w, h);
  • 填充颜色 pix.fill(颜色);
  • 利用画家在pix上画画 QPainter painter(&pix);
  • 保存 pix.save(路径);
1
2
3
4
5
6
7
8
9
10
11
12
//Pixmap绘图设备,300x300的画布
QPixmap pix(300, 300);
//画布填充颜色,默认黑色
pix.fill(Qt::white);
//创建画家
QPainter painter(&pix);
//设置画笔
painter.setPen(QPen(Qt::green));
//画一个圆
painter.drawEllipse(QPoint(150, 150), 100, 100);
//保存图片
pix.save("E:/QT/pix.png");

QImage

  • 创建 QImage img(w, h, QImage::Format_RGB32);
  • 设置像素 setPixel(x, y, value);
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
    //QImage 绘图设备
QImage img(300, 300, QImage::Format_ARGB32);
img.fill(Qt::white);

QPainter painter(&img);
painter.setPen(QPen(Qt::blue));
painter.drawEllipse(QPoint(150, 150),100,100);
//保存
img.save("E:/QT/img.png");

...

//修改像素点
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
//利用QImage修改像素
QImage img;
img.load(":/Image/Luffy.png"); //加载图片
//修改像素点
for(int i = 50; i < 100; ++i)
{
for(int j = 50; j < 100; ++j)
{
QRgb value = qRgb(255,0,0);
img.setPixel(i, j, value); //修改某点像素值
}
}
painter.drawImage(0, 0, img); //画QImage
}

QPicture:可以记录和重现绘图指令,但不能直接显示图片

  • 创建 QPicture pic
  • 开始记录 painter.begin(&pic);
  • 结束记录 painter.end();
  • 保存 pic.save("路径任意后缀名");
  • 重现 利用画家重现 painter.drawPicture(x, y, pic);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    //QPicture 绘图设备 可以记录和重现绘图指令
//保存指令
QPicture pic;
QPainter painter;
painter.begin(&pic); //开始记录 往pic上画的指令
painter.setPen(QPen(Qt::blue));
painter.drawEllipse(QPoint(150, 150), 100, 100);
painter.end(); //结束画画
pic.save("E:/QT/pic.pic"); //保存
...

//重现指令
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
//重现QPicture的绘图指令
QPicture pic;
pic.load("E:/QT/pic.pic");
painter.drawPicture(0, 0, pic);
}

QBitmap

1
2
3
4
5
6
7
8
9
10
11
12
13
void PaintWidget::paintEvent(QPaintEvent *)
{
QPixmap pixmap(":/Image/butterfly.png"); //背景透明
QPixmap pixmap1(":/Image/butterfly1.png"); //白色背景
QBitmap bitmap(":/Image/butterfly.png");
QBitmap bitmap1(":/Image/butterfly1.png");

QPainter painter(this);
painter.drawPixmap(0, 0, pixmap);
painter.drawPixmap(200, 0, pixmap1);
painter.drawPixmap(0, 130, bitmap);
painter.drawPixmap(200, 130, bitmap1);
}

image-20220312175643584

文件读取

QFile文件读取

QFileDialog文件对话框

QByteArray

QFileInfo文件信息类

QTextCodec编码格式类

QFile默认支持的格式为UTF-8

  • QFile file(str); 创建对象,str是文件路径

  • close() 关闭文件

  • atEnd() 判断是否是文件尾

  • readAll() 读取所有内容
  • readLine() 读取一行
  • write(内容) 写入
1
2
3
4
5
6
7
8
QFile file(path);	//文件路径
file.open(QIODevice::ReadOnly); //打开方式,只读
QByteArray array = file.readAll(); //读取所有数据
QByteArray array2;
while(!file.atEnd()) //一行行读取
{
array2 += file.readLine();
}

编码格式类QTextCodec

1
2
QTextCodec * codec = QTextCodec::codeForName("gbk"); //设置编码格式
codec->toUnicode(array); //进行转码 成设置的编码格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//点击按钮弹出文件对话框,选择文件在TextEdit中显示
connect(ui->pushButton, &QPushButton::clicked, [=](){
QString str = QFileDialog::getOpenFileName(this, "打开文件", "D:\\MLZ107\\Desktop");
ui->lineEdit->setText(str);

QTextCodec *codec = QTextCodec::codecForName("GBK"); //设置编码格式

QFile file(str);//str文件路径 QFile默认编码格式UTF-8
file.open(QIODevice::ReadOnly);//打开方式
QByteArray array = file.readAll();//读取所有内容
//ui->textEdit->setText(array);//在textEdit中显示
ui->textEdit->setText(codec->toUnicode(array));

file.close(); //关闭文件
});
1
2
3
4
5
      //写内容
file.open(str);
file.open(QIODevice::Append);
file.write("fjeofj都无可哦");
file.close();

QFileInfo读取文件信息

1
2
3
4
5
6
info.size();	//文件大小
info.suffix(); //文件后缀名
info.fileName(); //文件名
info.filePath(); //文件路径
info.created().toString("yyyy/MM/dd hh:mm:ss"); //文件创建日期,并转化为字符串
info.lastModified()toString("yyyy/MM/dd hh:mm:ss"); //文件最后修改日期,并转化为字符串
1
2
3
4
QFileInfo info(str);
qDebug() << "文件大小:" << info.size() << " 后缀名:" << info.suffix() << " 文件名:" << info.fileName() << " 文件路径:" << info.filePath();
qDebug() << "创建日期:" << info.created().toString("yyyy/MM/dd hh:mm:ss");
qDebug() << "最后修改时间:" << info.lastModified().toString("yyyy/MM/dd hh:mm:ss");

项目打包

1、使用QT的Release构建项目,将构建出来的exe 文件单独放置一个空文件夹中

2、确保QT的安装路径D:\Qt\Qt5.3.1\5.3\mingw482_32\binwindeployqt.exe文件添加在环境变量中

3、在exe 所在空文件中打开cmd窗口输入 windeployqt xxx.exe

第三方软件打包:hm nis edit

Socket通信

Qt中提供的所有的Socket类都是非阻塞的。

Qt中常用的用于socket通信的套接字类:

  • QTcpServer:用于TCP/IP通信, 作为服务器端套接字使用
  • QTcpSocket:用于TCP/IP通信,作为客户端套接字使用
  • QUdpSocket:用于UDP通信,服务器,客户端均使用此套接字

要在.pro文件中添加 network模块

image-20220506115226002

TCP/IP

UDP

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
55
56
#include "widget.h"
#include "ui_widget.h"

#include <QHostAddress>

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

//分配空间,指定父对象
udpSocket = new QUdpSocket(this);

//绑定
udpSocket->bind(8880);

setWindowTitle("端口号:8880");

//当对方成功发送数据过来,自动触发readyRead信号
connect(udpSocket, &QUdpSocket::readyRead, this, &Widget::dealMsg);
}

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

void Widget::dealMsg()
{
//获取对方发送的内容
char buf[1024] = {0};
QHostAddress peerIP;
quint16 peerPort;
qint64 len = udpSocket->readDatagram(buf, sizeof(buf), &peerIP, &peerPort); // 返回值为读取数据大小
if(len > 0)
{
//格式化 [192.78.2.2:8888]
QString str = QString("[%1:%2] %3").arg(peerIP.toString()).arg(peerPort).arg(buf);
//给编辑区设置内容
ui->textEdit->setText(str);
}
}

void Widget::on_ButtonSend_clicked()
{
//先获取对方的IP和端口
QString ip = ui->lineEditIP->text();
quint16 port = ui->lineEditPort->text().toInt();

//获取编辑区内容
QString str = ui->textEdit->toPlainText();

//给指定的IP发送数据
udpSocket->writeDatagram(str.toUtf8(), QHostAddress(ip), port);
}

组播地址:

1
2
3
4
5
6
//绑定端口要指定IPv4地址
udpSocket->bind(QHostAddress::AnyIPv4, 8888);
//加入组播,组播地址是D类地址
udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2"));
//离开组播
udpSocket->leaveMulticastGroup();

欢迎关注我的其它发布渠道