【Qt】桌面应用开发 ------ 绘图事件和绘图设备 文件操作

文章目录

  • 9、绘图事件和绘图设备
    • 9.1 QPainter
    • 9.2 手动触发绘图事件
    • 9.3 绘图设备
      • 9.3.1 QPixmap
      • 9.3.2 QImage
      • 9.3.3 QImage与QPixmap的区别
      • 9.3.4 QPicture
  • 10、文件操作
    • 10.1 文件读写
    • 10.2 二进制文件读写
    • 10.3 文本文件读写
    • 10.4 综合案例

9、绘图事件和绘图设备

什么时候画?

02_paintEvent

在显示到屏幕的前一刻画出来,绘图事件可以做到

绘图:窗口需要重新显示的时候,就会收到一个绘图事件 paintEvent,我们先体验一个触发绘图事件简单的代码:

void Mypushbutton::paintEvent(QPaintEvent *ev)
{static int num = 1;qDebug()<<num++;QPushButton::paintEvent(ev);//画出自己
}
动画28

我们每次改动后或者移动都会触发绘图事件,收到绘图事件之后,窗口就要将自己画出来;那我们知道了什么时候画,那应该怎么画:

首先需要画画的人QPainter、画笔QPen、画布QPaintDevice

9.1 QPainter

我们写学基本的绘图:

​ QPainter ( 参数是绘图设备 ,this表示在窗口上绘图)
​ drawLine 划线 , 两点成一线,参数就是两个点的坐标
​ drawRect 矩形 , 参数是 左上角的点和 宽和高
​ drawEllips 画椭圆 , 参数是 左上角的点和 宽和高 ,另一种方式就是 圆心 + rx + ry

​ 设置画家的画笔
​ setPen ( QPen) ,画笔可以设置颜色和风格
​ 设置画家的画刷
​ setBrush(QBrush) , 填充封闭的图形, 也可以设置颜色 ,默认情况下,画刷不填充,还得设置风格
​ 搬动画家
​ translate ( x, y) 将画家移动到某个坐标开始画画

void paintEvent(QPaintEvent *event) ;
#include<QPainter>
//在这里画画QPainter painter(this);//以当前窗口作为图片设备//画家偏移,搬动画家到某个坐标上开始画画painter.translate(100,0);//创建一支画笔QPen pen;pen.setColor(QColor(255,0,0));//设置笔宽pen.setWidth(3);//设置画笔的风格pen.setStyle(Qt::DashLine);//画家设置画笔painter.setPen(pen);//需要填充,使用画刷QBrush brush;brush.setColor(Qt::cyan);//默认情况下,画刷不填充,还得设置风格brush.setStyle(Qt::Dense4Pattern);//画家设置画刷painter.setBrush(brush);//画一条线painter.drawLine(0,0,100,100);//画矩形painter.drawRect(20,20,50,50);//画圆形,使用椭圆painter.drawEllipse(QPoint(100,100),50,50);//画文字painter.drawText(200,100,"好好学习,天天向上");
image-20221101093955405

我们可以联想现实生活中画画,如果需要更改颜色就要挑选颜色,并重新设置拾取使用;

9.2 手动触发绘图事件

我们用painter绘制已经从存在的图片,需要添加图片资源,

QPainter painter(this);QPixmap pixmap(":/image/Sunny.jpg");//图片路径painter.drawPixmap(0,0,pixmap);//坐标
image-20221101102007340

我们继续添加一些功能,每次点击一个按键移动一下位置:

需要添加一个按键,并转到槽,在头文件中定义一个mPox变量,并在源文件中初始化为0;设置按键点击事件,按一次,移动10像素

void Widget::on_pushButton_clicked()
{//每点击而一次,向右移动图片,10个像素mPox += 10;
}
动画29

我们发现我们点击按键没有直接移动图片,而是最小化之后在打开,图片才移动了位置;所以我们需要设置手动触发事件

有两种方法:repaint()update();它们的区别是:repaint会马上触发,在函数中调用了多次repaint,会收到很多次绘图事件;

update做了优化,多次调用update只会触发一次,通常我们用update

注意:不要在paintEvent处理函数中再触发绘图事件,会导致无限循环

9.3 绘图设备

绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmapQBitmapQImageQPicture。其中,

  • QPixmap专门为图像在屏幕上的显示做了优化
  • QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
  • QImage专门为图像的像素级访问做了优化。
  • QPicture则可以记录和重现QPainter的各条命令。

9.3.1 QPixmap

QPixmap继承了QPaintDevice,因此,你可以使用QPainter直接在上面绘制图形。QPixmap也可以接受一个字符串作为一个文件的路径来显示这个文件,比如你想在程序之中打开png、jpeg之类的文件,就可以使用 QPixmap。使用QPainter的drawPixmap()函数可以把这个文件绘制到一个QLabel、QPushButton或者其他的设备上面。QPixmap是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。**注意,这里说的显示设备并不是硬件,而是操作系统提供的原生的绘图引擎。所以,在不同的操作系统平台下,QPixmap的显示可能会有所差别。

#include<QPainter>
#include<QPixmap>QPixmap pix(300,300);//图片宽高QPainter painter(&pix);//以图片作为绘图设备painter.drawEllipse(QPoint(150,150),100,100);//坐标,半径pix.save("D:\\qt\\code\\painterDevice\\pix.png");//保存的位置

我们的关注点不是在运行界面上,我们打开文件后,看到多了一个图片,黑乎乎的,默认是填充黑色颜色,我们设置一下

image-20221101104825209

对比:设置为白色,形状为红色

image-20221101105813915

9.3.2 QImage

QPixmap使用底层平台的绘制系统进行绘制,无法提供像素级别的操作,而QImage则是使用独立于硬件的绘制系统,实际上是自己绘制自己,因此提供了像素级别的操作,并且能够在不同系统之上提供一个一致的显示形式。

我们声明了一个QImage对象,大小是300 x 300,颜色模式是RGB32,即使用32位数值表示一个颜色的RGB值,也就是说每种颜色使用8位。然后我们对每个像素进行颜色赋值,从而构成了这个图像。我们可以把QImage想象成一个RGB颜色的二维数组,记录了每一像素的颜色。

我们先显示一张图片,然后再改它的像素

image-20221101112646596
  //修改像素for(int x = 50;x<100;x++){for(int y = 50;y<100;y++){img.setPixelColor(x,y,QColor(255,0,0));}}
image-20221101113141504

9.3.3 QImage与QPixmap的区别

QPixmap主要是用于绘图,针对屏幕显示而最佳化设计,QImage主要是为图像I/O、图片访问和像素修改而设计的

QPixmap依赖于所在的平台的绘图引擎,故例如反锯齿等一些效果在不同的平台上可能会有不同的显示效果,QImage使用Qt自身的绘图引擎,可在不同平台上具有相同的显示效果

由于QImage是独立于硬件的,也是一种QPaintDevice,因此我们可以在另一个线程中对其进行绘制,而不需要在GUI线程中处理,使用这一方式可以很大幅度提高UI响应速度。

QImage可通过setPixpel()和pixel()等方法直接存取指定的像素。

QImage与QPixmap之间的转换:

QImage转QPixmap

使用QPixmap的静态成员函数: fromImage()

QPixmap fromImage(const QImage & image,   Qt::ImageConversionFlags flags = Qt::AutoColor);

QPixmap转QImage:

使用QPixmap类的成员函数: toImage()

QImage toImage() const;

9.3.4 QPicture

这是一个可以记录和重现QPainter命令的绘图设备。 QPicture将QPainter的命令序列化到一个IO设备,保存为一个平台独立的文件格式。这种格式有时候会是“元文件(meta- files)”。Qt的这种格式是二进制的,不同于某些本地的元文件,Qt的pictures文件没有内容上的限制,只要是能够被QPainter绘制的元素,不论是字体还是pixmap,或者是变换,都可以保存进一个picture中。

QPicture是平台无关的,因此它可以使用在多种设备之上,比如svg、pdf、ps、打印机或者屏幕。回忆下我们这里所说的QPaintDevice,实际上是说可以有QPainter绘制的对象。QPicture使用系统的分辨率,并且可以调整 QPainter来消除不同设备之间的显示差异。

如果我们要记录下QPainter的命令,首先要使用QPainter::begin()函数,将QPicture实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用QPainter::end()命令终止。代码示例如下:

#include<QPicture>QPicture pic;QPainter painter;//将图像绘制到QPicture中,并保存到文件painter.begin(&pic);painter.drawEllipse(20, 20, 100, 50);painter.fillRect(20, 100, 100, 100, Qt::red);painter.end();pic.save("D:\\drawing.pic");//将保存的绘图动作重新绘制到设备上pic.load("D:\\drawing.pic");painter.begin(this);painter.drawPicture(200, 200, pic);painter.end();

10、文件操作

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。下面是 I/O 设备的类图(Qt5):

img
  • 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是随机访问设备,

10.1 文件读写

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。在所有的 I/O 设备中,文件 I/O 是最重要的部分之一。因为我们大多数的程序依旧需要首先访问本地文件(当然,在云计算大行其道的将来,这一观点可能改变)。QFile提供了从文件中读取和写入数据的能力。

我们通常会将文件路径作为参数传给QFile的构造函数。不过也可以在创建好对象最后,使用setFileName()来修改。QFile需要使用 / 作为文件分隔符,不过,它会自动将其转换成操作系统所需要的形式。例如 C:/windows 这样的路径在 Windows 平台下同样是可以的。

QFile主要提供了有关文件的各种操作,比如打开文件、关闭文件、刷新文件等。我们可以使用QDataStream或QTextStream类来读写文件,也可以使用QIODevice类提供的read()、readLine()、readAll()以及write()这样的函数。值得注意的是,有关文件本身的信息,比如文件名、文件所在目录的名字等,则是通过QFileInfo获取,而不是自己分析文件路径字符串。

下面我们使用一段代码来看看QFile的有关操作:

int main(int argc, char *argv[])
{QApplication app(argc, argv);QFile file("in.txt");if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {qDebug() << "Open file failed.";return -1;} else {while (!file.atEnd()) {qDebug() << file.readLine();}}QFileInfo info(file);qDebug() << info.isDir();qDebug() << info.isExecutable();qDebug() << info.baseName();qDebug() << info.completeBaseName();qDebug() << info.suffix();qDebug() << info.completeSuffix();return app.exec();
}
  • 我们首先使用QFile创建了一个文件对象。

这个文件名字是 in.txt。如果你不知道应该把它放在哪里,可以使用QDir::currentPath()来获得应用程序执行时的当前路径。只要将这个文件放在与当前路径一致的目录下即可。

  • 使用open()函数打开这个文件,打开形式是只读方式,文本格式。

这个类似于fopen()的 r 这样的参数。open()函数返回一个 bool 类型,如果打开失败,我们在控制台输出一段提示然后程序退出。否则,我们利用 while 循环,将每一行读到的内容输出。

  • 可以使用QFileInfo获取有关该文件的信息。

QFileInfo有很多类型的函数,我们只举出一些例子。比如:

  • isDir() 检查该文件是否是目录(在操作系统这种,文件夹是一种特殊的文件,我们可以通过QFile来打开一个文件夹或者路径,这个函数返回值就是判断他是不是一个文件夹或路径);

  • isExecutable() 检查该文件是否可以被执行。

  • baseName() 可以直接获得文件名;

  • completeBaseName() 获取完整的文件名

  • suffix() 则直接获取文件后缀名。

  • completeSuffix() 获取完整的文件后缀

我们可以由下面的示例看到,baseName()和completeBaseName(),以及suffix()和completeSuffix()的区别:

QFileInfo fi("/tmp/archive.tar.gz");
QString base  = fi.baseName();  // base = "archive"
QString base  = fi.completeBaseName();  // base = "archive.tar"
QString ext   = fi.suffix();  // ext = "gz"
QString ext   = fi.completeSuffix();  // ext = "tar.gz"

10.2 二进制文件读写

QDataStream提供了基于QIODevice的二进制数据的序列化。数据流是一种二进制流,这种流完全不依赖于底层操作系统、CPU 或者字节顺序(大端或小端)。例如,在安装了 Windows 平台的 PC 上面写入的一个数据流,可以不经过任何处理,直接拿到运行了 Solaris 的 SPARC 机器上读取。由于数据流就是二进制流,因此我们也可以直接读写没有编码的二进制数据,例如图像、视频、音频等。

QDataStream既能够存取 C++ 基本类型,如 int、char、short 等,也可以存取复杂的数据类型,例如自定义的类。实际上,QDataStream对于类的存储,是将复杂的类分割为很多基本单元实现的。

结合QIODevice,QDataStream可以很方便地对文件、网络套接字等进行读写操作。我们从代码开始看起:

QFile file("file.dat");file.open(QIODevice::WriteOnly);QDataStream out(&file);out << QString("the answer is");out << (qint32)42;
  • 在这段代码中,我们首先打开一个名为 file.dat 的文件(注意,我们为简单起见,并没有检查文件打开是否成功,这在正式程序中是不允许的)。然后,我们将刚刚创建的file对象的指针传递给一个QDataStream实例out。类似于std::cout标准输出流,QDataStream也重载了输出重定向<<运算符。后面的代码就很简单了:将“the answer is”和数字 42 输出到数据流。由于我们的 out 对象建立在file之上,因此相当于将问题和答案写入file。

  • 需要指出一点:最好使用 Qt 整型来进行读写,比如程序中的qint32。这保证了在任意平台和任意编译器都能够有相同的行为。

如果你直接运行这段代码,你会得到一个空白的 file.dat,并没有写入任何数据。这是因为我们的file没有正常关闭。为性能起见,数据只有在文件关闭时才会真正写入。因此,我们必须在最后添加一行代码:

file.close(); // 如果不想关闭文件,可以使用 file.flush();

接下来我们将存储到文件中的答案取出来

  QFile file("file.dat");file.open(QIODevice::ReadOnly);QDataStream in(&file);QString str;qint32 a;
in >> str >> a;

唯一需要注意的是,你必须按照写入的顺序,将数据读取出来。顺序颠倒的话,程序行为是不确定的,严重时会直接造成程序崩溃。

那么,既然QIODevice提供了read()、readLine()之类的函数,为什么还要有QDataStream呢?QDataStream同QIODevice有什么区别?区别在于,QDataStream提供流的形式,性能上一般比直接调用原始 API **更好一些。**我们通过下面一段代码看看什么是流的形式:

   QFile file("file.dat");file.open(QIODevice::ReadWrite);QDataStream stream(&file);QString str = "the answer is 42";stream << str;

10.3 文本文件读写

上一节我们介绍了有关二进制文件的读写。二进制文件比较小巧,却不是人可读的格式。而文本文件是一种人可读的文件。为了操作这种文件,我们需要使用QTextStream类。QTextStream和QDataStream的使用类似,只不过它是操作纯文本文件的。

QTextStream会自动将 Unicode 编码同操作系统的编码进行转换,这一操作对开发人员是透明的。它也会将换行符进行转换,同样不需要自己处理。QTextStream使用 16 位的QChar作为基础的数据存储单位,同样,它也支持 C++ 标准类型,如 int 等。实际上,这是将这种标准类型与字符串进行了相互转换。

QTextStream同QDataStream的使用基本一致,例如下面的代码将把“The answer is 42”写入到 file.txt 文件中:

 QFile data("file.txt");if (data.open(QFile::WriteOnly | QIODevice::Truncate)) {QTextStream out(&data);out << "The answer is " << 42;
}

这里,我们在open()函数中增加了QIODevice::Truncate打开方式。我们可以从下表中看到这些打开方式的区别:

枚举值 描述

  • QIODevice::NotOpen 未打开
  • QIODevice::ReadOnly 以只读方式打开
  • QIODevice::WriteOnly 以只写方式打开
  • QIODevice::ReadWrite 以读写方式打开
  • QIODevice::Append 以追加的方式打开,新增加的内容将被追加到文件末尾
  • QIODevice::Truncate 以重写的方式打开,在写入新的数据时会将原有数据全部清除,游标设置在文件开头。
  • QIODevice::Text 在读取时,将行结束符转换成 \n;在写入时,将行结束符转换成本地格式,例如 Win32 平台上是 \r\n
  • QIODevice::Unbuffered 忽略缓存

我们在这里使用了QFile::WriteOnly | QIODevice::Truncate,也就是以只写并且覆盖已有内容的形式操作文件。注意,QIODevice::Truncate会直接将文件内容清空。

虽然QTextStream的写入内容与QDataStream一致,但是读取时却会有些困难:

  QFile data("file.txt");if (data.open(QFile::ReadOnly)) {QTextStream in(&data);QString str;int ans = 0;in >> str >> ans;}

在使用QDataStream的时候,这样的代码很方便,但是使用了QTextStream时却有所不同:读出的时候,str 里面将是 The answer is 42,ans 是 0。这是因为当使用QDataStream**写入的时候,实际上会在要写入的内容前面,额外添加一个这段内容的长度值。而以文本形式写入数据,是没有数据之间的分隔的。**因此,使用文本文件时,很少会将其分割开来读取,而是使用诸如使用:

QTextStream::readLine();//读取一行
QTextStream::readAll();//读取所有文本

这种函数之后再对获得的QString对象进行处理。

默认情况下,QTextStream的编码格式是 Unicode,如果我们需要使用另外的编码,可以使用:

stream.setCodec("UTF-8");

这样的函数进行设置。

10.4 综合案例

下面我们编写这么一个案例:浏览一个文本文件内容:

image-20221101143658936

先编辑ui文件样式如上,需使用一个按键改为浏览和Line Edit用widget容器水平对齐,之后添加一个Plain Text Edit,最后全部垂直对齐就可以了;我们再点击按键右击转到槽,编写代码:使用打开文件函数 getOpenFileName

image-20221101143936315

我们写好槽之后:

void Widget::on_pushButton_clicked()
{QFileDialog::getOpenFileName(this,"打开一个txt","C:\\文档","Txt (*.txt)");
}
image-20221101144622478

框框已经搭建好了,然后我们还需要处理返回值,读取文本内容:

//判断文件是否为空if(fileName.isEmpty()){return;}//不为空串,选择了某个文件,将文件显示到lineEditthis->ui->lineEdit->setText(fileName);//使用qfile来读取文件QFile file(fileName);//只读方式打开文件file.open(QIODevice::ReadOnly);//读取文件内容//将所有内容全部读取出来QByteArray arry = file.readAll();//将QByteArray转化成QStringQString content = QString(arry);//输出到edit上this->ui->plainTextEdit->setPlainText(content);file.close();//关闭文件
image-20221101151525052

但这里只能处理utf-8格式的文本,如果是其它比如gdb的话,需要单独转码,使用QTextCodec函数

image-20221101152112830
 //将arry转化为QStringQString content = codec->toUnicode(arry);

我们还可以使用其它函数比如读取一行readLine使用循环读:

 QByteArray array;do{//单行读取array += file.readLine();}while(!file.atEnd());//读取文本结束

还有写文件,以追加的方式,文件名为text.txt,内容为hello

    QFile file("G:\\C7_Qt\\day04\\04_lessonNote\\text.txt");file.open(QIODevice::WriteOnly|QIODevice::Append);file.write("hello");file.close();

152112830" style=“zoom:67%;” />

 //将arry转化为QStringQString content = codec->toUnicode(arry);

我们还可以使用其它函数比如读取一行readLine使用循环读:

 QByteArray array;do{//单行读取array += file.readLine();}while(!file.atEnd());//读取文本结束

还有写文件,以追加的方式,文件名为text.txt,内容为hello

    QFile file("G:\\C7_Qt\\day04\\04_lessonNote\\text.txt");file.open(QIODevice::WriteOnly|QIODevice::Append);file.write("hello");file.close();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/23203.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构绪论

一-数据结构的基本概念 1-数据 数据是信息的载体&#xff0c;是描述客观事物属性的数、字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据是计算机程序加工的原料。 2-数据元素 数据元素是数据的基本单位&#xff0c;通常作为一个整体进行考虑和处理。…

kafka基本知识

什么是 Kafka&#xff1f; Apache Kafka 是一个开源的分布式流处理平台&#xff0c;最初由 LinkedIn 开发&#xff0c;后来成为 Apache 软件基金会的一部分。Kafka 主要用于构建实时数据管道和流处理应用程序。它能够高效地处理大量的数据流&#xff0c;广泛应用于日志收集、数…

DeepSeek R1本地+私有云版医疗AI部署开发成功案例技术剖析

1. 引言 1.1 研究背景与意义 随着科技的飞速发展,人工智能(AI)在医疗领域的应用正逐渐成为推动医疗行业变革的重要力量。近年来,医疗 AI 取得了显著的进展,从疾病诊断、药物研发到医疗管理等各个环节,AI 技术都展现出了巨大的潜力。它能够处理和分析海量的医疗数据,为…

网络安全-openssl工具

OpenSSl是一个开源项目&#xff0c;包括密码库和SSL/TLS工具集。它已是在安全领域的事实标准&#xff0c;并且拥有比较长的历史&#xff0c;现在几乎所有的服务器软件和很多客户端都在使用openssl&#xff0c;其中基于命令行的工具是进行加密、证书管理以及测试最常用到的软件。…

“三次握手”与“四次挥手”:TCP传输控制协议连接过程

目录 什么是TCP协议 “三次握手”建立连接 “四次挥手”断开连接 “三次握手”和“四次挥手”的反思 总结 什么是TCP协议 想象一下&#xff0c;你和远方的朋友要进行一场电话交流&#xff0c;但这通电话不仅仅是随便聊聊&#xff0c;而是要传递一封重要的信件。为了确保这…

Visual Studio中打开多个项目

1) 找到解决方案窗口 2) 右键添加→ 选择现有项目 3) 选择.vcxproj文件打开即可

基于 Python Django 的校园互助平台(附源码,文档)

博主介绍&#xff1a;✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不…

el-dropdown选中效果

vue2版本 <template><el-dropdown size"mini" command"handleCommand"><span class"el-dropdown-link">{{ selectedOption }}<i class"el-icon-arrow-down el-icon--right"></i></span><el-d…

Deepseek首页实现 HTML

人工智能与未来&#xff1a;机遇与挑战 引言 在过去的几十年里&#xff0c;人工智能&#xff08;AI&#xff09;技术取得了突飞猛进的发展。从语音助手到自动驾驶汽车&#xff0c;AI 正在深刻地改变我们的生活方式、工作方式以及社会结构。然而&#xff0c;随着 AI 技术的普及…

Linux(ubuntu) GPU CUDA 构建Docker镜像

一、创建Dockerfile FROM ubuntu:20.04#非交互式&#xff0c;以快速运行自动化任务或脚本&#xff0c;无需图形界面 ENV DEBIAN_FRONTENDnoninteractive# 安装基础工具 RUN apt-get update && apt-get install -y \curl \wget \git \build-essential \software-proper…

Rocky8 源码安装 HAProxy

HAProxy 是一款开源的高性能 负载均衡器 和 反向代理 软件&#xff0c;专注于处理高并发流量分发&#xff0c;广泛应用于企业级架构中提升服务的可用性、扩展性和安全性。 一、HAProxy 简介 1.1.HAProxy 是什么&#xff1f; 本质&#xff1a; 基于 C 语言开发 的轻量级工具&a…

JAVA最新版本详细安装教程(附安装包)

目录 文章自述 一、JAVA下载 二、JAVA安装 1.首先在D盘创建【java/jdk-23】文件夹 2.把下载的压缩包移动到【jdk-23】文件夹内&#xff0c;右键点击【解压到当前文件夹】 3.如图解压会有【jdk-23.0.1】文件 4.右键桌面此电脑&#xff0c;点击【属性】 5.下滑滚动条&…

Spring Boot 日志管理(官网文档解读)

摘要 本篇文章详细介绍了SpringBoot 日志管理相关的内容&#xff0c;文章主要参考官网文章的描述内容&#xff0c;并在其基础上进行一定的总结和拓展&#xff0c;以方便学习Spring Boot 的小伙伴能快速掌握Spring Boot 日志管理相关的内容。 日志实现方式 Sping Boot 的日志管…

【废物研究生零基础刷算法】DFS与递归(一)典型题型

文章目录 跳台阶递归实现指数级枚举递归实现排列型枚举上面两题总结 递归实现组合型枚举P1036选数 跳台阶 思路&#xff1a; 如果 n 1&#xff0c;只有一种走法&#xff08;走 1 级&#xff09;。如果 n 2&#xff0c;有两种走法&#xff08;11 或 2&#xff09;。对于 n &g…

百度首页上线 DeepSeek 入口,免费使用

大家好&#xff0c;我是小悟。 百度首页正式上线了 DeepSeek 入口&#xff0c;这一重磅消息瞬间在技术圈掀起了惊涛骇浪&#xff0c;各大平台都被刷爆了屏。 百度这次可太给力了&#xff0c;PC 端开放仅 1 小时&#xff0c;就有超千万人涌入体验。这速度&#xff0c;简直比火…

at32f103a+rtt+AT组件+esp01s 模块使用

AT组件使用 这里需要设置wifi名称和密码 配置使用的串口 配置上边的自动会配置,at_device 依赖了at_client 依赖sal也自动加入 依赖了串口2 uart2 连接WiFi AT+ CWJAP = TP-LINK_45A1

QT 基础知识点

1.基础窗口类QMainWindow qDialog Qwidget 随项目一起创建的窗口基类有三个可选QMainWindow qDialog Qwidget 1.1 Qwidget 是所有窗口的基类&#xff0c;只要是他的子类&#xff0c;或子类的子类&#xff0c;都具有他的属性。 右键项目 Add New -> Qt qt设计师界面类&am…

[漏洞篇]文件上传漏洞详解

[漏洞篇]文件上传漏洞详解 一、介绍 1. 概念 文件上传漏洞是指用户上传了一个可执行的脚本文件&#xff0c;并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的&#xff0c;“文件上传” 本身没有问题&#xff0c;有问题的是文件上传后&#xf…

Grok 3与GPT-4.5的“智能天花板”争夺战——谁才是大模型时代的算力之王?

2025年2月18日&#xff0c;马斯克旗下 xAI 高调发布新一代大模型Grok 3&#xff0c;号称“地球上最聪明AI”&#xff0c;在数学推理、代码生成等核心能力上碾压 GPT-4o、DeepSeek-V3 等对手。而就在同一天&#xff0c;OpenAI创始人 Sam Altman 暗示 GPT-4.5 即将登场&#xff0…

ubuntu新系统使用指南

1. 更新源 2. 配置rime 输入法 sudo apt install ibus-rimeibus-setup #打开配置界面添加雾凇拼音 cd ~/Documents/Tool/input_source/plumgit clone --depth 1 https://github.com/rime/plum plum #没有梯子就劝退cd plum/bash rime-install iDvel/rime-ice:others/recipe…