1、绘图事件
Qt 的绘图系统实际上是,使用QPainter在QPainterDevice上进行绘制,它们之间使用QPaintEngine进行通讯(也就是翻译QPainter的指令)。
mywidget.h
protected: //重写绘图事件,虚函数 //如果在窗口绘图,必须放在绘图事件里实现 //绘图事件内部自动调用,窗口重绘的时候(状态改变:窗口变大变小、鼠标点击焦点改变等) //允许认为调用 void paintEvent(QPaintEvent *event) override;
mywidget.cpp
#include<QPainter>//画家 #include<QPen>//画笔 #include<QBrush>//画刷 #include<QPoint> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); x=0;//坐标 } Widget::~Widget() { delete ui; } //绘图事件 //绘图事件里面不要做太复杂的事件处理,耗时,因为该事件随时调用,自动调用 void Widget::paintEvent(QPaintEvent *event) { //创建画家对象 //方法一 //QPainter p(this); //方法二 QPainter p; p.begin(this);//指定当前窗口为绘图设备。。。后面一定要有p.end; //绘图操作 //p.drawxxx(); //画背景图 //不生成资源的话要把图片文件夹的位置放在项目文件的同一级目录 //p.drawPixmap(0,0,width(),height(),QPixmap("../image/1.jpg"));//指定背景图的坐标及大小:窗口的宽度、高度 //p.drawPixmap(rect(),QPixmap("../image/1.jpg"));//等价于上一行代码,rect()获取矩形区域 //定义画笔 QPen pen; pen.setWidth(5);//设置线宽 //pen.setColor(Qt::red); pen.setColor(QColor(128,255,255));//rgb设置颜色 pen.setStyle(Qt::DashDotLine);//设置风格(虚直线?) //把画笔交给画家 p.setPen(pen); //画直线 p.drawLine(50,50,150,50); p.drawLine(50,50,50,150); QBrush brush; brush.setColor(Qt::white); brush.setStyle(Qt::Dense1Pattern); p.setBrush(brush); //画矩形 p.drawRect(150,150,100,50); //画圆型 p.drawEllipse(QPoint(150,150),50,25); //画上图片 p.drawPixmap(x,180,80,80,QPixmap("../image/cute.jpg")); p.end(); } void Widget::on_pushButton_clicked() { x+=20; if(x>width()) { x=0; } //刷新窗口,让窗口重绘,整个窗口都刷新 update();//间接调用paintEvent(),update绝对不能放在paintEvent()里面,疯狂递归 }
2、QBitmap和QPixmap的区别:
QBitmap是QPixmap的子类
1)QPixmap,用于画彩色图片
2)QBitmap,用于只有黑白两种颜色,绘图事件中,若只要两种颜色建议用QBitmap,内存小一些,效率高,如光标文件和笔刷
#include<QPainter> #include<QPixmap> #include<QBitmap> void Widget::paintEvent(QPaintEvent *e) { //创建画家 QPainter p(this); //图片背景透明 //QPixmap p.drawPixmap(0,0,200,200,QPixmap("../image/cute2.png")); //QBitmap p.drawPixmap(200,0,200,200,QBitmap("../image/cute2.png")); //图片背景不透明 //QPixmap QPixmap pixmap; pixmap.load("../image/cute.jpg"); p.drawPixmap(0,200,200,200,pixmap); //QBitmap QBitmap bitmap; bitmap.load("../image/cute.jpg"); p.drawPixmap(200,200,200,200,bitmap); }
3、绘图设备
绘图设备是指继承QPainterDevice的子类。
QPixmap:针对屏幕进行优化,和平台无关,不能对图片对图片进行修改
QImage:和平台有关,可以对图片进行修改(修改像素点如,颜色),在线程中绘图(如图像处理、视频、地图)
QPicture:保存绘图的状态(二进制文件),主要用于平台不相关,可以记录和重现QPainter的各条指令
①QPixmap
#include "widget.h" #include "ui_widget.h" #include<QPainter> //不在窗口里面绘图,故不要求一定在paintEvent里实现 //现在指定的绘图设备为QPixmap,不是窗口,因此放在构造函数绘图就可以了 Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //绘图设备,400*300 QPixmap pixmap(400,300); //画家 QPainter p(&pixmap); //填充白色背景色,不填充的话为黑色 //p.fillRect(0,0,400,300,QBrush(Qt::white)); pixmap.fill(Qt::white);//与上面等价 p.drawPixmap(0,0,80,80,QPixmap("../image/cute2.png")); //画在设备上,非窗口,要把它保存成一张图片,不显示窗口,直接生成一张图片,名称与格式自己指定 pixmap.save("../pixmap.jpg"); }
② QImage:操作与QPixmap差不多
#include "widget.h" #include "ui_widget.h" #include<QPainter> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //绘图设备 //QImage::Format_ARGB32,透明背景色 QImage image(600,600,QImage::Format_ARGB32); //画家 QPainter p; p.begin(&image); //绘图可以是QPixmap、Image等 p.drawImage(0,0,QImage("../image/cute2.png")); //操作像素点 for(int i=0;i<50;i++) { for(int j=0;j<50;j++) { image.setPixel(i,j,qRgb(255,128,128)); //获取像素点,暂时用不上 //image.pixel(QPoint(i,j)); } } p.end(); //保存图片 image.save("../image.png"); }
③QPicture:
#include "widget.h" #include "ui_widget.h" #include<QPicture> #include<QPainter> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //绘图设备 QPicture picture; //画家 QPainter p; p.begin(&picture); //绘图 p.drawPixmap(0,0,80,80,QPixmap("../image/cute2.png")); p.drawLine(50,50,150,50); p.end(); //保存的是图片状态,二进制文件 picture.save("../picture.png"); } Widget::~Widget() { delete ui; } //将图片状态加载到窗口 //在窗口上绘图,重写绘图事件 void Widget::paintEvent(QPaintEvent *event) { QPicture pic; pic.load("../picture.png");//加载文件 QPainter p(this); p.drawPicture(0,0,pic); }
④QImage和QPixmap的相互转换:
QPicture,保存后加载就可以直接绘图;但是在实际运用中,QImage和QPixmap常常需要相互转换,QImage与平台无关,因此,传输过程用的是QImage;而实际绘画过程使用QPixmap,因为它对屏幕进行了优化
void Widget::paintEvent(QPaintEvent *event) { QPainter p(this); QPixmap pixmap; pixmap.load("../image/cute2.png"); //QPixmap->QImage QImage tempImage=pixmap.toImage(); p.drawImage(0,0,tempImage); QImage image; image.load("../image/cute2.png"); //QImage->QPixmap QPixmap tempPix=QPixmap::fromImage(image); p.drawPixmap(400,0,tempPix); }
4、不规则窗口
将图片换成透明背景的工具(若不会使用PS):https://www.gaoding.com/koutu
需求:将一张背景为透明的图片显示到窗口,且去除窗口的边框(也去除了关闭按钮等),实现右击鼠标关闭窗口,长按鼠标左键并移动实现拖拽窗口
widget.h
protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *) override; private: Ui::Widget *ui; QPoint p;
widget.cpp
#include "widget.h" #include "ui_widget.h" #include<QPainter> #include<QMouseEvent> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //去窗口边框| windowFlags(),保留窗口特性的同时去除窗口边框 setWindowFlags(Qt::FramelessWindowHint | windowFlags()); //把窗口背景设置为透明 setAttribute(Qt::WA_TranslucentBackground); } Widget::~Widget() { delete ui; } void Widget::paintEvent(QPaintEvent *event) { QPainter p(this); p.drawPixmap(0,0,QPixmap("../image/乔巴_hyaline.png")); } //窗口去边框,没法关闭窗口和移动窗口,因此需要重写鼠标移动事件 void Widget::mouseMoveEvent(QMouseEvent *e) { //移动,是buttons!!! if(e->buttons() & Qt::LeftButton) { this->move(e->globalPos()-p); } } void Widget::mousePressEvent(QMouseEvent *e) { if(e->button()==Qt::RightButton) { //点击右键,关闭窗口 close(); } if(e->button()==Qt::LeftButton) { //求坐标差值 //当前点击坐标-窗口左上角坐标 p=e->globalPos() - this->frameGeometry().topLeft();//包括边框的窗口的左上角 //this->geometry();//获取不包括边框的主工作区的矩形 } }
5、文件系统
- QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;
- QFileDevice:Qt5新增加的类,提供了有关文件操作的通用实现;
- QFlie:访问本地文件或者嵌入资源;
- QTemporaryFile:创建和访问本地文件系统的临时文件;
- QBuffer:读写QbyteArray, 内存文件;
- QProcess:运行外部程序,处理进程间通讯;
- QAbstractSocket:所有套接字类的父类;
- QTcpSocket:TCP协议网络数据传输;
- QUdpSocket:传输 UDP 报文;
-
QSslSocket:使用 SSL/TLS 传输数据
文件系统分类:
顺序访问设备:
是指它们的数据只能访问一遍:从头走到尾,从第一个字节开始访问,直到最后一个字节,中途不能返回去读取上一个字节,这其中,QProcess、QTcpSocket、QUdpSoctet和QSslSocket是顺序访问设备。
随机访问设备:
可以访问任意位置任意次数,还可以使用QIODevice::seek()函数来重新定位文件访问位置指针,QFile、QTemporaryFile和QBuffer是随机访问设备
1)字符编码转换
//打开文件
//QString path=QFileDialog::getOpenFileName(this,"open","../","TXT(*.txt)");//指定文件格式,也可以不指定(所有格式)
//保存文件
QString path=QFileDialog::getSaveFileName(this,"save","../","TXT(*.txt)");
//创建文件对象
QFile file;
file.setFileName(path);//QFile file(path);
//获取编辑区内容
QString str=ui->textEdit->toPlainText();
①QString -> QByteArray
file.write(str.toUtf8());
②QString -> C++ string ->char*
file.write(str.toStdString().data());
③转换成本地平台编码(Windows:ANSI)
file.write(str.toLocal8Bit());
④QString -> QByteArray
QString buf="123";
QByteArray a=buf.toUtf8();//UTF8主要是针对有中文字符
a=buf.toLocal8Bit();//本地编码,上下两种转换方式返回值都是QByteArray
⑤QByteArray -> char*
char * b=a.data();
⑥char * -> QString 网络编程会常常用到
char *p="abc";
QString c=QString(p);
2)QFile读写文件及获取文件信息
需求:通过readButton打开文件,将内容显示到文本框;通过writeButton将文本框内的内容存进文件
#include "widget.h" #include "ui_widget.h" #include<QFile> #include<QFileDialog> #include<QFileInfo>//获取文件信息 #include<QDebug> #include<QDateTime> void Widget::on_ButtonRead_clicked() { //获取文件路径 QString path=QFileDialog::getOpenFileName(this, "open","../","TXT(*.txt)");//指定文件格式,也可以不指定(所有格式) if(path.isEmpty()==false) { //文件对象 QFile file(path); //打开文件,只读方式 bool isOk=file.open(QIODevice::ReadOnly); if(isOk==true) { #if 0 //读文件,两种方法,默认只识别utf_8编码,要读取其他编码需借助文本流 //1)一次读完,不靠谱,文件很大时 QByteArray array=file.readAll(); //显示到编辑区 //ui->textEdit->setText(QString(array)); ui->textEdit->setText(array);//会自动转换成QSreing,QString的构造函数有一种是字节数组转换的 #endif //2)一行行读取 QByteArray array; while(file.atEnd()==false) { //读一行 array+=file.readLine(); } ui->textEdit->setText(array); } //关闭文件 file.close(); //获取文件信息 QFileInfo info(path); qDebug()<<"文件名字:"<<info.fileName().toUtf8().data();//转成char* qDebug()<<"文件后缀:"<<info.suffix(); qDebug()<<"文件大小:"<<info.size();//单位为字节,*1024=KB qDebug()<<"文创建时间:"<<info.birthTime().toString("yyyy-MM-dd hh:mm:ss");//2020-4-5 13:13:00 } } void Widget::on_ButtonWrite_clicked() { QString path=QFileDialog::getSaveFileName(this,"save","../","TXT(*.txt)"); if(path.isEmpty()==false) { //创建文件对象 QFile file; file.setFileName(path); //打开文件 bool isOk=file.open(QIODevice::WriteOnly); if(isOk==true) { //获取编辑区内容 QString str=ui->textEdit->toPlainText(); //写文件 //①QString -> QByteArray //file.write(str.toUtf8()); //②QString -> C++ string ->char* //file.write(str.toStdString().data()); //③转换成本地平台编码(Windows:ANSI) file.write(str.toLocal8Bit()); //QString -> QByteArray QString buf="123"; QByteArray a=buf.toUtf8();//UTF8主要是针对有中文字符 a=buf.toLocal8Bit();//本地编码,上下两种转换方式返回值都是QByteArray //QByteArray -> char* char * b=a.data(); //char * -> QString 网络编程会常常用到 char *p="abc"; QString c=QString(p); } file.close(); } }
3)QDataStream(二进制)
#include "widget.h" #include "ui_widget.h" #include<QDataStream>//数据流,以二进制方式操作,很灵活,文件,音频等都可以操作 #include<QFile>//打开文件、关联路径 #include<QDebug> //__FILE__哪个文件输出打印;__LINE__文件代码的行号;C语言的全局宏 #define cout qDebug() << "[" << __FILE__ << ":" << __LINE__<<"]" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); writeData(); readData(); } Widget::~Widget() { delete ui; } void Widget::writeData() { //创建文件对象 QFile file("../test.txt"); //打开文件,只写 bool isOk=file.open(QIODevice::WriteOnly); if(isOk==true) { //创建数据流,和file文件关联 //往数据流中写数据,相当于往文件里写数据 QDataStream stream(&file); stream<<QString("啦啦啦啦啦")<<250; file.close(); } } void Widget::readData() { //创建文件对象 QFile file("../test.txt"); //打开文件,只读 bool isOk=file.open(QIODevice::ReadOnly); if(isOk==true) { //文件流 //往文件流中读数据,相当于从文件里读数据 QDataStream stream(&file); //读的时候,按写的顺序读取 QString str; int a; stream>>str>>a; //qDebug()<<str.toUtf8().data()<<a; //qDebug()<<str<<a; cout<<str.toUtf8().data()<<a; file.close(); } }
//__FILE__哪个文件输出打印;__LINE__文件代码的行号;C语言的全局宏,好用
#define cout qDebug() << "[" << __FILE__ << ":" << __LINE__<<"]"
cout<<str.toUtf8().data()<<a;
4)QTextStream
#include "widget.h" #include "ui_widget.h" #include<QString> #include<QFile> #include<QTextStream>//文本流,读写时可指定编码 #include<QFileDialog> #include<QDebug> #define cout qDebug()<<"["<<__FILE__<<":"<<__LINE__<<"]" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); writeData(); readData(); } Widget::~Widget() { delete ui; } void Widget::writeData() { //创建文本对象 QFile file; file.setFileName("../TextStream.txt"); //打开文件,只写 bool isOk=file.open(QIODevice::WriteOnly); if(true==isOk) { QTextStream stream(&file); //指定编码,不指定时生成编码与平台相关 stream.setCodec("UTF-8"); stream<<QString("啦啦啦啦啦")<<250; file.close(); } } void Widget::readData() { //创建文件对象 QFile file("../TextStream.txt"); bool isOk=file.open(QIODevice::ReadOnly); //读文件不推荐,有bug // if(true==isOk) // { // QTextStream stream(&file); // //指定编码 // stream.setCodec("UTF-8");//与所读文件编码匹配 // QString str; // int a; // //读取时把“啦啦啦啦啦250”赋值给str,中间没空格,a没有内容故赋值为0 // stream>>str>>a; // cout<<str.toUtf8().data()<<a; // file.close(); // } } void Widget::on_pushButton_clicked() { QString path=QFileDialog::getOpenFileName(this,"open","../"); if(false==path.isEmpty()) { QFile file; file.setFileName(path); bool isOk=file.open(QIODevice::ReadOnly); if(true==isOk) { QTextStream stream(&file); //指定编码 stream.setCodec("UTF-8"); QString str=stream.readAll(); ui->textEdit->setText(str); file.close(); } } }
void Widget::readData()运行结果:
5)QBuffer(内存文件)
#include "widget.h" #include "ui_widget.h" #include<QBuffer>//内存文件 #include<QDebug> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //QBuffer memFile;//创建内存文件 QByteArray array; QBuffer memFile(&array);//指定内存数组,将数据放入 memFile.open(QIODevice::WriteOnly); memFile.write("1111111111111111"); memFile.write("22222222222222222222222222"); memFile.close(); qDebug()<<memFile.buffer(); qDebug()<<"array"<<array; QBuffer memFile1; memFile1.open(QIODevice::WriteOnly); QDataStream stream(&memFile1); stream<<QString("测试")<<250; memFile1.close(); // qDebug()<<memFile1.buffer();//转换麻烦,且内容整在一起 memFile1.open(QIODevice::ReadOnly); QDataStream in; in.setDevice(&memFile1);//缓冲区内容也是二进制的 QString str; int a; in>>str>>a; memFile1.close(); qDebug()<<str.toUtf8().data()<<a; } Widget::~Widget() { delete ui; }