QT的绘图系统(QPainterDevice)与文件系统(QIODevice)
文章目录
- 1、Qt 的绘图系统
-
- 1、QPainter的使用
- 2、QPen(画笔)及QBursh(画刷)
- 3、手动更新窗口
- 4、绘图设备
-
-
- 1、四种绘图设备的 区别
- 2、 QBitmap
- 3、QPixmap
- 4、 QImage
- 5、QPicture
- 6、QImage与QPixmap的转换
-
- 2、不规则窗口
- 3、文件系统
-
- 1、QFile文件的读写
- 2、QT的QString与QByteArray以及char *的相互转换
- 3、文件信息QFileInfo类
- 4、二进制文件读写 QDataStream类(打开是看不懂)
- 5、文本文件读写QTextStream类
- 6、内存读写QBuffer类
- 4、总结
1、Qt 的绘图系统
Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于QPainter,QPainterDevice和QPaintEngine三个类。
下图给出了这三个类之间的层次结构:
上面的示意图告诉我们,Qt 的绘图系统实际上是,使用QPainter在QPainterDevice上进行绘制,它们之间使用QPaintEngine进行通讯(也就是翻译QPainter的指令)。
1、QPainter的使用
对窗口绘图首先要重写**void paintEvent(QPaintEvent *)**虚函数。
- 重写绘图事件,虚函数
- 如果在窗口绘图,必须放在绘图事件里实现
- 绘图事件内部自动调用,窗口需要重绘的时候(状态改变)
void MyWidget::paintEvent(QPaintEvent *) { QPainter p(this);//指定当前窗口为绘图设备 p.drawPixmap(rect(), QPixmap("../Image/bk.jpg")); //画背景图 //画直线 p.drawLine(50, 50, 150, 50); /* 在坐标50,50的位置画长150,宽50的线 */ p.drawLine(50, 50, 50, 150); //画矩形;l;d p.drawRect(150, 150, 100, 50); //画圆形 p.drawEllipse(QPoint(150, 150), 50, 25); }
注意:在上层目录要有背景图的存在。要先画背景图在画直线,矩形,圆形。
指定QQidget对象为绘图对象。即窗口是绘图设备。QQidget继承与QPaintDevice.
2、QPen(画笔)及QBursh(画刷)
QPainter有很多以 draw 开头的函数,用于各种图形的绘制,比如drawLine(),drawRect()以及drawEllipse()等。
如果需要设置画家的颜色,风格,线条的粗细。从而引入画笔。如果需要填充图形的颜色需要画刷。
void MyWidget::paintEvent(QPaintEvent *) { QPainter p(this);//指定当前窗口为绘图设备 //画背景图 p.drawPixmap(rect(), QPixmap("../Image/bk.jpg")); //定义画笔 QPen pen; pen.setWidth(5); //设置线宽 pen.setColor( QColor(14, 9, 234) );//rgb设置颜色 pen.setStyle(Qt::DashLine); //设置风格 p.setPen(pen); //把画笔交给画家 QBrush brush; //创建画刷对象 brush.setColor(Qt::red); //设置颜色 brush.setStyle(Qt::Dense1Pattern);//设置样式 p.setBrush(brush);//把画刷交给画家 //画直线 p.drawLine(50, 50, 150, 50); /* 在坐标50,50的位置画长150,宽50的线 */ p.drawLine(50, 50, 50, 150); //画矩形;l;d p.drawRect(200, 200, 100, 50); //画圆形 p.drawEllipse(QPoint(150, 150), 50, 25); }
3、手动更新窗口
当我们在窗口绘制的图不断变化时。需要不断地刷新窗口才会有变化。否则不会有。
mywidget.h
class MyWidget : public QWidget { Q_OBJECT public: MyWidget(QWidget *parent = nullptr); ~MyWidget(); void paintEvent(QPaintEvent *); private: int x; QPushButton *button; private slots: void FunSlot(void); private: Ui::MyWidget *ui; };
mywidget.cpp
#include "mywidget.h" #include "ui_mywidget.h" #include <QPainter> MyWidget::MyWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MyWidget) { ui->setupUi(this); x = 0; QPushButton *button = new QPushButton(this); button->setText("向右移动"); connect(button, &QPushButton::pressed, this, &MyWidget::FunSlot); } MyWidget::~MyWidget() { delete ui; } void MyWidget::paintEvent(QPaintEvent *) { QPainter p(this); //画笑脸 p.drawPixmap(x, 180, 80, 80, QPixmap("../Image/face.png")); } void MyWidget::FunSlot(void) { x += 20; if(x > width()- 80) { x = 0; } //刷新窗口,让窗口重绘,整个窗口都刷新 update(); //间接调用paintEvent() }
点击按钮绘图不断向右移动变化。到窗口终点又回到起点。
4、绘图设备
1、四种绘图设备的 区别
绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和 QPicture。
**QBitmap:**是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
总结:它们操作都方法都是差不多一样的。
2、 QBitmap
QBitmap只有黑白两种颜色。
void MyWidget::paintEvent(QPaintEvent *) { QPainter p(this); //QPixmap 图片背景透明 p.drawPixmap(0, 0, QPixmap("../Image/face.png")); //QBitmap 图片背景透明 p.drawPixmap(200, 0, QBitmap("../Image/face.png")); //QPixmap 图片背景白色 QPixmap pixmap; pixmap.load("../Image/image.jpg"); p.drawPixmap(0, 200, pixmap); //QBitmap 图片背景白色 QBitmap bitmap; bitmap.load("../Image/image.jpg"); p.drawPixmap(400, 200, bitmap); }
右边为QBitmap 左边为QPixmap。上面的QBitmap为黑色背景。下面为QBitmap的白色背景。
3、QPixmap
如下在QPixmap绘图而不是在窗口。不用重写绘图事件。在构造函数里实现即可。
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MyWidget) { 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/face.png")); //上层路径保存图片 pixmap.save("../pixmap.jpg"); }
在上层目录生成pixmap.jpg文件。
4、 QImage
QImage可以对像素点进行操作。
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //创建一个绘图设备,QImage::Format_ARGB32背景是透明 QImage image(400, 300, QImage::Format_ARGB32); QPainter p; p.begin(&image); //绘图 p.drawImage(0, 0, QImage("../Image/image.jpg")); //对绘图设备前50个像素点进行操作 for(int i = 0; i < 50; i++) { for(int j = 0; j < 50; j++) { image.setPixel(QPoint(i, j), qRgb(0, 255, 0)); /* 设置像素点 */ //image.pixel(QPoint(i, j)); /* 获取像素点 */ } } p.end(); image.save("../image.png"); // 保存图片 }
在上层目录生成image.png文件。绿色部分为对像素点的操作。
5、QPicture
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MyWidget) { ui->setupUi(this); QPicture picture; QPainter p; p.begin(&picture); p.drawPixmap(0, 0, 80, 80, QPixmap("../Image/face.png")); p.drawLine(50, 50, 150, 50); p.end(); //保存的是二进制文件 picture.save("../picture.png"); }
在上层路径生成picture.png的二进制文件。
二进制打开也看不出效果。我们在窗口里加载出来。重写绘图事件。
void MyWidget::paintEvent(QPaintEvent *) { QPicture pic; pic.load("../picture.png"); //加载二进制picture.png文件 QPainter p(this); p.drawPicture(0, 0, pic); }
运行结果如下:
6、QImage与QPixmap的转换
在对屏幕进行优化可能需要用到QPixmap。在传输可能需要用到QImage。
void MyWidget::paintEvent(QPaintEvent *) { QPainter p(this); QPixmap pixmap; pixmap.load("../Image/face.png"); //QPixmap -> QImage QImage tempImage = pixmap.toImage(); p.drawImage(0, 0, tempImage); QImage image; image.load("../Image/face.png"); //QImage -> QPixmap QPixmap tempPixmap = QPixmap::fromImage(image); p.drawPixmap(200, 0, tempPixmap); }
运行结果:
第一张是通过QPixmap转QImage画的。第二张是通过QImage转QPixmap画的。
2、不规则窗口
常见的窗体是各种方形的对话框,但有时候也需要非方形的窗体,如圆形,椭圆甚至是不规则形状的对话框。
widget.h
class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); protected: void paintEvent(QPaintEvent *); void mouseMoveEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); private: QPoint p; private: Ui::Widget *ui; };
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); //去窗口表框Qt::FramelessWindowHint windowFlags()其它属性不变 setWindowFlags(Qt::FramelessWindowHint | windowFlags()); //把窗口背景设置为透明 setAttribute(Qt::WA_TranslucentBackground); } Widget::~Widget() { delete ui; } void Widget::paintEvent(QPaintEvent *) { QPainter p(this); p.drawPixmap(0, 0, QPixmap("../Image/sunny.png")); } void Widget::mousePressEvent(QMouseEvent *e) { if(e->button() == Qt::RightButton) { //如果是右键 close(); } else if(e->button() == Qt::LeftButton) { //求坐标差值 //当前点击坐标(屏幕)-窗口左上角坐标 p = e->globalPos() - this->frameGeometry().topLeft(); } } void Widget::mouseMoveEvent(QMouseEvent *e) { /* 移动加鼠标左键按下 */ if(e->buttons() & Qt::LeftButton) { move(e->globalPos() - p); // 当前点击坐标(屏幕) - 求坐标差值 } }
当鼠标左键按下移动无规则窗口跟着移动。鼠标右键按下则无规则窗口关闭。
如下图片关于上面代码的坐标计算的示意图
3、文件系统
文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。下面是 I/O 设备的类图(Qt5):
1、QFile文件的读写
QFile提供了从文件中读取和写入数据的能力。
我们通常会将文件路径作为参数传给QFile的构造函数。不过也可以在创建好对象最后,使用setFileName()来修改。
ui界面如下:
功能:把读取到文件显示在文本框里。把文本框里的内容保存成文件。
#include "mywidget.h" #include "ui_mywidget.h" #include<QFile> #include<QFileDialog> #include<QDebug> MyWidget::MyWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MyWidget) { ui->setupUi(this); connect(ui->pushButtonRead, &QPushButton::pressed, this, &MyWidget::ReadSlot); connect(ui->pushButtonWrite, &QPushButton::pressed, this, &MyWidget::WriteSlot); } MyWidget::~MyWidget() { delete ui; } void MyWidget::ReadSlot(void) { 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 //读文件,默认只识别utf8编码 在文件流才可以改变编码格式 QByteArray array = file.readAll(); //显示到编辑区 //ui->textEdit->setText(QString(array)); //ui->textEdit->setText(array); #endif // 一次读取一行 QByteArray array; while( file.atEnd() == false) { //读一行 array += file.readLine(); } ui->textEdit->setText(array); } //关闭文件 file.close(); } } void MyWidget::WriteSlot(void) { 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(); //写文件 file.write(str.toUtf8()); } file.close(); } }
2、QT的QString与QByteArray以及char *的相互转换
// QString -> QByteArray file.write(str.toUtf8()); //QString -> c++ string -> char * file.write(str.toStdString().data()); //转换为本地平台编码 file.write(str.toLocal8Bit()); //QString -> QByteArray QString buf = "123"; QByteArray a = buf.toUtf8(); //中文 a = buf.toLocal8Bit(); //本地编码 //QByteArray -> char * char *b = a.data(); //char * -> QString char *p = "abc"; QString c = QString(p);
3、文件信息QFileInfo类
#include <QFileDialog> #include <QFileInfo> #include <QDebug> #include <QDateTime> QString path = QFileDialog::getOpenFileName(this, "open", "../"); if(path.isEmpty() == false) { //获取文件信息 QFileInfo info(path); qDebug() << "文件名字:" << info.fileName().toUtf8().data(); qDebug() << "文件后缀:" << info.suffix(); qDebug() << "文件大小:" << info.size(); qDebug() << "文件创建时间:" <<info.birthTime().toString("yyyy-MM-dd hh:mm:ss"); //2024-01-04 15:13:00 }
4、二进制文件读写 QDataStream类(打开是看不懂)
mywidget.h
#include <QWidget> QT_BEGIN_NAMESPACE namespace Ui { class MyWidget; } QT_END_NAMESPACE class MyWidget : public QWidget { Q_OBJECT public: MyWidget(QWidget *parent = nullptr); ~MyWidget(); void writeData(); void readData(); private: Ui::MyWidget *ui; };
mywidget.cpp
#include "mywidget.h" #include "ui_mywidget.h" #include <QDataStream> #include <QFile> #include <QDebug> #define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]" MyWidget::MyWidget(QWidget *parent) : QWidget(parent) , ui(new Ui::MyWidget) { ui->setupUi(this); writeData(); readData(); } MyWidget::~MyWidget() { delete ui; } void MyWidget::writeData() { //创建文件对象 QFile file("../hello.txt"); //打开文件, 只写方式打开 bool isOk = file.open(QIODevice::WriteOnly); if(true == isOk) { //创建数据流,和file文件关联 //往数据流中写数据,相当于往文件里写数据 QDataStream stream(&file); stream << QString("你好") << 777; file.close(); } } void MyWidget::readData() { //创建文件对象 QFile file("../hello.txt"); //打开文件, 只读方式打开 bool isOk = file.open(QIODevice::ReadOnly); if(true == isOk) { //创建数据流,和file文件关联 //往数据流中读数据,相当于往文件里读数据 QDataStream stream(&file); //读的时候,按写的顺序取数据 QString str; int a; stream >> str >> a; //qDebug() << str.toUtf8().data() << a; cout << str.toUtf8().data() << a; file.close(); } }
先写入数据流(二进制形式),然后以写的顺序读出来。
需要注意的是,你必须按照写入的顺序,将数据读取出来。顺序颠倒的话,程序行为是不确定的,严重时会直接造成程序崩溃。
那么,既然QIODevice提供了read()、readLine()之类的函数,为什么还要有QDataStream呢?QDataStream同QIODevice有什么区别?区别在于,QDataStream提供流的形式,性能上一般比直接调用原始 API 更好一些。
5、文本文件读写QTextStream类
ui界面:
widget.h
#include <QWidget> namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); void writeData(); private slots: void on_pushButton_clicked(); private: Ui::Widget *ui; };
widget.cpp
#include "widget.h" #include "ui_widget.h" #include <QTextStream> #include <QFile> #include <QDebug> #include <QFileDialog> #define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); writeData(); } 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.setEncoding(QStringConverter::Utf8); stream << QString("你好") << 777; 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.setEncoding(QStringConverter::Utf8); QString str = stream.readAll(); ui->textEdit->setText(str); file.close(); } } }
当使用QDataStream写入的时候,实际上会在要写入的内容前面,额外添加一个这段内容的长度值。而以文本形式写入数据,是没有数据之间的分隔的。因此,使用文本文件时,很少会将其分割开来读取,而是使用诸如使用:
QTextStream::readLine() 读取一行
QTextStream::readAll()读取所有文本。
6、内存读写QBuffer类
QByteArray array; QBuffer memFile(&array); //创建内存文件 还指定QByteArray对象 memFile.open(QIODevice::WriteOnly); memFile.write("5455454"); memFile.write("6564565"); memFile.close(); qDebug() << memFile.buffer(); qDebug() << "array:" << array;
写进了内存也保存在了指定的QByteArray对象里。
QBuffer与QDataStream一起操作dome。
QBuffer memFile1; memFile1.open(QIODevice::WriteOnly); /* 只写 */ QDataStream stream(&memFile1); /* QBuffer与QDataStream关联 */ stream << QString("测试") << 999; 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; qDebug() << memFile1.buffer();
运行结果:
4、总结
例如:以上就是今天要讲的内容,本文仅仅简单介绍了QT的绘图系统与文件系统。