main函数模板

#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

return a.exec();
}

方便调试时查看打印信息的方式

#include <QDebug>
//方便调试时的打印输出
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "] "
//最终输出: 能看到文件信息, 与第几行
//[ ..\QDataStream\mainwidget.cpp : 51 ] "sorrowfeng彭烈枫" 100

QWidget与QMainWindow

QWidget不带菜单栏, 而QMainWindow有菜单栏

打包exe的方法

先将release里的exe文件拷贝到别的地方
在qt自带的命令行里, cd到exe所在的文件夹, 输入命令
windeployqt hello.exe

使exe文件有图标的方法

1.创建.rc文件
新建文本文件并将后缀改为.rc,这里将其命名为app.rc
 2.编辑.rc文件
此时需要一个XXX.ico图标文件,使用文本编辑器打开.rc文件并写入以下内容
IDI_ICON1 ICON DISCARDABLE "XXX.ico"
 3.添加.rc文件
在Qt应用工程文件中,使用RC_FILE添加.rc文件
RC_FILE += app.rc
然后编译生成exe文件后,可以看到exe文件已有图标
ico与rc文件都放在与工程文件(pro文件)同目录下


在MainWidget以外的cpp中使用ui的方法

将ui指针传入
#include <ui_mainwindow.h>
void FileManagemnt::OpenFile(Ui::MainWindow *u){}

加载qss资源文件

QFile qssFile("://style/beautify.qss");
qssFile.open(QFile::ReadOnly);
QString qss = qssFile.readAll();
this->setStyleSheet(qss);
qssFile.close();

按钮

 #include <QPushButton>


QWidget w;
w.setWindowTitle("nihao");//显示窗口标题

QPushButton b;
b.setText("hello"); //指定按钮内容
b.setParent(&w); //指定按钮父类,通过setParent函数
b.move(100,200); //移动坐标

QPushButton b1(&w); //通过构造函数传参指定父对象
b1.setText("nihao");
b1.move(200, 200);

w.show(); //只要show父类, 指定其为父类的子类自动显示

信号与槽

/*
* &b1 信号发出者
* &QPushButton::pressed 处理的信号 发送者的类名::信号名字
* this 信号接收者
* &Widget::close 槽函数, 信号处理函数 接收者的类名::槽函数名字
*/
connect(&b1, &QPushButton::pressed, this, &Widget::close);


自定义槽

/*
* 自定义槽,普通函数的用法
* Qt5:任意的成员函数,普通全局函数,静态函数
* 槽函数需要和信号一致(参数,返回值)
* 由于信号都是没有返回值,所以,槽函数一定没有返回值
*/
connect(b2, &QPushButton::released, this, &Widget::my_slot);
//这里b2不用&的原因是, b2本来就是一个地址类型的

//实现按下b2, b1按钮隐藏
connect(b2, &QPushButton::released, &b1, &QPushButton::hide);


//显示子窗口
this->setWindowTitle("boss");
b3.setParent(this);
b3.setText("convert");
b3.move(100,100);

//处理子窗口b3按键按下事件, 使得子窗口显示, 父窗口隐藏
connect(&b3, &QPushButton::released, this, &Widget::change);

//处理子窗口传来的信号, 使得子窗口隐藏, 父窗口显示
connect(&s, &SubWidget::mySignal, this, &Widget::dealSubWin);


//重设窗口大小
resize(200, 200);

自定义信号

  /*
* 信号必须有 signals:关键字来声明
* 信号没有返回值,但可以有参数
* 信号就是函数的声明,只需声明,无需定义
* 使用: emit mysigna1();
* 信号可以重载
*/

void mySignal();
void mySignal(int, QString);


//发送信号
void SubWidget::sendSlot()
{
emit this->mySignal();
emit this->mySignal(100, "plf"); //带参数的信号
}

带参数的信号

signals:
void mySignal(int, QString);


emit this->mySignal(100, "plf"); //带参数的信号


//接收时, 使用函数指针来指向有参和无参的信号
//无参数信号 //函数指针, 这里的func指向无参数信号
void (SubWidget::*func)() = &SubWidget::mySignal;
connect(&s, func, this, &Widget::dealSubWin);

//有参数信号 //函数指针, 这里的func指向有参数信号
void (SubWidget::*func_arg)(int, QString) = &SubWidget::mySignal;
connect(&s, func_arg, this, &Widget::dealslot);


//QT4槽函数连接, 使用SIGNAL与SLOT关键字
//容易出错:SIGNAL SLOT将函数名字->字符串不进行错误检查
connect(&s, SIGNAL(mySignal()), this, SLOT(dealslot()));

//这种方法槽函数必须有slots关键字
public slots:
void dealslot();

lambda表达式

connect(b4, &QPushButton::released,
//=: [=] 把外部所有局部变量、类中所有成员以值传递方式, 默认是只读的
//要修改需要加上关键字mutable
//[=]() mutable
//this:类中所有成员以值传递方式
//&:把外部所有局部变量,引用符号
//[]中可以传入外部变量, 来给内部使用
//()中可以传入信号中的参数, 这里的bool类型是clicked信号的参数
[b4](bool isCheck)
{
b4->setText("lambda");
qDebug() << "qwerty" << isCheck;
}
);

//[不传入数值
[]()
{
}

坐标系统

/*
* 对于父窗口(主窗口),坐标系统相对于屏幕
* 原点:相对于屏幕左上角
* x:往右递增
* y:往下递增
*/
move(100,100);

QPushButton * b1 = new QPushButton(this);
b1->setText("hello");
/*
* 对于子窗口,坐标系统相对于父窗口
* 原点:相对于父窗口空白区域左上角(不包括边框)
* x:往右递增
* y:往下递增
*/
b1->move(0,0);
b1->resize(100, 100);

//这里是指定父对象为按钮, 坐标则是从父按钮左上角开始
QPushButton * b2 = new QPushButton(b1);
b2->setText("world");
b2->move(10, 10);
b2->resize(50, 20);

内存回收机制

//1)指定父对象后        2)直接或间接继承于 QObject
//子对象如果是动态分配空间的new,不需要手动释放 delete
//系统会自动释放

窗口界面, 菜单栏,状态栏, 浮动窗口等的编写

菜单栏

QMenuBar *mBar = menuBar();
setMenuBar(mBar); //不设置也能显示
//添加菜单
QMenu *pFile = mBar->addMenu("文件");

//添加菜单项, 添加动作
QAction *pNew = pFile->addAction("新建");

//添加触发事件
connect(pNew, &QAction::triggered,
[=]()
{
mBar->addMenu("hello");
}
);

//添加分隔线
pFile->addSeparator();
//添加菜单项, 添加动作
QAction *pOpen = pFile->addAction("打开");

工具栏, 是菜单项的快捷方式

QToolBar *toolBar = addToolBar("toolBar");

//工具栏添加快捷键, 将要实现功能的指针传入就行了, 这里传入的是新建的动作
toolBar->addAction(pNew);


//在工具栏添加按钮
QPushButton *button = new QPushButton(this);
button->setText("***");
//添加小控件, 在toolBar上添加控件(如按钮)
toolBar->addWidget(button);
//添加动作
connect(button, &QPushButton::pressed,
[=]()
{
button->setText("???");
qDebug() << "new";
}
);

状态栏

QStatusBar *fettleBar = statusBar();
//添加标签控件
QLabel *label = new QLabel(this);
label->setText("www.sorrowfeng.top");

//给状态栏来添加一个label控件来显示
fettleBar->addWidget(label);

//addWidget从左往右添加, 这里用的是QLabel构造函数直接创建
fettleBar->addWidget(new QLabel("1399600304", this));
//addPermanentWidget从右往左添加
fettleBar->addPermanentWidget(new QLabel("sorrowfenggg", this));

核心控件,文本框

QTextEdit *textEdit = new QTextEdit(this);
setCentralWidget(textEdit); //添加中心文本框

浮动窗口

QDockWidget *dockWidget = new QDockWidget(this);
addDockWidget(Qt::RightDockWidgetArea,dockWidget); //添加浮动窗口
//为浮动窗口添加一个文本控件
QTextEdit *textEditDock = new QTextEdit(this);
dockWidget->setWidget(textEditDock);

模态对话框与非模态对话框

QMenuBar *mBar = menuBar();
setMenuBar(mBar); //不设置也能显示

QMenu *pNew = mBar->addMenu("新建");

QAction *pMotai = pNew->addAction("新建模态对话框");
QAction *pUnMotai = pNew->addAction("新建非模态对话框");
//显示模态对话框
connect(pMotai, &QAction::triggered,
[=]()
{
QDialog dlg;
dlg.exec();
}
);
//显示非模态对话框
connect(pUnMotai, &QAction::triggered,
[=]()
{
//方法一:将dlg作为成员变量
dlg.show();

//方法二:不指定父对象, 设置属性delete对象
//并不使用指定父对象的方法, 因为指定父对象只有在父对象被释放时才会释放, 容易占用内存过多
QDialog *p = new QDialog;
//设置属性: 在关闭时delete对象
p->setAttribute(Qt::WA_DeleteOnClose);
p->show();
}
);

Dialog对话框

显示关于对话框

connect(pAbout, &QAction::triggered,
[=]()
{
QMessageBox::about(this, "关于", "www.sorrowfeng.top");
}
);

QAction *pQuestion = pNew->addAction("问题对话框");

显示问题对话框

connect(pQuestion, &QAction::triggered,
[=]()
{
int ret = QMessageBox::question(this, "question", "确认?", QMessageBox::Ok | QMessageBox::Cancel);
//对按下之后的返回值做处理
switch(ret)
{
case QMessageBox::Ok:
qDebug() << "ok";
break;
case QMessageBox::Cancel:
qDebug() << "cancel";
break;
default:
break;
}
}
);

显示打开文件对话框

QAction *pFileDlg = pNew->addAction("文件对话框");
connect(pFileDlg, &QAction::triggered,
[=]()
{
QString path = QFileDialog::getOpenFileName(
this,
"open", //打开标题名
"../", //打开路径
"source(*.c *.cpp *.h);;text(*.txt *.md);;all(*.*)" //过滤文件格式
);
qDebug() << path;
}
);

常用控件的使用

这里的lineEdit通过ui文件设置
//获取输入框文本
QString str = ui->lineEdit->text();
qDebug() << str;

//修改输入框文本
ui->lineEdit->setText("hello");

//设置内容显示间隙
ui->lineEdit->setTextMargins(15, 0, 0, 0);

//设置内容显示方式
ui->lineEdit->setEchoMode(QLineEdit::PasswordEchoOnEdit);

自定义控件

/*
* 自定义控件
* 新建一个class, 继承于基类
* 继承于基类, 要提升QLabel就要继承于QLabel, 提升QWidget就继承于QWidget
* 在ui界面使用时, 先放一个布局, 右键, 提升为, 填写类名, 一般勾上全局包含
*/

QSpinBox *spin = new QSpinBox(this);
QSlider *slider = new QSlider(Qt::Horizontal,this);

//把控件添加到布局中
QHBoxLayout *hLayout = new QHBoxLayout(this);
hLayout->addWidget(spin);
hLayout->addWidget(slider);

//setLayout(hLayout); //没有指定父对象时需要人为添加

//另一种选择重载信号的方式
//通过数值改变滑块的位置
connect(spin, static_cast<void (QSpinBox::*) (int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);

//通过滑块的位置改变数值
connect(slider, &QSlider::valueChanged, spin, &QSpinBox::setValue);

样式修改

//    部件的前景色用于绘制上面的文本,可以通过 color属性指定。
// 背景色用于绘制部件的壤充矩形,可以通过 background-color属性指定。
// 背景图片使用 background-image属性定义,它用于绘制由 background-origin
// 指定的矩形区域(空白、边框、填充或内容)。背景图片在矩形区域内的对齐和
// 平铺方式可以通过 background-position和 background-repeat隅性指定
// 如果指定的背景图片具有a1pha通道(即有半透明效果),通过 background-color
// 指定的颜色将会透过透明区域,这一功能可以使背景图片在多种环境下重复利用


//全局样式
// this->setStyleSheet("QLabel{ color:#0000ff; "
// "background-color:#cecece;"
// "}");
//设置样式, 针对某标签的样式//设置图片背景
ui->label->setStyleSheet("QLabel{ color:#af0fef;"
"background-color:#bebebe;"
// "background-image:url(://Image/image.png);" //自动平铺
"border-image:url(://Image/image.png);" //自适应
"}");

//伪状态
ui->pushButton->setStyleSheet("QPushButton{"
"border: 2px outset green;"
"}"
"QPushButton:pressed{" //按下后改变状态
"border-image:url(://Image/image.png);"
"}");

//下面是可用的伪状态列表:
//伪状态描述
//:checked button部伴被选中
//:disabled 部件被禁用
//:enabled 部件被启用
//:focus 部件获得焦点
//:hover 鼠标位于部件上
//:indeterminate checkbox或 radiobutton被部分选中
//:off 部件可以切换,且处于off状态
//:on 部件可以切换,且处于on状态
//:pressed 部件被鼠标按下
//:unchecked button部件未被选中


事件

/*
Qt程序得要在main0函数创建一个QApplication
对象,然后调用它的exec()函数。这个函数就是开始Qt的事件循环。在执行
exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,
Qt将创建一个事件对象。Qt中所有事件类都继承于 QEvent。在事件对象创建
完毕后,Qt将这个事件对象传递给 QObject的 event()函数。 event()函数并不
直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数( event
handler),
在所有组件的父类 QWidget中,定义了很多事件处理的回调函数,如
keyPressEvent()
keyReleaseEvent()
mouseDoubleClickEvent()
mouseMoveEvent()
mousePressEvent()
mouseReleaseEvent()等*/

自定义的label控件

/*以下为自定义的label控件, 在其中调用event事件*/
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
//设置默认追踪鼠标, 这样这个例子里鼠标一进入就可以移动了, 不需要点一下
this->setMouseTracking(true);
}


void MyLabel::mousePressEvent(QMouseEvent *ev)
{
/*
* Qt中格式化为字符串的方法
* %1, %2 代表第一个第二个要填充的位置, 分别对应后面arg里的参数
* QString str = QString("hello %1 !! %2").arg("sorrowfeng").arg(1999)
*/
int mouseX = ev->x();
int mouseY = ev->y();

if(ev->button() == Qt::LeftButton) {
qDebug() << "左键按下";
}else if(ev->button() == Qt::RightButton) {
qDebug() << "右键按下";
}else if(ev->button() == Qt::MidButton) {
qDebug() << "中键按下";
}

QString posXY = QString("<center><h1>Pressed X: %1 Y: %2</h1></center>").arg(mouseX).arg(mouseY);

this->setText(posXY);
}

void MyLabel::mouseReleaseEvent(QMouseEvent *ev)
{
QString posXY = QString("<center><h1>Release X: %1 Y: %2</h1></center>").arg(ev->x()).arg(ev->y());
this->setText(posXY);
}

void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
QString posXY = QString("<center><h1>Move X: %1 Y: %2</h1></center>").arg(ev->x()).arg(ev->y());
// this->setText(posXY);
}

void MyLabel::enterEvent(QEvent *)
{
QString text = QString("<center><h1>enter</h1></center>");
this->setText(text);

}

void MyLabel::leaveEvent(QEvent *)
{
QString text = QString("<center><h1>leave</h1></center>");
this->setText(text);
}

响应键盘事件与定时器事件

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

this->id = this->startTimer(1000); //间隔1000ms进入一次定时中断函数
}

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

void MainWidget::keyPressEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key_Escape)
{
qDebug() << "esc被按下";
this->close();
}

}

void MainWidget::timerEvent(QTimerEvent *)
{
static int sec = 0;
if(sec == 10)
this->killTimer(id); //停止定时器

ui->label->setText(QString("<center><h1>time %1</h1></center>").arg(sec++));
}

事件的接收与忽略

//accept, ignore通常使用方法
void MainWidget::closeEvent(QCloseEvent *e)
{
int ret = QMessageBox::question(this, "close", "sure close?");

if(ret == QMessageBox::Yes)
{
//关闭窗口
//处理窗口关闭事件, 接收事件, 事件就不会继续往下传递
e->accept();
}
else
{
//不关闭窗口
//忽略事件, 事件继续给父组件传递
e->ignore();
}
}

event函数的使用

//通常不修改event函数, 容易出问题

bool MainWidget::event(QEvent *e)
{
// 事件分发
// switch (e->type()) {
// case QEvent::Close:
// closeEvent(e);
// break;
// case QEvent::MouseMove:
// mouseMoveEvent(e);
// break;
// default:
// break;
// }

if(e->type() == QEvent::Timer)
{
//将QEvent *类型的e转换成QTimerEvent *类型
// QTimerEvent * ev = static_cast<QTimerEvent *>(e);
// timerEvent(ev);
//这里事件停止传播, 目的时直接在上层关掉定时器
//返回true事件停止传播
return true;
}
else if(e->type() == QEvent::KeyPress)
{
//这里的含义是, 直接在上层处理按键J, J则原样方式传播, 其他则停止传播
QKeyEvent *ev = static_cast<QKeyEvent *>(e);
if(ev->key() == Qt::Key_J)
{
return QWidget::event(e);
}
return true;
}
else
{
//使其他事件按照原来方式继续传播
return QWidget::event(e);
}
}

事件过滤器

bool MainWidget::eventFilter(QObject *obj, QEvent *e)
{
if(obj == ui->label_2) {
QMouseEvent *ev = static_cast<QMouseEvent *>(e);
if(e->type() == QEvent::MouseMove) {
ui->label_2->setText(QString("<center><h1>%1 %2</h1></center>").arg(ev->x()).arg(ev->y()));
return true;
}
}
return QWidget::eventFilter(obj, e);
}

生成随机数

int num;
//以从0时0分0秒到现在的秒数为种子
qsrand(uint(QTime(0,0,0).secsTo(QTime::currentTime()) ));
//调用全局的 qrand()函数生成随机数,对10000取余,保证位于10000的范围内
while(( num = qrand()%10000)<999 );

绘图

//指定绘图设备
//方法一: 构造函数传参
QPainter p(this);

//方法二: 使用begin与end函数
// QPainter p;
// p.begin(this);
// 绘图操作
// p.end();

//画背景图
//注意图片文件夹要放在工程文件夹的同一级路径, 资源文件夹才放在工程文件夹里面
// p.drawPixmap(0, 0, width(), height(), QPixmap("../image/background.png"));
p.drawPixmap(rect(), QPixmap("../image/background.png"));//使用rect()可以直接获取窗口的巨型区域

//自定义画笔
QPen pen;
pen.setWidth(8);//设定宽度
pen.setColor("#FFFFFF");//设置颜色
pen.setStyle(Qt::DashLine);//设置风格
//把画笔交给画家
p.setPen(pen);

//创建画刷对象
QBrush brush;
brush.setColor(Qt::white);//画刷颜色
brush.setStyle(Qt::SolidPattern);//填充风格
//把画刷交给画家
p.setBrush(brush);

//画直线
p.drawLine(0, 0, 200, 200);
//画矩形
p.drawRect(10, 10, 100, 100);
//画圆形
p.drawEllipse(QPoint(200, 200), 20, 20);
//画图片, 宽高可以不写, 不写则是图片原始宽高
p.drawPixmap(x, 200, 80, 80, QPixmap("../image/icon.png"));

窗口重绘

//如果给窗口绘图, 一定要写在paintEvent里面
//不要写在paintEvent()里面, 会进入无限递归
void MainWidget::on_pushButton_clicked()
{
this->x += 20;
if(x >= width()) this->x = 0;
//整个窗口都重绘, 传入参数可以指定重绘某个区域,
this->update(); //窗口重绘, 间接重绘paintEvent()
}

QPixmap与QBitmap

//QPixmap用于画彩色图片, QBitmap用于画黑白图片
//QPixmap背景透明
p.drawPixmap(200, 100, 80, 80, QPixmap("../image/icon.png"));
//QBitmap背景透明
p.drawPixmap(300, 100, 80, 80, QBitmap("../image/icon.png"));
//QPixmap背景白色
QPixmap pixmap;
pixmap.load("../image/icon.png");
p.drawPixmap(0, 100, 80, 80, pixmap);
//QBitmap背景白色
QBitmap bitmap;
bitmap.load("../image/icon.png");
p.drawPixmap(100, 100, 80, 80, bitmap);

绘图设备

QPixmap

 //这里不在窗口里面绘图, 可以在这里写, 要是再窗口里面绘图, 一定要在paintEvent里面写
//绘图设备
QPixmap pixmap(400, 300);
//指定绘图设备
QPainter p(&pixmap);
//填充白色背景色
// p.fillRect(rect(), QBrush(Qt::white));
pixmap.fill(Qt::white);
//在绘图设备里画图片
p.drawPixmap(0, 0, 80, 80, QPixmap("../image/icon.png"));
//保存图片
pixmap.save("../pixmap_save.png");

QImage

    //创建绘图设备,QImage::Format_ARGB32背景是透明
QImage image(400, 300, QImage::Format_ARGB32);
//指定绘图设备
QPainter p(&image);
//画图
p.drawImage(0,0,QImage("../image/background.png"));
//可以对图片进行操作, 这里将前50个像素点变成红色
for (int i=0;i<50;i++) {
for (int j=0;j<50;j++) {
image.setPixel(QPoint(i,j), qRgb(255, 0, 0)); //设置像素点
// image.pixel(QPoint(i,j));//也可以获取像素点
}
}
//保存图片
image.save("../image_save.png");


QPicture

//可以显示在窗口上, 保存的图片是打不开的, 因为保存的只是二进制数据 
//二进制文件可以发给别处, 能够原样显示出来
QPicture picture;
QPainter p(&picture);

p.drawPixmap(0, 0, 100, 100, QPixmap("../image/background.png"));
p.drawLine(0, 0, 50, 50);

p.end();

picture.save("../picture_save.png");

void MainWidget::paintEvent(QPaintEvent *)
{
QPicture pic;
pic.load("../picture_save.png");

QPainter p(this);
p.drawPicture(0, 0, pic);
}

QPixmap与QImage之间的相互转换

QPainter p(this);
QPixmap pixmap;
pixmap.load("../image/background.png");

//QPixmap to QImage
QImage tempImage = pixmap.toImage();
p.drawImage(0, 0, tempImage);

//QImage to QPixmap
QPixmap tempPixmap = QPixmap::fromImage(tempImage);
p.drawPixmap(0, 200, tempPixmap);

不规则窗口

MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
ui->setupUi(this);
//去边框
this->setWindowFlag(Qt::FramelessWindowHint); // | windowFlags()

//把窗口背景设置为透明
this->setAttribute(Qt::WA_TranslucentBackground);
}

void MainWidget::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.drawPixmap(0, 0, QPixmap("../image/icon.png"));
}

void MainWidget::mousePressEvent(QMouseEvent *e)
{

if(e->button() == Qt::RightButton) {
//右键关闭窗口
this->close();
} else if (e->button() == Qt::LeftButton) {
//求坐标差值
//当前点击坐标 - 窗口左上角坐标
p = e->globalPos() - this->frameGeometry().topLeft();
}
}

void MainWidget::mouseMoveEvent(QMouseEvent *e)
{
//实现鼠标左键拖动
if(e->buttons() & Qt::LeftButton) {
this->move(e->globalPos() - p);
}
}

Qstring与QByteArray和char *之间的转换

//QString to QByteArray
QString temp = "hello";
QByteArray array = temp.toUtf8();//转为utf-8, 解决中文
array = temp.toLocal8Bit(); //转为本地编码, 本地是是什么就转什么

//QByteArray to char *
char *p = array.data();

//char * 和 QByteArray to QString
QString s = QString(p);

QFile文件的读取和写入

void MainWidget::on_readButton_clicked()
{
//读取中文要utf-8格式的
QString path = QFileDialog::getOpenFileName(this, "open", "../", "TXT(*.txt)");

//path非空
if(!path.isEmpty())
{
//文件对象, 以只读方式打开, 默认只识别utf-8
QFile file(path);
if(file.open(QIODevice::ReadOnly)) //如果打开成功
{
#if 0
//一起读文件, 一般不一起读
QByteArray arr = file.readAll();
//显示到编辑区
ui->textEdit->setText(QString(arr));
#endif
//每行读文件
QByteArray arr;
while (!file.atEnd()) { //当还没读到最后一行
arr += file.readLine(); //把每一行加到arr上
}
//读取完毕显示
ui->textEdit->setText(arr);
}
//关闭文件
file.close();
}
}

void MainWidget::on_writeButton_clicked()
{
QString path = QFileDialog::getSaveFileName(this, "save", "../", "TXT(*.txt);;ALL(*.*)");
if(!path.isEmpty())
{
QFile file;
//关联文件对象
file.setFileName(path);
//以只写方式打开文件
if(file.open(QIODevice::WriteOnly))
{
//获取编辑区的内容
QString str = ui->textEdit->toPlainText();
//QString to QByteArray
file.write(str.toUtf8());
}
file.close();
}
}

获取文件信息

QFileInfo info(path);
qDebug() << info.fileName(); //文件名字
qDebug() << info.suffix(); //文件后缀
qDebug() << info.created().toString("yyyy-MM-dd hh:mm:ss"); //文件创建时间, 显示年月日时分秒
qDebug() << info.size(); //文件大小

QDataStream数据流操作文件

void MainWidget::writedata()
{
QFile file("../test.txt");
if(file.open(QIODevice::WriteOnly))
{
//创建数据流, 关联file
//往数据流中输入数据, 就是像文件中写入数据
QDataStream stream(&file);
//往数据流中输入数据
stream << QString("sorrowfeng彭烈枫") << 100;

file.close();
}
}

void MainWidget::readdata()
{
QFile file("../test.txt");
//以只读方式打开文件
if(file.open(QIODevice::ReadOnly))
{
QDataStream stream(&file);
//读的时候要按写入文件的顺序读, 写的时候是先string再int, 读也要先string再int
QString str;
int i;
//从数据流读取文件
stream >> str >> i;
//打印输出
qDebug() << str << i;

file.close();
}
}

QTextStream操作文件

//好处: 读写时可以指定编码

void MainWidget::writedata()
{
QFile file("../temp.txt");
if(file.open(QIODevice::WriteOnly))
{
QTextStream stream(&file); //创建文本流并绑定文件
stream.setCodec("UTF-8"); //指定编码写入
//写入数据
stream << "www.sorrowfeng.top" << 100;

file.close();
}
}

void MainWidget::readdata()
{
QFile file("../temp.txt");
if(file.open(QIODevice::ReadOnly))
{
#if 0
//读取文件不使用这种方法, 读取到的i的值是0, 是因为会把所有内容都给str, 没有分隔,
QTextStream stream(&file);
stream.setCodec("UTF-8"); //指定编码读取
//读取文件
QString str;
int i;
stream >> str >> i;
cout << str << i;
//关闭文件
file.close();
#endif
QTextStream stream(&file);
stream.setCodec("UTF-8"); //指定编码读取
QByteArray arr;
while(!stream.atEnd())
{
arr += stream.readLine();
}
cout << arr;
file.close();
}
}

QBuffer缓冲区

QByteArray arr;
QBuffer buf(&arr); //将缓冲区数据放入arr
if(buf.open(QIODevice::WriteOnly))
{
buf.write("sorrowfeng");
buf.write("1399600304");
}
buf.close();
cout << buf.buffer(); //打印缓冲区数据
cout << arr; //打印QByteArray数据

//与QDataStream配合使用
QBuffer buf_data;
if(buf_data.open(QIODevice::WriteOnly))
{
QDataStream stream(&buf_data);
stream << "www.sorrowfeng.top"; //将内容写入缓冲区
buf_data.close();
}
cout << buf_data.buffer(); //打印缓冲区数据

TCP

要用网络时要加上network模块
QT += core gui network
//要在项目文件的地方加上一个network

#include <QTcpServer> //监听套接字
#include <QTcpSocket> //通信套接字
//服务器端需要两个套接字, 而客户端只要一个通信套接字就行了

server服务器端

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

this->setWindowTitle("Tcp Server(port: 8888)");

//先给指针个空值, 方便后面判断
tcpserver = nullptr;
tcpsocket = nullptr;
//监听套接字, 指定父对象让其自动回收空间
tcpserver = new QTcpServer(this);

//监听, 第一个参数为默认值, 表示绑定本机网卡所有地址, 第二个参数指定端口号
tcpserver->listen(QHostAddress::Any, 8888);

//响应newConnection, 显示连接信息
connect(tcpserver, &QTcpServer::newConnection,
[=]()
{
//下面获取出客户端传来的信息
//取出连接好的套接字
tcpsocket = tcpserver->nextPendingConnection();
//取出ip地址和端口号
QString ip = tcpsocket->peerAddress().toString();
quint16 port = tcpsocket->peerPort();
//显示出来, 使用append追加, 用setText会覆盖
ui->receiveText->setText(QString("[%1 : %2], connected success!").arg(ip).arg(port));


//显示客户端发来的消息
connect(tcpsocket, &QTcpSocket::readyRead,
[=]()
{
//接收从tcpsocket传来的所有内容, 并追加显示
QByteArray arr = tcpsocket->readAll();
ui->receiveText->append(QString(arr));
}
);
}
);

}

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

//发送信息到客户端
void ServerWidget::on_sendButton_clicked()
{
if(nullptr == tcpsocket) return;
//发送信息到tcpsocket, 因为上面已经建立好连接了
//获取编辑区内容
QString sendstr = ui->sendText->toPlainText();

//QString 转为 QByteArray 并发送
tcpsocket->write(sendstr.toUtf8());
}

void ServerWidget::on_closeButton_clicked()
{
if(nullptr == tcpsocket) return;
//主动与客户端断开连接
tcpsocket->disconnectFromHost();
tcpsocket->close();
tcpsocket = nullptr; //断开之后重新赋值为空

this->close();
}

client客户端

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

this->setWindowTitle("client");
tcpsocket = nullptr;
//分配内存, 并指定父对象
tcpsocket = new QTcpSocket(this);
//成功与服务器连接, 响应对面发来的connected信号
connect(tcpsocket, &QTcpSocket::connected,
[=]()
{
ui->receiveText->setText(QString("connect success!"));
}
);

//读取客户端发来的所有内容
connect(tcpsocket, &QTcpSocket::readyRead,
[=]()
{
QByteArray arr = tcpsocket->readAll();
ui->receiveText->append(QString(arr));
}
);

//对面server主动断开连接
connect(tcpsocket, &QTcpSocket::disconnected,
[=]()
{
ui->receiveText->append(QString("server disconnect!"));
}
);

}

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

void clientwidget::on_sendButton_clicked()
{
if(nullptr == tcpsocket) return;
//获取编辑区内容
QString sendstr = ui->sendText->toPlainText();
//QString 转为 QByteArray 并发送
tcpsocket->write(sendstr.toUtf8());

}

void clientwidget::on_closeButton_clicked()
{
if(nullptr == tcpsocket) return;
//主动与服务器断开连接
tcpsocket->disconnectFromHost();
tcpsocket->close();
tcpsocket = nullptr;

this->close();
}

void clientwidget::on_connectButton_clicked()
{
if(nullptr == tcpsocket) return;
//获取输入的ip与端口号
QString ip = ui->ipLineEdit->text();
quint16 port = quint16(ui->portLineEdit->text().toInt());

//主动与服务器建立连接
tcpsocket->connectToHost(QHostAddress(ip), port);
}

UDP

MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
ui->setupUi(this);
udpsocket = nullptr;
//分配内存
udpsocket = new QUdpSocket(this);
//绑定端口号
udpsocket->bind(9000);
#if 0
// udpsocket->bind(QHostAddress::AnyIPv4, 9000); //使用组播地址的时候要这样绑定端口号

//加入某个组播, (群聊), 组播地址必须是第一类地址
udpsocket->joinMulticastGroup(QHostAddress("224.0.0.2"));
//退出某个组播
udpsocket->leaveMulticastGroup(QHostAddress("224.0.0.2"));
#endif
this->setWindowTitle("port: 9000");
//处理接收信息, 接收数据
connect(udpsocket, &QUdpSocket::readyRead,
[=]()
{
char buf[1024] = {0};
QHostAddress clientAddr;
quint16 port;
qint64 len = udpsocket->readDatagram(buf, sizeof(buf), &clientAddr, &port);

if(len > 0)
{
QString str = QString("[%1 : %2] %3").arg(clientAddr.toString()).arg(port).arg(buf);

ui->textEdit->setText(str);
}
}
);

}

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

//发送数据
void MainWidget::on_sendButton_clicked()
{
if(nullptr == udpsocket) return;
//获取对方的ip和端口号
QString ip = ui->ipLineEdit->text();
quint16 port = quint16(ui->portLineEdit->text().toInt());
//获取编辑区内容
char *buf = ui->textEdit->toPlainText().toUtf8().data();
//给指定的ip发送数据
udpsocket->writeDatagram(buf, sizeof(buf), QHostAddress(ip), port);
}

void MainWidget::on_closeButton_clicked()
{
if(nullptr == udpsocket) return;
udpsocket->disconnectFromHost();
udpsocket->close();

this->close();
}

QTImer定时器

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

myTimer = new QTimer(this);

//响应timeout信号
connect(myTimer, &QTimer::timeout,
[=]()
{
static int i = 0;
i++;
ui->lcdNumber->display(i);
}
);
}

void MainWidget::on_buttonStart_clicked()
{
//启动定时器, 时间间隔为1000ms, 每隔1000ms会发出一个timeout信号
if(!myTimer->isActive()) //当定时器未激活时才启动
myTimer->start(1000);
}

void MainWidget::on_buttonStop_clicked()
{
//当定时器时启动的时候, 停止定时器
if(myTimer->isActive())
myTimer->stop();
}

线程

新建线程类时, 不要选QWidget, 因为它不是控件, 选Qobject

老方法

UW3xfS.png

class MyThread : public Qthread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);

protected:
//线程处理函数, 不能直接调用, 要通过start函数来间接调用
void run();

signals:
void isDone();

public slots:
};
void MyThread::run()
{
//因为继承于QThread
this->sleep(5);

emit isDone();
}
调用
MyThread *myThread;
myThread = new MyThread(this);
//不能直接调用run, 要通过start函数来间接调用
myThread->start();
//点击右上角的×时, 关闭线程
connect(this, &MainWidget::destroyed,
[=]()
{
//关闭线程
myThread->quit();
//等待线程处理完
myThread->wait();
}
);

新方法

UW89yj.png

主线程
signals:
void startThread(); //启动子线程的信号


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

//分配内存, 注意不能指定父对象, 指定了就不能将对象移入myThread中了
myThread = new MyThread;

//创建子线程
thread = new QThread(this);

//把自定义的线程加入到子线程中
myThread->moveToThread(thread);

connect(myThread, &MyThread::mySignal,
[=]()
{
static int i = 0;
i++;
ui->lcdNumber->display(i);
}
);

connect(this, &MainWidget::startThread, myThread, &MyThread::myTimeOut);
}

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

void MainWidget::on_buttonStart_clicked()
{
//启动线程, 但没有启动线程函数
thread->start();
myThread->setFlag(false);

//不能直接调用线程处理函数, 会导致线程处理函数与主线程在同一个线程
//只能通过signal-slot的方法来调用
emit startThread();
}

void MainWidget::on_buttonStop_clicked()
{
myThread->setFlag(true);
//退出线程
thread->quit();
thread->wait();
}



子线程
//线程处理函数
void myTimeOut();
void setFlag(bool flag = true); //设置标志位

signals:
void mySignal();


//线程处理函数, 槽函数, 等带主线程发送信号过来触发
void MyThread::myTimeOut()
{
while(false == isStop)
{
//每隔100ms发送一次信号
QThread::msleep(100);
emit mySignal();
if(true == isStop) break;
}
}

void MyThread::setFlag(bool flag)
{
isStop = flag;
}

线程函数内部, 不允许处理图形界面操作

conne第五个参数的作用

//connect()函数第五个参数的作用, 指定连接方式, 默认 直接 队列
//只有在多线程的时候才有意义
//默认的时候, 如果是单线程, 用直接; 如果是多线程, 用队列
//队列: 槽函数所在线程与接收者一样
//直接: 槽函数所在线程与发送者一样

数据库操作: QMYSQL

要加上模块 sql
要将libmysql.dll的dll文件加入到qt的安装路径中Qt\Qt5.12.2\5.12.2\mingw73_32\bin

#include "mainwidget.h"
#include "ui_mainwidget.h"
#include <QSqlDatabase>
#include <QMessageBox>
#include <QSqlError>
#include <QSqlQuery>
#include <QVariantList>
#include <QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"

MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
ui->setupUi(this);
//打印Qt支持的数据库驱动, 模块加上sql
cout << QSqlDatabase::drivers();
//添加MYSQL数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
//连接数据库
db.setHostName("127.0.0.1"); //设置数据库ip
db.setUserName("root"); //设置数据库用户名
db.setPassword("111111"); //设置密码
db.setDatabaseName("info"); //设置要使用的数据库
//打开数据库
if(!db.open())
{
QMessageBox::warning(this, "warning", db.lastError().text());
return; //如果打开失败
}
QSqlQuery query;

// 有多个数据库时, 要指定数据库,
// 如果定义了连接名"a", 一定要在query后面指定数据库
// QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "a");
// QSqlQuery query(db);

//执行sql语句
//插入数据
query.exec("insert into student values (1,'张三',18,'一班');");

//批量插入
#if 0
//odbc风格
//预处理语句
//? 相当于占位符
query.prepare("insert into teacher(teacher_name,teacher_sex) values (?, ?);");
//给字段设置内容 list
QVariantList namelist;
namelist << "张三" << "李四";
QVariantList sexlist;
sexlist << "男" << "女";
//给字段绑定相应的值, 一定要按顺序绑定
query.addBindValue(namelist);
query.addBindValue(sexlist);
//执行预处理命令
query.execBatch();
#endif
//oracle风格
//占位符 : + 自定义名字
query.prepare("insert into teacher(teacher_name,teacher_sex) values (:teacher_name, :teacher_sex);");
//给字段设置内容 list
QVariantList namelist;
namelist << "张三" << "李四";
QVariantList sexlist;
sexlist << "男" << "女";
//给字段绑定相应的值
query.bindValue(":teacher_name", namelist);
query.bindValue("teacher_sex", sexlist);
//执行预处理命令
query.execBatch();

//查找数据
query.exec("select * from student;");
while(query.next()) //遍历每一行
{
//取出当前行的内容
cout << query.value(0).toInt() //打印当前行第0列的内容
<< query.value(1).toString()
<< query.value("teacher_sex").toString(); //打印teacher_sex这个字段的内容
}
}

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

void MainWidget::on_buttonDel_clicked()
{
//获取编辑行内容
QString name = ui->lineEdit->text();

QString sql = QString("delete from student where student_name=%1;").arg(name);

//开启一个事务
QSqlDatabase::database().transaction();
//执行sql语句
QSqlQuery query;
query.exec(sql);
}

void MainWidget::on_buttonSure_clicked()
{
//确认删除
QSqlDatabase::database().commit();
}

void MainWidget::on_buttonCancel_clicked()
{
//回滚, 撤销
QSqlDatabase::database().rollback();
}

数据库操作: SQLITE

MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
ui->setupUi(this);
//打印Qt支持的数据库驱动, 模块加上sql
cout << QSqlDatabase::drivers();
//添加MYSQL数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
//设置数据库
db.setDatabaseName("../info.db");
//打开数据库
if(!db.open())
{
QMessageBox::warning(this, "warning", db.lastError().text());
return; //如果打开失败
}

QSqlQuery query;
query.exec("create table teacher(teacher_id int primary key,teacher_name varchar(50),teacher_sex varchar(20));");
//odbc风格
//预处理语句
//? 相当于占位符
query.prepare("insert into teacher(teacher_id, teacher_name,teacher_sex) values (?, ?, ?);");
//给字段设置内容 list
QVariantList idlist;
idlist << 1 << 2;
QVariantList namelist;
namelist << "张三" << "李四";
QVariantList sexlist;
sexlist << "男" << "女";
//给字段绑定相应的值, 一定要按顺序绑定
query.addBindValue(idlist);
query.addBindValue(namelist);
query.addBindValue(sexlist);
//执行预处理命令
query.execBatch();

//查找数据
query.exec("select * from teacher;");
while(query.next()) //遍历每一行
{
//取出当前行的内容
cout << query.value(0).toInt() //打印当前行第0列的内容
<< query.value(1).toString()
<< query.value("teacher_sex").toString(); //打印teacher_sex这个字段的内容
}
}

数据库可视化操作:QSqlTableModel

#include "mainwidget.h"
#include "ui_mainwidget.h"
#include <QSqlTableModel>
#include <QSqlDatabase>
#include <QMessageBox>
#include <QSqlError>
#include <QSqlQuery>
#include <QVariantList>
#include <QSqlRecord>
#include <QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"
MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
ui->setupUi(this);
this->setWindowTitle("数据库可视化操作");
//打印Qt支持的数据库驱动, 模块加上sql
cout << QSqlDatabase::drivers();
//添加MYSQL数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
//设置数据库
db.setDatabaseName("../info.db");
//打开数据库
if(!db.open())
{
QMessageBox::warning(this, "warning", db.lastError().text());
return; //如果打开失败
}
QSqlQuery query;
query.exec("create table teacher(teacher_id int primary key,teacher_name varchar(50),teacher_sex varchar(20));");
//odbc风格
//预处理语句
//? 相当于占位符
query.prepare("insert into teacher(teacher_id, teacher_name,teacher_sex) values (?, ?, ?);");
//给字段设置内容 list
QVariantList idlist;
idlist << 1 << 2;
QVariantList namelist;
namelist << "张三" << "李四";
QVariantList sexlist;
sexlist << "男" << "女";
//给字段绑定相应的值, 一定要按顺序绑定
query.addBindValue(idlist);
query.addBindValue(namelist);
query.addBindValue(sexlist);
//执行预处理命令
query.execBatch();
//查找数据
query.exec("select * from teacher;");
while(query.next()) //遍历每一行
{
//取出当前行的内容
cout << query.value(0).toInt() //打印当前行第0列的内容
<< query.value(1).toString()
<< query.value("teacher_sex").toString(); //打印teacher_sex这个字段的内容
}
//设置模型
model = new QSqlTableModel(this);
model->setTable("teacher"); //使用哪个表
//把model放在view里面
ui->tableView->setModel(model);
//显示model里的数据
model->select();
//修改显示的列名
model->setHeaderData(0,Qt::Horizontal, "教师编号");
model->setHeaderData(1,Qt::Horizontal, "教师姓名");
model->setHeaderData(2,Qt::Horizontal, "教师性别");
//修改编辑策略,为手动提交
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
//设置view中的数据库不允许修改
// ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
}
MainWidget::~MainWidget()
{
delete ui;
}
void MainWidget::on_buttonAdd_clicked()
{
//添加空记录
QSqlRecord record = model->record(); //获取空记录
//获取行号
int row = model->rowCount();
model->insertRecord(row, record);
}
void MainWidget::on_buttonSure_clicked()
{
//提交所有动作
model->submitAll();
}
void MainWidget::on_buttonCancel_clicked()
{
//取消所有动作
model->revertAll();
model->submitAll(); //提交
}
void MainWidget::on_buttonDel_clicked()
{
//获取选中的模型
QItemSelectionModel *sModel = ui->tableView->selectionModel();
//取出模型中的索引
QModelIndexList list = sModel->selectedRows();
//删除所有行
for(int i = 0; i < list.size(); i++)
{
model->removeRow(list.at(i).row());
}
}
void MainWidget::on_buttonFind_clicked()
{
QString name = ui->lineEdit->text();
QString str = QString("name = '%1'").arg(name);
model->setFilter(str);
model->select();
}

XML

要在项目配置中加上xml模块
新建c++类, 不继承任何东西

#include <QString>
class MyXML
{
public:
MyXML();
static void createXML(QString filepath); //创建XML空文件
};


#include "myxml.h"
#include <QFile>
#include <QTextStream>
#include <QDomDocument> //文件
#include <QDomProcessingInstruction> //格式头部
#include <QDomElement> //元素
#include <QDebug>
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"
MyXML::MyXML()
{

}
//创建xml空文件
void MyXML::createXML(QString filepath)
{
QFile file(filepath); //关联文件名字
//存在就不创建
if(file.exists())
{
cout << "文件已经存在";
return;
}
//不存在才创建
//只写方式打开文件
if(!file.open(QIODevice::WriteOnly))
{
cout << "WriteOnly打开文件失败";
return;
}
//创建xml文档对象
QDomDocument doc;
//创建xml头部格式
QDomProcessingInstruction ins;
//添加头部, 注意内部的引号要用\反斜杠
ins = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
//追加元素
doc.appendChild(ins);
//根节点元素
QDomElement root = doc.createElement("root");
doc.appendChild(root);
//根节点里面的元素, 在根节点上追加
QDomElement data = doc.createElement("data");
root.appendChild(data);
//保存
QTextStream stream(&file); //文本流关联文件
doc.save(stream, 4); //doc文件关联文本流, 4代表4个缩进字符
//关闭文件
file.close();
}
/*
* xml文档顺序, 所有标签节点都是元素element, 节点要转换成元素才能获取值
* 根节点 (documentElement)获取根节点, 这里函数返回直接就是一个元素(element)
* -子节点 节点(node)
* -----属性 (attribute获取内容, attributeNode获取属性对象)
* -----内容 (text)
*/

// 读取svg图片
QFile file("../test.svg");
// 打开文件
if(!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::information(nullptr, QString("title"),QString("open error!"));
return;
}
// 绑定文件
QString error;
int row = 0, column = 0;
if(!svgCode.setContent(&file, false, &error, &row, &column)) { //形参2,是否创建命名空间
QMessageBox::information(nullptr, QString("Error"),
QString("parse file failed at line row and column") +
QString::number(row, 10) + QString(",") +
QString::number(column, 10));
file.close();
return;
}
// 如果内容为空
if(svgCode.isNull()) {
QMessageBox::information(nullptr, QString("title"), QString("document is null!"));
file.close();
return;
}

// 获取根节点, 这里是svg
QDomElement root=svgCode.documentElement();
// root节点里是否含有属性"xmlns"
qDebug() << root.nodeName() << root.hasAttribute("xmlns") ;
// 打印该节点中含有"xmlns"属性的值
qDebug() << root.attributeNode("xmlns").value();
// 获取第一个子节点
QDomNode node = root.firstChild();
// 打印子节点的名字
qDebug() << node.nodeName();
// 注意节点要转换为Element才能得到其中value的值, 直接nodeValue是空的

// 得到value的两种方法
// 1. attribute, 直接一字符串返回value
QString id = node.toElement().attribute("id");
// 2. attributeNode, 返回的是个QDomAttr对象(属性), 通过value再得到里面的值
id = node.toElement().attributeNode("id").value();
// 获取元素内的内容text
qDebug() << node.toElement().text();

// 也可以来到子节点的下一个节点
node.nextSibling();

// 获取了root的所有子节点, 即svg的所有子节点
QDomNodeList list = root.childNodes();
for (int i = 0; i < list.length(); i++)
{
QDomNode node = list.at(i);
qDebug() << node.nodeName();
}

// 指针来到下一个节点, 这里是来到和root并行的下一个节点
QDomNode nextnode = root.nextSibling();

串口

导入模块 QT += serialport

#include "mainwidget.h"
#include "ui_mainwidget.h"
#include <QSerialPort>
#include <QFileDialog>
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QDebug>
#define cout qDebug() << "[" << __FILE__ << ": " << __LINE__ << "]"

MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
ui->setupUi(this);
file = new QFile(this);

// 获取可用的端口号,并传入QStringList
m_serialPortName = getPortNameList();
cout << m_serialPortName;
// 加入所有的端口到下拉框
ui->comboBoxCom->addItems(m_serialPortName);
// 默认设置为第一个
ui->comboBoxCom->setCurrentIndex(0);


m_serialPort = new QSerialPort();//实例化串口类一个对象

if(m_serialPort->isOpen())//如果串口已经打开了 先给他关闭了
{
m_serialPort->clear();
m_serialPort->close();
}


//连接信号槽 当下位机发送数据
//QSerialPort 会发送个 readyRead 信号
//定义槽函数void receiveInfo()解析数据
connect(m_serialPort,&QSerialPort::readyRead,this,&MainWidget::receiveInfo);
}

MainWidget::~MainWidget()
{
if (m_serialPort->isOpen())
{
m_serialPort->clear();
m_serialPort->close();
}
delete m_serialPort;
delete ui;
}
// 获取所有可用的端口号
QStringList MainWidget::getPortNameList()
{
QStringList m_serialPortName;
foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
{
m_serialPortName << info.portName();
cout << "serialPortName:" << info.portName();
}
return m_serialPortName;
}

// 处理接受到的数据
void MainWidget::receiveInfo()
{
QByteArray info = m_serialPort->readAll();
QByteArray hexData = info.toHex();
//这里面的协议 你们自己定义就行 单片机发什么 代表什么 我们这里简单模拟一下
if(hexData == "0x10000")
{
//do something
}
else if(hexData == "0x100001")
{
//do something
}
}

//写两个函数 向单片机发送数据
void MainWidget::sendInfo(char* info,int len)
{

for(int i=0; i<len; ++i)
{
printf("0x%x\n", info[i]);
}
m_serialPort->write(info,len);//这句是真正的给单片机发数据 用到的是QIODevice::write 具体可以看文档
}

// 发送信息
void MainWidget::sendInfo(const QString &info)
{
QByteArray sendBuf;
// if (info.contains(" "))
// {
// info.replace(QString(" "),QString(""));//我这里是把空格去掉,根据你们定的协议来
// }
qDebug()<<"Write to serial: "<<info;
convertStringToHex(info, sendBuf); //把QString 转换 为 hex

m_serialPort->write(sendBuf);//这句是真正的给单片机发数据 用到的是QIODevice::write 具体可以看文档
}


//基本和单片机交互 数据 都是16进制的 我们这里自己写一个 Qstring 转为 16进制的函数
void MainWidget::convertStringToHex(const QString &str, QByteArray &byteData)
{
int hexdata,lowhexdata;
int hexdatalen = 0;
int len = str.length();
byteData.resize(len/2);
char lstr,hstr;
for(int i=0; i<len; )
{
//char lstr,
hstr=str[i].toLatin1();
if(hstr == ' ')
{
i++;
continue;
}
i++;
if(i >= len)
break;
lstr = str[i].toLatin1();
hexdata = convertCharToHex(hstr);
lowhexdata = convertCharToHex(lstr);
if((hexdata == 16) || (lowhexdata == 16))
break;
else
hexdata = hexdata*16+lowhexdata;
i++;
byteData[hexdatalen] = (char)hexdata;
hexdatalen++;
}

byteData.resize(hexdatalen);
}

//另一个 函数 char 转为 16进制
char MainWidget::convertCharToHex(char ch)
{
/*
0x30等于十进制的48,48也是0的ASCII值,,
1-9的ASCII值是49-57,,所以某一个值-0x30,,
就是将字符0-9转换为0-9

*/
if((ch >= '0') && (ch <= '9'))
return ch-0x30;
else if((ch >= 'A') && (ch <= 'F'))
return ch-'A'+10;
else if((ch >= 'a') && (ch <= 'f'))
return ch-'a'+10;
else return (-1);
}

void MainWidget::on_comboBoxCom_activated(int index)
{
this->com_index = index;
qDebug() << com_index;
}

void MainWidget::on_pushButtonLink_clicked()
{
//设置串口名字
m_serialPort->setPortName(m_serialPortName[com_index]);
cout << m_serialPortName[com_index];
if(!m_serialPort->open(QIODevice::ReadWrite))//用ReadWrite 的模式尝试打开串口
{
QMessageBox::warning(this, "警告", "打开失败");
return;
}
//打开成功
QMessageBox::information(this, "打开", "打开成功");
ui->labelComText->setText(m_serialPortName[com_index] + "已成功连接");
m_serialPort->setBaudRate(115200,QSerialPort::AllDirections);//设置波特率和读写方向
m_serialPort->setDataBits(QSerialPort::Data8); //数据位为8位
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);//无流控制
m_serialPort->setParity(QSerialPort::NoParity); //无校验位
m_serialPort->setStopBits(QSerialPort::OneStop); //一位停止位
}

void MainWidget::on_pushButtonDisconnect_clicked()
{
if (m_serialPort->isOpen())
{
m_serialPort->clear();
m_serialPort->close();
QMessageBox::information(this, "关闭", "关闭成功");
ui->labelComText->setText("未连接");
return;
}
QMessageBox::warning(this, "警告", "关闭失败");
}

void MainWidget::on_pushButtonChoose_clicked()
{
QString filepath = QFileDialog::getOpenFileName(this, "open", "../");
if(!filepath.isEmpty())
{
// 只读方式打开文件
file->setFileName(filepath);
// 获取文件信息
QFileInfo fileinfo(filepath);
filename = fileinfo.fileName();
filesize = fileinfo.size();
sendsize = 0; //发送文件的大小
if(file->open(QIODevice::ReadOnly))
{
// 发送按钮亮起, 选择变灰
ui->pushButtonChoose->setEnabled(false);
ui->pushButtonSend->setEnabled(true);
}
}
}


void MainWidget::on_pushButtonSend_clicked()
{
qint64 len = 0;
do
{
//每次发送数据的大小
char buf[4*1024] = {0};
len = 0;
//读取文件中的数据
len = file->read(buf, sizeof (buf));
//返回已经写入的数据
len = m_serialPort->write(buf, len);
//积累的已经发送的文件数据
sendsize += len;
} while(len > 0); //只要写入的数据大于0, 就说明还没发完
//判断文件是否发送完成
if(filesize == sendsize)
{
QMessageBox::information(this, "完成", "文件发送完毕");
//关闭文件
file->close();

ui->pushButtonChoose->setEnabled(true);
ui->pushButtonSend->setEnabled(false);
}
}

正则

注意用QRegularExpression , 不要用QRegExp, 后者bug太多

QRegularExpression separators(".(?=[A-Za-z])");
QStringList tokens = d.split(separators);