Qt OpenGL 2D图像文字

news2024/7/4 5:22:03

这次教程中,我们将学会如何使用四边形纹理贴图把文字显示在屏幕上。我们将把256个不同的文字从一个256×256的纹理图像中一个个提取出来,接着创建一个输出函数来创建任意我们希望的文字。

还记得在第一篇字体教程中我提到使用纹理在屏幕上绘制文字吗?通常当你使用纹理绘制文字时你会调用你最喜欢的图像处理程序,选择一种字体,然后输入你想显示的文字或段落,然后保存下来位图并把它作为纹理读入到你的程序里,问题是这对一个需要很多文字或者文字在不停变化的程序来说,这么做效率并不高。这次教程中我们只使用一个纹理来显示任意256个不同的字符。

程序运行时效果如下:

 

下面进入教程:

由于相较于之前几课字体教程的代码改动较大,我们将直接在第01课的基础上修改代码,我会一一解释新增的代码,首先myglwidget.h文件,将类声明更改如下:

 1 #ifndef MYGLWIDGET_H
 2 #define MYGLWIDGET_H
 3  
 4 #include <QWidget>
 5 #include <QGLWidget>
 6  
 7 class MyGLWidget : public QGLWidget
 8 {
 9     Q_OBJECT
10 public:
11     explicit MyGLWidget(QWidget *parent = 0);
12     ~MyGLWidget();
13  
14 protected:
15     //对3个纯虚函数的重定义
16     void initializeGL();
17     void resizeGL(int w, int h);
18     void paintGL();
19  
20     void keyPressEvent(QKeyEvent *event);           //处理键盘按下事件
21  
22 private:
23     void buildFont();                               //创建字体
24     void killFont();                                //删除显示列表
25     //输出字符串
26     void glPrint(GLuint x, GLuint y, const char *string, int set);
27  
28 private:
29     bool fullscreen;                                //是否全屏显示
30  
31     GLuint m_Base;                                  //储存绘制字体的显示列表的开始位置
32     GLfloat m_Cnt1;                                 //字体移动计数器1
33     GLfloat m_Cnt2;                                 //字体移动计数器2
34  
35     QString m_FileName[2];                          //图片的路径及文件名
36     GLuint m_Texture[2];                            //储存两个纹理
37 };
38  
39 #endif // MYGLWIDGET_H

我们增加了变量m_Base、m_Cnt1、m_Cnt2,函数声明buildFont()、killFont(),这些和之前都讲过的作用都一样就不重复了。而m_FileName和m_Texture变为了长度为2的数组,这是因为程序中我们会用两种不同的图来建立两个不同的纹理。最后是glPrint()函数的声明,注意下它的参数和前几课不同,但作用是相同的,具体的下面会讲到。

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

接下来,我们需要打开myglwidget.cpp,加入声明#include <QTimer>、#include<QtMath>,将构造函数和析构函数修改如下(比较简单不具体解释了):

CSDN QT技术栈大纲:Qt开发必备技术栈学习路线和资料

 1 MyGLWidget::MyGLWidget(QWidget *parent) :
 2     QGLWidget(parent)
 3 {
 4     fullscreen = false;
 5     m_Cnt1 = 0.0f;
 6     m_Cnt2 = 0.0f;
 7     m_FileName[0] = "D:/QtOpenGL/QtImage/Font.bmp";        //应根据实际存放图片的路径进行修改
 8     m_FileName[1] = "D:/QtOpenGL/QtImage/Bumps.bmp";
 9  
10     QTimer *timer = new QTimer(this);                   //创建一个定时器
11     //将定时器的计时信号与updateGL()绑定
12     connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
13     timer->start(10);                                   //以10ms为一个计时周期
14 }
1 MyGLWidget::~MyGLWidget()
2 {
3     killFont();                                         //删除显示列表
4 }

继续,我们要来定义我们增加的三个函数,同样是重头戏,具体代码如下:

 1 void MyGLWidget::buildFont()                            //创建位图字体
 2 {
 3     float cx, cy;                                       //储存字符的x、y坐标
 4     m_Base = glGenLists(256);                           //创建256个显示列表
 5     glBindTexture(GL_TEXTURE_2D, m_Texture[0]);         //选择字符纹理
 6  
 7     for (int i=0; i<256; i++)                           //循环256个显示列表
 8     {
 9         cx = float(i%16)/16.0f;                         //当前字符的x坐标
10         cy = float(i/16)/16.0f;                         //当前字符的y坐标
11  
12         glNewList(m_Base+i, GL_COMPILE);                //开始创建显示列表
13             glBegin(GL_QUADS);                          //使用四边形显示每一个字符
14                 glTexCoord2f(cx, 1-cy-0.0625f);
15                 glVertex2i(0, 0);
16                 glTexCoord2f(cx+0.0625f, 1-cy-0.0625f);
17                 glVertex2i(16, 0);
18                 glTexCoord2f(cx+0.0625f, 1-cy);
19                 glVertex2i(16, 16);
20                 glTexCoord2f(cx, 1-cy);
21                 glVertex2i(0, 16);
22             glEnd();                                    //四边形字符绘制完成
23             glTranslated(10, 0, 0);                     //绘制完一个字符,向右平移10个单位
24         glEndList();                                    //字符显示列表完成
25     }
26 }
1 void MyGLWidget::killFont()                             //删除显示列表
2 {
3     glDeleteLists(m_Base, 256);                         //删除256个显示列表
4 }
 1 void MyGLWidget::glPrint(GLuint x, GLuint y,            //输入字符串
 2                          const char *string, int set)
 3 {
 4     if (set > 1)                                        //如果字符集大于1
 5     {
 6         set = 1;                                        //设置其为1
 7     }
 8  
 9     glBindTexture(GL_TEXTURE_2D, m_Texture[0]);         //绑定为字体纹理
10     glDisable(GL_DEPTH_TEST);                           //禁止深度测试
11     glMatrixMode(GL_PROJECTION);                        //选择投影矩阵
12     glPushMatrix();                                     //保存当前的投影矩阵
13     glLoadIdentity();                                   //重置投影矩阵
14     glOrtho(0, 640, 0, 480, -1, 1);                     //设置正投影的可视区域
15     glMatrixMode(GL_MODELVIEW);                         //选择模型观察矩阵
16     glPushMatrix();                                     //保存当前的模型观察矩阵
17     glLoadIdentity();                                   //重置模型观察矩阵
18  
19     glTranslated(x, y ,0);                              //把字符原点移动到(x,y)位置
20     glListBase(m_Base-32+(128*set));                    //选择字符集
21     glCallLists(strlen(string), GL_BYTE, string);       //把字符串写到屏幕
22     glMatrixMode(GL_PROJECTION);                         //选择投影矩阵
23     glPopMatrix();                                      //设置为保存的矩阵
24     glMatrixMode(GL_MODELVIEW);                        //选择模型观察矩阵
25     glPopMatrix();                                      //设置为保存
26     glEnable(GL_DEPTH_TEST);                            //启用深度测试
27 }

首先是buildFont()函数。我们先是定义两个临时变量来储存字体纹理中每个字的位置,cx储存水平方向位置,cy储存竖直方向位置。接着我们告诉OpenGL我们要建立256个显示列表,变量m_Base指向第一个显示列表,然后选择我们的字体纹理。现在我们开始循环,来创建所以256个字符,并存在显示列表里。一开始我们计算得到cx、cy,对16取余和除以16是由于一行是16个字符,最后都除以16.0f是按16个字符把纹理宽度高度均为1.0分成16份。

后面就开始创建显示列表了,绘制四边形对应纹理时,+或-0.0625f是指一个字符的高或宽,还有由于纹理坐标(0, 0)是在左下角,所以glTexCoord2f(x, y)的第二参数是1-cy、1-cy-0.0625而不是cy、cy+0.0625(比如说cx、cy同时为0,那它对应的字符纹理左下角坐标就应是(0.0, 1-0.0f-0.0625f)了,希望大家能明白)。要注意的是,我们使用glVertex2i()而不是glVertex3f(),我们的字体是2D字体,所以不需要z值。因为我们使用的是正交投影,我们不需要移进屏幕,在一个正交投影平面绘图你所需要的是指定x、y坐标。又因为我们的屏幕是以像素形式从0到639(宽),从0到479(高),因此我们既不需要用浮点数也不需要负数。

画完四边形后,我们右移了10个像素,至于纹理有病。如果我们不平移,文字将会重叠。有由于我们的字体太窄太瘦,我们不想右移16个像素那么多,如果那样的话,每个字符之间将有很大的间隔,只移动10个像素是个不错的选择。

接着是killFont()函数。它很简单,就是调用glDeleteLists()函数从m_Base开始删除256个显示列表。

最后是glPrint()函数。首先我们判断一下set字符集,如果大于1,就将set置0。这是由于我们的字体纹理中只有两种字体,第一种是普通的,第二种是斜体,如果选择的字符集有误,我们就把它设为默认的普通字符集。接着我们再次选择字体纹理,我们这么做事防止我们在决定往屏幕上输出文字前选择了别的纹理,导致出错。然后我们禁用了深度测试,我们这么做事因为混合的效果会更好。如果我们不禁用深度测试,文字可能会被什么东西挡住,或者得不到正确的混合效果。当然,如果你不打算混合文字(那样文字周围的黑色区域就不会显示),你就可以启用深度测试。

下面几行十分重要!我们选择投影矩阵,然后调用glPushMatrix()函数,保存当前投影矩阵(其实就是把投影矩阵压入堆栈)。保存投影矩阵后,我们重置矩阵并调用glOrtho()设置正交投影屏幕,第一和第三个参数表示屏幕的底边和左边,第二和第四个参数表示屏幕的上边和右边。由于我们不需要用到深度测试,所以我们将第五和第六个参数设为-1和1。我们再选择模型观察矩阵,用glPushMatrix()保存当前设置。然后我们重置模型观察矩阵以便在正交投影视点下工作。

现在我们可以绘制文字了,我们从移动到绘制文字的位置开始。我们使用glTranslated()而不是glTranslatef(),因为我们处理的是像素,所以浮点数没有意义。接着我们用glListBase()来选择字符集,如果我们想使用第二个字符集,我们在当前的显示列表基数上加上128,通过加128,我们跳过了前128个字符。而减去32是因为我们的字符集是从“空格”开始的,即ASCII码第33个字符开始,故我们减去32,告诉OpenGL跳过前面32个字符。然后我们使用glCallLists()绘制文字,这个之前解释过,不再解释了。

最后我们要做的是恢复透视视图。我们选择投影矩阵并用glPopMatrix()恢复我们先前glPushMatrix()保存的设置,接着选择模型观察矩阵做相同的工作。你或许会问,难道不用按相反顺序弹出矩阵吗?不用,这是用于投影矩阵和模型观察矩阵的堆栈并不是同一个(这样说其实并不准确,不过道理是差不多的),所以无论选择哪个矩阵先弹出都没有问题。值得注意的是,如果你把代码中的最后两句glMatrixMode()调换位置,运行程序时你是看不到图像纹理的只能看到文字,这是由于我们最后选择的矩阵是GL_PROJECTION,而我们绘制图像纹理是在GL_MODEWIEW上绘制的,所以你看不到图像纹理。当然解决办法就是,在恢复了投影矩阵后,开始深度测试之前,再次调用glMatrix()选择模型观察矩阵GL_MODEVIEW。那为什么我们能看到文字呢?这是由于做了平面正交投影后,在任何矩阵所绘制的东西都是在平面绘制的,OpenGL自动会把它们投影到屏幕上,所以总能看到文字的。函数最后我们启用了深度测试,如果你没有在上面的代码中关闭深度测试,就不需要这行。

然后我们修改一下initializeGL()函数,具体代码如下:

 1 void MyGLWidget::initializeGL()                         //此处开始对OpenGL进行所以设置
 2 {
 3     m_Texture[0] = bindTexture(QPixmap(m_FileName[0])); //载入位图并转换成纹理
 4     m_Texture[1] = bindTexture(QPixmap(m_FileName[1]));
 5     glEnable(GL_TEXTURE_2D);                            //启用纹理映射
 6  
 7     glClearColor(0.0, 0.0, 0.0, 0.0);                   //黑色背景
 8     glShadeModel(GL_SMOOTH);                            //启用阴影平滑
 9     glClearDepth(1.0);                                  //设置深度缓存
10     glEnable(GL_DEPTH_TEST);                            //启用深度测试
11     glDepthFunc(GL_LEQUAL);                             //所作深度测试的类型
12     glBlendFunc(GL_SRC_ALPHA, GL_ONE);                  //设置混合因子
13     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  //告诉系统对透视进行修正
14  
15     buildFont();                                        //创建字体
16 }

最开始三行载入位图转换纹理,启用纹理映射就不解释了。中间部分有小的改动,由于我们要给字体上色,所以要设置混合因子。最后调用buildFont()创建字体。

最后,我们该进入paintGL()函数,这次难度还行,具体代码如下:

 1 void MyGLWidget::paintGL()                              //从这里开始进行所以的绘制
 2 {
 3     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
 4     glLoadIdentity();                                   //重置当前的模型观察矩阵
 5  
 6     glBindTexture(GL_TEXTURE_2D, m_Texture[1]);         //设置为图像纹理
 7     glTranslatef(0.0f, 0.0f, -5.0f);                    //移入屏幕5.0单位
 8     glRotatef(45.0f, 0.0f, 0.0f, 1.0f);                 //绕z轴旋转45度
 9     glRotatef(m_Cnt1*30.0f, 1.0f, 1.0f, 0.0f);          //绕(1,1,0)轴旋转
10     glDisable(GL_BLEND);                                //关闭融合
11     glColor3f(1.0f, 1.0f, 1.0f);                        //设置颜色为白色
12     glBegin(GL_QUADS);                                  //绘制纹理四边形
13         glTexCoord2d(0.0f, 0.0f);
14         glVertex2f(-1.0f, 1.0f);
15         glTexCoord2d(1.0f, 0.0f);
16         glVertex2f(1.0f, 1.0f);
17         glTexCoord2d(1.0f, 1.0f);
18         glVertex2f(1.0f, -1.0f);
19         glTexCoord2d(0.0f, 1.0f);
20         glVertex2f(-1.0f, -1.0f);
21     glEnd();
22  
23     glRotatef(90.0f, 1.0f, 1.0f, 0.0);                  //绕(1,1,0)轴旋转90度
24     glBegin(GL_QUADS);                                  //绘制第二个四边形,与第一个垂直
25         glTexCoord2d(0.0f, 0.0f);
26         glVertex2f(-1.0f, 1.0f);
27         glTexCoord2d(1.0f, 0.0f);
28         glVertex2f(1.0f, 1.0f);
29         glTexCoord2d(1.0f, 1.0f);
30         glVertex2f(1.0f, -1.0f);
31         glTexCoord2d(0.0f, 1.0f);
32         glVertex2f(-1.0f, -1.0f);
33     glEnd();
34  
35     glEnable(GL_BLEND);                                 //启用混合
36     glLoadIdentity();                                   //重置视口
37     //根据字体位置设置颜色
38     glColor3f(1.0f*float(cos(m_Cnt1)), 1.0*float(sin(m_Cnt2)),
39             1.0f-0.5f*float(cos(m_Cnt1+m_Cnt2)));
40     glPrint(int((280+250*cos(m_Cnt1))),
41             int(235+200*sin(m_Cnt2)), "NeHe", 0);
42     glColor3f(1.0*float(sin(m_Cnt2)), 1.0f-0.5f*float(cos(m_Cnt1+m_Cnt2)),
43               1.0f*float(cos(m_Cnt1)));
44     glPrint(int((280+230*cos(m_Cnt2))),
45             int(235+200*sin(m_Cnt1)), "OpenGL", 1);
46     glColor3f(0.0f, 0.0f, 1.0f);
47     glPrint(int(240+200*cos((m_Cnt1+m_Cnt2)/5)), 2,
48             "Giuseppe D'Agata", 0);
49     glColor3f(1.0f, 1.0f, 1.0f);
50     glPrint(int(242+200*cos((m_Cnt1+m_Cnt2)/5)), 2,
51             "Giuseppe D'Agata", 0);
52  
53     m_Cnt1 += 0.01f;                                   //增加两个计数器的值
54     m_Cnt2 += 0.0081f;
55 }

函数中我们先绘制3D物体最后绘制文字,这样文字将显示在3D物体上面,而不会被3D物体遮住。我们之所以加入一个3D物体是为了演示透视投影和正交投影可同时使用。首先我们选择纹理,为了看见3D物体,我们往屏幕内移动5个单位。我们绕z轴旋转45度,这将使我们的四边形顺时针旋转45度,让我们的四边形看起来更像砖石而不是矩形,接着我们让物体同时绕x、y轴旋转m_Cnt1*30度,这使我们的物体像在一个点上旋转的钻石那样旋转。然后我们关闭混合,设置颜色为亮白,绘制第一个纹理映射的四边形。再绕x、y轴旋转90度,画另一个四边形,第二个四边形从第一个四边形中间切过去,来形成一个好看的形状。

在绘制完有纹理贴图的四边形后,我们开启混合并绘制文字,下面的根据文字选择颜色,打印“NeHe”、“OpenGL”就不解释了。我们来看打印“Giuseppe D'Agata”时,我们用深蓝色和亮白色两次绘制(作者的名字),并在x方向上平移2个像素,这样创造出一种亮白色字附带深蓝色阴影的效果,感觉真的很棒啊!要注意的是,这里必须打开混合,如果没有打开是不会出现这样的效果的(大家可以注释掉glEnable(GL_BLEND)看看,我就不解释了),甚至其它两个字符串也变得糟糕透了。最后一件事是以不同的速率递增我们的计数器,这使得文字移动,3D物体自转。

现在就可以运行程序查看效果了!

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot构建RESTful 风格应用

Spring Boot 构建 RESTful 风格应用 1.Web开发的两种模式&#xff1a; 前后端不分离&#xff1a; 以前没有移动互联网时&#xff0c;我们做的大部分应用都是前后端不分的&#xff0c;比如jsp&#xff0c;或者thymeleaf等后端分离模板&#xff0c;在这种架构的应用中&#xf…

SQLMAP _DNS注入配置方法

网上针对sqlmap进行dns注入的相关文章太少&#xff0c;只是简单介绍了下–dns-domain参数&#xff0c;相关的实战文章要么就模糊或者一笔带过&#xff0c;。然后参考网上的方法重新整理了一遍&#xff0c;简单理解。 需要准备的东西&#xff0c;sqlmap、windows盲注一个、两个…

档案信息化咨询方法论实践要点有哪些?

咨询工作中&#xff0c;有些咨询师也许会有这样的疑问&#xff1a;“我业务经验挺丰富的&#xff0c;但做咨询工作怎么这么吃力呢&#xff1f;”这可能就是因为缺乏方法论的有效指导。咨询方法论是咨询工作中用来分析和解决某类特定问题的工具、方法、流程、模型和评价准则等。…

Ansible之playbook详解和应用实例

目录 一、playbook简介 1.什么是playbook 2.playbook组成 二、应用实例 1.使用playbook安装启用httpd服务 2.使用playbook安装启用nginx服务 三、ansible-playbook其他用法 1.检查yaml文件的语法是否正确 2.检查tasks任务 3.检查指定的主机 4.指定从某个task开始运行…

红队学习隧道必须了解的知识

端口映射和端口转发 端口映射 端口映射就是将外网的主机的一个端口映射到内网主机的一个端口&#xff0c;提供相应的服务。当用户访问外网IP的这个端口时&#xff0c;服务器自动将请求映射到对应局域网内部的机器上 端口转发 端口转发就是将发往外网指定端口的通信完全转发给…

windows下安装hbase

windows下安转hbase 安装流程 解压提供的压缩包 该压缩包是经过我修改后的&#xff0c;已经创建data、tmp、zookeeper目录和替换相关配置文件&#xff0c;目的是简化安装流程 压缩包 https://share.weiyun.com/SLTS9woO 环境变量配置 在系统变量中添加HBASE_HOME环境变量&…

Java ConcurrentHashMap 高并发安全实现原理解析

三、C13Map的字段定义 C13Map的字段定义 //最大容量 private static final int MAXIMUM_CAPACITY 1 << 30; //默认初始容量 private static final int DEFAULT_CAPACITY 16; //数组的最大容量,防止抛出OOM static final int MAX_ARRAY_SIZE Integer.MAX_VALUE -…

使用pyautogui进行PC用户界面自动化测试

目录 1.pyautogui简介及安装 2.pyautogui常见用法 2.1返回所用显示器的分辨率 2.2键盘输入函数 2.3常用函数typewrite() 2.4键盘事件 2.5函数hotkey() 2.6保存屏幕截图 3.常见用法代码汇总 1.pyautogui简介及安装 Pyautogui是一个纯Python的图形化自动化工具&#x…

代码审计-4 代码执行漏洞

代码执行漏洞 代码执行漏洞利用 ZZZPHP1.6 远程代码执行漏洞分析 漏洞点函数 此处如果能控制$ifstr就可以进行闭合&#xff0c;执行恶意代码 parserIfLabel函数将传入的参数进行正则匹配&#xff0c;当匹配通过时继续往下走 下面并没有对恶意代码内容进行过滤 跟踪parserIfLab…

游戏开发37课 狙击枪 视野问题

首先说一下视野的思路&#xff1a;我们可视化的视野全部都是以扇形显示的&#xff0c;同时为了后期的方便调整我们的视野和距离都必须是动态的。那么我们是不是可以使用度数来控制视野范围&#xff0c;那么我们就需要画出一个扇形。那么我们可以先画出来一个圆 然后在这个圆上面…

Linux安装 vmware workstation

官网下载地址 vmware workstation&#xff1a; Download VMware Workstation Pro 也可以下载提供的安装包。 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;au74 一、Ubuntu 安装 安装构建依赖项&#xff0c;打开您的系统终端并运行以下命令&#xff1a; s…

Linux操作系统中的yum命令

Linux操作系统中的yum命令是大家经常会用到的命令&#xff0c;有着非常重要的作用&#xff0c;但很多朋友依然不太清楚yum命令作用是什么&#xff1f;yum命令有哪些语法和常用命令&#xff1f;接下来我们一起来看看详细的内容介绍。 yum命令全称为Yellow dog Updater, Modified…

ChatGPT生成量化交易策略,真好玩

OK&#xff0c;还有没有更好玩的对 量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 『正文』 ˇ 最近比较火的OpenAI-ChatGPT&#xff0c;太有意思了。尝试让它写了几个策略&#xff0c;您别说&#xff0c;还真是有模有样。我们来看看吧。 模型一&a…

哈希(开散列、闭散列)-位图-布隆过滤器-哈希切分

文章目录1、哈希概念2、哈希表/散列表&#xff08;1&#xff09;哈希函数的设计&#xff1a;&#xff08;2&#xff09;&#xff08;最常用&#xff09;除留余数法&#xff1a;&#xff08;3&#xff09;如何解决哈希冲突&#xff1f;更加合理的设计哈希函数闭散列&#xff08;…

Java多线程之线程同步机制(锁,线程池等等)

Java多线程之线程同步机制一、概念1、并发2、起因3、缺点二、三大不安全案例1、样例一&#xff08;模拟买票场景&#xff09;2、样例二&#xff08;模拟取钱场景&#xff09;3、样例三&#xff08;模拟集合&#xff09;三、同步方法及同步块1、同步方法2、同步块四、JUC安全类型…

java多线程这一篇就差不多了

java多线程这一篇就差不多了 什么是多线程&#xff1f; 一般被问你对多线程了解多少的时候&#xff0c;你可能不仅仅只需要知道线程怎么创建&#xff0c;你可能需要了解线程的几种创建方式&#xff0c;线程的生命周期&#xff0c;线程池相关&#xff0c;并发安全&#xff0c;…

ADSP-21489的图形化编程详解(4:左右声道音量调节和多通道的输入输出详解)

左右声道音量调节 在直通的前提下&#xff0c;我们加入一个调音量的算法模块&#xff0c;来实现调节输出音量大小的功能。首先拖出来一个音量调节算法模块&#xff1a; 我们这里都是双通道&#xff0c;所以需要对这个音量调节模块进行配置&#xff1a; 连好程序&#xff0c;下…

acwing基础课——Dijkstra

由数据范围反推算法复杂度以及算法内容 - AcWing 常用代码模板3——搜索与图论 - AcWing 基本思想&#xff1a; 迪杰斯特拉&#xff08;dijkstra&#xff09;算法是单源最短路径问题的求解方法,它是一个按路径长度递增的次序产生最短路径的算法。单源最短路径就在给出一个固定…

Sqoop数据导出第2关:HDFS数据导出至Mysql内

为了完成本关任务,你需要掌握: 1、数据库( MySQL )建表。 2、HDFS 数据导出至 MySQL 中。 数据库( MySQL )建表 因为这边 Sqoop 不能够帮关系型数据库创建表,所以需要我们自己来创建表。 用命令进入 MySQL 客户端。 mysql -uroot -p123123 -h127.0.0.1 创建数据库…

备忘录模式(Memento)

参考&#xff1a; [备忘录设计模式 (refactoringguru.cn)](https://refactoringguru.cn/design-patterns/mediator) 文章目录一、什么是备忘录模式&#xff1f;二、实现三、优缺点优点缺点四、适用环境一、什么是备忘录模式&#xff1f; 在软件构建过程中&#xff0c;某些对象…