QT中使用opengl

.pro文件中添加

QT += opengl

1、使用指定版本的OpenGL
如下使用opengl4.5调用方法,使用指定版本的接口,必须设备图形显示设备支持对应OpenGL版本才可。
Q:什么是CoreProfile和Compatibility Profile?
A:在OpenGL的发展历程中,总是兼顾向下兼容的特性,但是到了一定的程度之后,这些旧有的OpenGLAPI不再适应时代的需要,还有一些扩展并不是驱动一定要实现的扩展,这些被统一划入可选的CompatibilityProfile;而由OpenGL规范规定必须支持的扩展,则是Core Profile,想要支持先进的OpenGL,相应的CoreProfile扩展必须被实现。所以如果使用#include < QOpenGLFunctions > 或者 #include <QOpenGLFunctions_4_5_Core>时,会发现如glDrawPixels等接口找不到的问题。所以我们使用Cmpatibility版本。所有opengl的接口函数都可以使用。

 #include <QOpenGLFunctions_4_5_Cmpatibility>
//QOpenGLFunctions类提供了跨平台访问的OpenGL ES 2.0 API,QOpenGLFunctions提供了一个在所有OpenGL系统上都可用的保证API,并在需要它的系统上负责功能解析。使用QOpenGLFunctions的推荐方法是直接继承:
class Widget : public QOpenGLWidget, protected QOpenGLFunctions_4_5_Cmpatibility
{
public:
Widget(QWidget *parent = );
~Widget();
void initializeGL(); ///< 初始化
void resizeGL(int w, int h); ///< 当窗口发生变化时重新初始化
void paintGL(); ///< 绘制
}
 void Widget::initializeGL()
{
//获取上下文
//QOpenGLFunctions_4_5_Compatibility* compatibility= QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Compatibility>();
//compatibility->initializeOpenGLFunctions(); /* 0. 初始化函数,使得函数可以使用 */
initializeOpenGLFunctions(); const GLubyte* name = glGetString(GL_VENDOR); //返回负责当前OpenGL实现厂商的名字
const GLubyte* biaoshifu = glGetString(GL_RENDERER); //返回一个渲染器标识符,通常是个硬件平台
const GLubyte* OpenGLVersion =glGetString(GL_VERSION); //返回当前OpenGL实现的版本号
// const GLubyte* OpenGLExensions =glGetString(GL_EXTENSIONS); // QString str;
str.sprintf("%s | %s | %s",name,biaoshifu,OpenGLVersion); qDebug()<<str;
}

2、使用QOpenGLFunctions
QOpenGLFunctions类提供跨平台访问的OpenGL ES 2.0 API,QOpenGLFunctions提供了一个在所有OpenGL系统上都可用的保证API, 并在需要它的系统上负责功能解析。使用QOpenGLFunctions的推荐方法是直接继承,同时在初始化函数中void initializeGL() 调用此接口initializeOpenGLFunctions() 进行初始化。如下:
OpenGL ES相对OpenGL删减了一切低效能的操作方式,有高性能的决不留低效能的,即只求效能不求兼容性(和苹果的作风类似)。也就是说很多opengl函数无法使用,如glDrawPixels等。
典型:
1.没有double型数据类型,但加入了高性能的定点小数数据类型。
2.没有glBegin/glEnd/glVertex,只能用glDrawArrays/glDraw…
3.没有实时将非压缩图片数据转成压缩贴图的功能,程序必须直接提供压缩好的贴图
数据类型:
1: i GLint 整数型
2: f GLfixed 定点小数
3: x GLclampx 限定型定点小数
删除的功能:
1.glBegin/glEnd
2.glArrayElement
3.显示列表
4.求值器
5.索引色模式
6.自定义裁剪平面
7.glRect
8.图像处理(这个一般显卡也没有,FireGL/Quadro显卡有)
9.反馈缓冲
10.选择缓冲
11.累积缓冲
12.边界标志
13.glPolygonMode
14.GL_QUADS,GL_QUAD_STRIP,GL_POLYGON
15.glPushAttrib,glPopAttrib,glPushClientAttrib,glPopClientAttrib
15.TEXTURE_1D、TEXTURE_3D、TEXTURE_RECT、TEXTURE_CUBE_MAP
16.GL_COMBINE
17.自动纹理坐标生成
18.纹理边界
19.GL_CLAMP、GL_CLAMP_TO_BORDER
20.消失纹理代表
21.纹理LOD限定
22.纹理偏好限定
23.纹理自动压缩、解压缩
24.glDrawPixels,glPixelTransfer,glPixelZoom
25.glReadBuffer,glDrawBuffer,glCopyPixels
其它注意事项:
1.glDrawArrays等函数中数据必须紧密排列,即间隔为0
2.各种数据的堆栈深度较低
参考代码:

 #ifndef WIDGET_H
#define WIDGET_H #include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram> //QOpenGLFunctions类提供了跨平台访问的OpenGL ES 2.0 API,QOpenGLFunctions提供了一个在所有OpenGL系统上都可用的保证API,并在需要它的系统上负责功能解析。使用QOpenGLFunctions的推荐方法是直接继承:
class Widget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
Widget(QWidget *parent = );
~Widget();
void initializeGL(); ///< 初始化
void resizeGL(int w, int h); ///< 当窗口发生变化时重新初始化
void paintGL(); ///< 绘制 void initVbo(); ///< 初始化Vbo
void loadTextures(); ///< 加载纹理
void keyPressEvent(QKeyEvent * e); ///< 键盘事件
private:
/* [1] 需要定点着色器和片段着色器,不然做不了任何渲染 */
/* 这里定义了一个着色器[顶点着色器、片段着色器]编译对象 */
QOpenGLShaderProgram * program;
///< 可以根据此id,利用glGetUniformLocation等方法获取shader里面的属性
GLuint programid; ///< 其实还有视图矩阵、投影矩阵、MVP矩阵,这里简单的用下,不区分特别细!
///< 分三个这样的矩阵,分别是模型矩阵、视图矩阵、透视矩阵,这样以后灵活性更强:
/// 1. 比如单独控制模型灯光跟随,shader可能需要传入除了mvp矩阵外的模型矩阵*视图矩阵这样一个矩阵
QMatrix4x4 m_projection; ///< 矩阵、顶点、颜色在着色器里面的位置
GLuint matrixLocation, vertexLocation, textureLocation,
samplerLocation; ///< 顶点、索引、颜色->buffer的标识
GLuint verVbo, v_indexVbo, textureVbo;
GLuint texture; int vVerticesLen; ///< 顶点数组长度
int tri_indexLen; ///< 索引数组长度
int textureCoordLen;///< 纹理坐标数组长度
}; #endif // WIDGET_H
 #include "widget.h"
#include<QKeyEvent>
#include<QGLWidget>
#include<QOpenGLFunctions_3_2_Core> Widget::Widget(QWidget *parent) : QOpenGLWidget(parent)
{
///< 官方文档有这样设置,具体还没细细看,但是意思吧,就是告诉渲染属性,使用版本等;有空可以深入研究,光看效果不一定能看出什么区别!
///< 这个有些是放到main.cpp中去了,通过new widget.setFormat(format)那样去设置,不清楚...我觉得本质是一样,都是给该widget设置属性。
// QSurfaceFormat format;
// format.setDepthBufferSize(24);
// format.setStencilBufferSize(8);
// format.setVersion(3, 2);
// format.setProfile(QSurfaceFormat::CoreProfile);
// setFormat(format);
} Widget::~Widget()
{
glDeleteBuffers(, &verVbo);
glDeleteBuffers(, &v_indexVbo);
glDeleteProgram(programid);
glDeleteTextures(, &texture);
} /* 1.1 着色器代码 */
/* *********************************************
* 顶点着色器定义一个输入,它是 4 个成员的矢量 vPosition。
* 主函数声明着色器宣布着色器开始执行。着色器主体非常简单,
* 它复制输入 vPosition 属性到 gl_Position 输出变量中。
* 每个顶点着色器必须输出位置值到 gl_Position 变量中,
* 这个变量传入到管线的下一个阶段中。
* matrix主要是模型视图矩阵,控制位置和旋转等
* ******************************************** */
/* 顶点着色器 */
static const char *vertexShaderSourceCore =
"attribute vec4 vPosition;\n"
"uniform highp mat4 matrix;\n"
"attribute vec2 TexCoord;\n"
"varying vec2 TexCoord0;\n"
"void main() {\n"
" TexCoord0 = TexCoord;\n"
" gl_Position = matrix * vPosition;\n"
"}\n"; /* *********************************************
* gl_FragColor,gl_FragColor是片段着色器最终的输出值,
* 本例中输出值来自外部传入的颜色数组。
* ******************************************** */ /* 片段着色器 */
static const char *fragmentShaderSourceCore =
"varying vec2 TexCoord0;\n"
"uniform sampler2D gSampler;\n"
"void main() {\n"
" gl_FragColor = texture2D(gSampler, TexCoord0.st);\n"
"}\n"; ///* 2.1 三角形顶点的坐标 */
//GLfloat vVertices[] = {0.0f, 0.5f, 0.0f,
// -0.5f, -0.5f, 0.0f,
// 0.5f, -0.5f, 0.0f};
///* 2.2 三角形顶点的索引 */
//GLuint tri_index[] = {0, 1, 2};
///* 2.3 顶点颜色数组 */
//GLfloat colors[] = {1.0f, 0.0f, 0.0f,0.5f,
// 0.0f, 1.0f, 0.0f,0.5f,
// 0.0f, 0.0f, 1.0f,0.5f}; /* 2.1 正方体顶点的坐标 */
GLfloat vVertices[] = {-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f};
/* 2.2 正方体顶点的索引 */
GLuint tri_index[] = {, , ,
, , ,
, , ,
, , ,
, , ,
, , ,
, , ,
, , ,
, , ,
, , ,
, , ,
, , }; ///< 纹理点 6个面 每个面四个纹理坐标映射???好像不对呀,绘制出来花花的...
///< 还得好好思考下纹理坐标和现在的顶点索引坐标如何对应!!!
/// 下面这个先注释掉,上面的说法:六个面,每个面四个纹理坐标映射好像不对....
//float texCoords[] =
//{
// 0.0f, 0.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 1.0f, 1.0f, // 0.0f, 0.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 1.0f, 1.0f, // 0.0f, 0.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 1.0f, 1.0f,
// 0.0f, 0.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 1.0f, 1.0f,
// 0.0f, 0.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 1.0f, 1.0f,
// 0.0f, 0.0f,
// 1.0f, 0.0f,
// 0.0f, 1.0f,
// 1.0f, 1.0f
//};
///< 我们就来手动修改下,直到不花为止,然后回过头去细细分析下,具体原因?
/// 这样学习起来更快,毕竟现有感觉,带着感觉去更加有激情和感悟...
/// 不过按照自己认为的坐标去对应,始终不对(上下两面还是花)...哎,缓一缓,先仔细研究下再回过头来修改...
float texCoords[] =
{
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f
}; /**
* @brief 初始化模型信息vbo【显存】
*/
void Widget::initVbo()
{
///< 计算获得数组长度,之后会用到该变量,这样只需要改动这里即可!如果用链表,直接.size()即可求出!
vVerticesLen = sizeof(vVertices)/sizeof(GLfloat);
tri_indexLen = sizeof(tri_index)/sizeof(GLuint);
textureCoordLen = sizeof(texCoords)/sizeof(GLfloat); qDebug() << vVerticesLen;
qDebug() << tri_indexLen; ///< 初始化顶点buffer并装载数据到显存
glGenBuffers(1, &verVbo);
glBindBuffer(GL_ARRAY_BUFFER, verVbo);
glBufferData(GL_ARRAY_BUFFER, vVerticesLen * sizeof(GLfloat), vVertices, GL_STATIC_DRAW); ///< 初始化索引buffer并装载数据到显存
glGenBuffers(1, &v_indexVbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, v_indexVbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, tri_indexLen * sizeof(GLuint), tri_index, GL_STATIC_DRAW); ///< 初始化纹理坐标buffer并装载到显存
glGenBuffers(1, &textureVbo);
glBindBuffer(GL_ARRAY_BUFFER, textureVbo);
glBufferData(GL_ARRAY_BUFFER, textureCoordLen * sizeof(GLfloat), texCoords, GL_STATIC_DRAW);
} /**
* @brief 装载纹理,具体纹理知识可以参考网友资料:
* http://www.cnblogs.com/tornadomeet/archive/2012/08/24/2654719.html
*/
void Widget::loadTextures()
{
QImage tex, buf;
if (!buf.load("../2012082420060914.jpg"))
{
qWarning("annot open the image...");
QImage dummy(, , QImage::Format_RGB32);
dummy.fill(Qt::green);
buf = dummy;
} ///< 转换为OpenGL支持的格式
tex = QGLWidget::convertToGLFormat(buf); ///< 开辟一个纹理内存,内存指向texture
glGenTextures(1, &texture);
///< 将创建的纹理内存指向的内容绑定到纹理对象GL_TEXTURE_2D上,
/// 经过这句代码后,以后对GL_TEXTURE_2D的操作的任何操作都同时对应与它所绑定的纹理对象
glBindTexture(GL_TEXTURE_2D, texture);
///< 开始真正创建纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(),
, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits()); ///< 当所显示的纹理比加载进来的纹理小时,采用GL_LINEAR的方法来处理
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
///< 当所显示的纹理比加载进来的纹理大时,采用GL_LINEAR的方法来处理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} void Widget::initializeGL()
{
qDebug("+++ initializeGL +++");
/* 0. 初始化函数,使得函数可以使用 */
initializeOpenGLFunctions(); /* 创建项目对象链接着色器 */
/* 1. 初始化最大的任务是装载顶点和片段着色器 */
program = new QOpenGLShaderProgram(this);
/* 一旦应用程序已经创建了顶点、片段着色器对象,
* 它需要去创建项目对象,项目是最终的链接对象,
* 每个着色器在被绘制前都应该联系到项目或者项目对象。
* ***************************************** */
/* 1.2 加载 */
if(!program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSourceCore))
{
return;
}
if(!program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSourceCore))
{
return;
} /* 1.3 设置属性位置,将vPosition属性设置为位置0, vertex为位置1
这里我就让程序自动分配,当然你也可以手动; 我在后面通过代码获取到了!
*/
//program->bindAttributeLocation("vertex", 1);
//program->bindAttributeLocation("vPosition", 0);
//program->bindAttributeLocation("a_color", 1);
//program->bindAttributeLocation("matrix", 2); /* 1.4 链接项目检查错误 */
if( !program->link() )
{
return;
} if( !program->bind() ){
return ;
} ///< 获取shaderprogram的id号,然后可以通过id号获取一些属性...
programid = program->programId(); ///< 从shaderprogram里面获取变量标识,总共用到两种方式,看你喜好!倾向第一种
matrixLocation = glGetUniformLocation(programid, "matrix");
vertexLocation = glGetAttribLocation(programid, "vPosition");
textureLocation = program->attributeLocation("TexCoord");
samplerLocation = program->uniformLocation("gSampler"); ///< 初始化vbo,对于实时变化的数据,可能需要在paintGL()里面每次调用!
initVbo(); ///< 装载纹理
loadTextures(); ///< 允许采用2D纹理技术
glEnable(GL_TEXTURE_2D);
///< 设置背景颜色
glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
///< 开启深度测试,避免颜色相互透过,具体需要自己深入学习的哦!
glEnable(GL_DEPTH_TEST);
///< 设置深度测试类型 - 不设置也会默认
glDepthFunc(GL_LEQUAL); ///< 这个地方先于resizeGL运行,所以这里设置无效!我一开始犯了这个错误!!!
//m_projection.translate(0.0f, 0.0f, -1.0f);
} void Widget::resizeGL(int w, int h)
{
/* 2.1 viewport 设定窗口的原点 origin (x, y)、宽度和高度 */
glViewport(, , w, h); ///< 模型矩阵重置
m_projection.setToIdentity();
///< 透视投影【做了简单容错】
qreal aspect = qreal(w) / qreal(h ? h : 1);
m_projection.perspective(60.0f, aspect, 1.0f, 100.0f);
///< 增加了模型矩阵,需要做一定偏移量,保证物体刚开始渲染出来时可以被看到!
m_projection.translate(0.0f, 0.0f, -2.0f);
} void Widget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ///< shader传入模型视图矩阵
glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, m_projection.data()); ///< shader绑定并启用顶点数组buffer
glBindBuffer(GL_ARRAY_BUFFER, verVbo);
glEnableVertexAttribArray(vertexLocation);
///< 顶点xyz坐标,所以每三个作为一个顶点值
glVertexAttribPointer( vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); ///< shader绑定并顶点索引数组buffer - 索引无需启用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, v_indexVbo); ///< 绑定纹理
glUniform1i(samplerLocation, 0);
glActiveTexture(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glBindBuffer(GL_ARRAY_BUFFER, textureVbo);
glEnableVertexAttribArray(textureLocation);
///< 2是标识两个float为一个纹理坐标,从varying vec2 TexCoord0也可以看出!
glVertexAttribPointer(textureLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); glDrawElements(GL_TRIANGLES, tri_indexLen, GL_UNSIGNED_INT, ); ///< 解绑buffer、关闭启用顶点、颜色数组、解绑纹理
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, );
glBindTexture(GL_TEXTURE_2D, );
glBindBuffer(GL_ARRAY_BUFFER, );
glDisableVertexAttribArray(textureLocation);
glDisableVertexAttribArray(vertexLocation);
} /**
* @brief 写了键盘监听事件,可以控制模型旋转,方便预览
* @param k
*/
void Widget::keyPressEvent(QKeyEvent * k)
{
qDebug("+++ keyPressEvent +++");
if(k->key() == Qt::Key_A)
{
m_projection.rotate(, , , );
}
else if(k->key() == Qt::Key_D)
{
m_projection.rotate(-, , , );
}
else if(k->key() == Qt::Key_W)
{
m_projection.rotate(, , , );
}
else if(k->key() == Qt::Key_S)
{
m_projection.rotate(-, , , );
}
update();
}
 #include "widget.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv); Widget widget();
widget.show(); return a.exec();
}

Qt 使用自带的OpenGL模块开发程序的更多相关文章

  1. Qt的Graphics-View框架和OpenGL结合详解

    Qt的Graphics-View框架和OpenGL结合详解 演示程序下载地址:这里 程序源代码下载地址:这里 这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果. Qt中有一个非常炫的例子:Boxe ...

  2. 学习基于OpenGL的CAD程序的开发计划(一)

    本人目前从事的工作面对的客户中很多来自高端制造业,他们对CAD/CAE/CAM软件的应用比较多.公司现有的软件产品主要是用于渲染展示及交互,但面对诸如CAD方面的应用(比如基于约束的装配.制造工艺的流 ...

  3. 程序员带你学习安卓开发,十天快速入-对比C#学习java语法

    关注今日头条-做全栈攻城狮,学代码也要读书,爱全栈,更爱生活.提供程序员技术及生活指导干货. 如果你真想学习,请评论学过的每篇文章,记录学习的痕迹. 请把所有教程文章中所提及的代码,最少敲写三遍,达到 ...

  4. Spring Boot 的Maven多模块开发web项目使用外部容器进行部署

    Spring Boot中自带有Tomcat容器,因此Spring Boot项目只需要运行main函数,就可以运行,但是以往的web项目,我们习惯于使用自己安装的Tomcat运行或者使用Tomcat.J ...

  5. QT + OpenCV + MinGW 在windows下配置开发环境

           由于研究项目需要,最近开始接触C++界面设计,关于“QT + OpenCV + MinGW在windows下配置开发环境”着实让人头疼,单次配置时间相当长,也十分不容易,本人第一次配置成 ...

  6. idea上使用maven模块开发

    使用maven模块开发: 使用Maven构建多模块项目 在平时的Javaweb项目开发中为了便于后期的维护,我们一般会进行分层开发,最常见的就是分为common(域模型层).dao(数据库访问层).s ...

  7. React Native Android原生模块开发实战|教程|心得|怎样创建React Native Android原生模块

    尊重版权,未经授权不得转载 本文出自:贾鹏辉的技术博客(http://blog.csdn.net/fengyuzhengfan/article/details/54691503) 告诉大家一个好消息. ...

  8. 《例说XBee无线模块开发》

    <例说XBee无线模块开发> 基本信息 原书名:The Hands-on XBee Lab Manual:Experiments that Teach you XBee Wireless ...

  9. C语言-apache mod(模块开发)-采用apxs开发实战(centos7.2 linux篇)

    C语言-apache mod(模块开发)-采用apxs开发实战(centos7.2 linux篇) 名词解释:apxs apxs is a tool for building and installi ...

随机推荐

  1. .net core excel导入导出

    做的上一个项目用的是vs2013,传统的 Mvc模式开发的,excel报表的导入导出都是那几段代码,已经习惯了. 导入:string filename = ExcelFileUpload.FileNa ...

  2. 2018.11.16javascript课上随笔(DOM)

    <li> <a href = "“#”>-</a> </li> <li>子节点:文本节点(回车),元素节点,文本节点. 不同节点树 ...

  3. F5之LTM入门(转)

    原文链接:https://kuaibao.qq.com/s/20180812G02WG200?refer=cp_1026文章来源:企鹅号 - 奕知伴解 什么是负载均衡? 服务器负载均衡器是指设置在一组 ...

  4. 更新anaconda包

    升级安装python环境后, 把老的包重新安装回去. ls -l /opt/anaconda3/lib/python3.7/site-packages/ | grep "\-info&quo ...

  5. Ubuntu Teamviewer安装使用

    关于Ubuntu环境下teamviewer的安装(亲测可用-) 以下内容均转自:https://blog.csdn.net/weixin_41887832/article/details/798329 ...

  6. sqlserver2008的sql语句支持的最大长度

    想写一个sql语句,很长,主要是in后跟着无数个用户ID,(虽然实现方式很低级,但是还是凑合着用吧) 不知道sql最大长度是多少,看了 SQL Server 的最大容量规范,写的是 包含 SQL 语句 ...

  7. Ansible-大保健

    一.Ansible大纲 Ansible被红帽收购 1.什么是Ansible 2.Ansible特性\优点 3.Ansible基础架构 控制端\被控端\inventory\ad-hoc\playbook ...

  8. 练习-HTML表单

    <html lang="en"> <head> <h1>大学生爱好调查</h1> <meta charset="ut ...

  9. LeetCode——623.在二叉树中增加一行

    给定一个二叉树,根节点为第1层,深度为 1.在其第 d 层追加一行值为 v 的节点. 添加规则:给定一个深度值 d (正整数),针对深度为 d-1 层的每一非空节点 N,为 N 创建两个值为 v 的左 ...

  10. 干货 | 玩转云文件存储——利用CFS实现web应用的共享访问

    京东云文件服务(Cloud File Service,以下简称:CFS)是一种高可靠.可扩展.可共享访问的全托管分布式文件系统.它可在不中断应用服务的情况下,根据您对文件系统的使用,按需扩展或缩减,并 ...