C/C++图形化编程——EasyX使用教程

主打一个清晰易懂教程:

原版移步:EasyX完整版基础教程【语雀文档】

目录

1.创建图形化窗口

a. 包含头文件

ⅰ. 两种头文件:

ⅱ. 几种窗口函数:

1. 打开:initgraph(int x,int y,int style);

2. 关闭窗口函数:closegraph()关闭图形库(窗口有打开就有关闭)

3. 清空绘图函数:cleardevice()

ⅲ. 窗口坐标的知识:

ⅳ. 绘制窗口例子:

2. 基本绘图函数

a. 设置填充样式函数

ⅰ. 以画圆为例

可视化例子:

b. 设置图形样式函数

ⅰ. 代码实现示例:

c. 设置颜色函数

d. 文字绘制函数

3. 图像处理函数

4. 鼠标消息函数

a. 接收鼠标消息代码示例1

b. 接收鼠标消息代码示例2

5. 鼠标画线操作

6. 音乐播放

a. 播放音乐的函数

7. 阻塞式和非阻塞式

a. 阻塞式(Blocking):

b. 非阻塞式(Non-blocking):

c. 相关函数


1.创建图形化窗口

a. 包含头文件

ⅰ. 两种头文件:

graphics.h(包含已经淘汰的函数)

easy.h(只包含最新的)

ⅱ. 几种窗口函数:

1. 打开:initgraph(int x,int y,int style);

initgraph函数的参数的解释:

  • x:窗口宽度,单位是像素
  • y:窗口高度,单位是像素
  • style:窗口样式,以下是常见窗口样式:
  • DEFAULT:默认样式,创建一个可关闭的窗口。
  • NOCLOSE:创建一个无法关闭的窗口,没有关闭按钮。
  • NOMINIMIZE:创建一个无法最小化的窗口,没有最小化按钮。
  • NOMAXIMIZE:创建一个无法最大化的窗口,没有最大化按钮。
  • NORESIZE:创建一个无法改变大小的窗口,没有调整大小的边框。
  • NOCAPTION:创建一个没有标题栏的窗口,没有标题栏和边框。
  • CUSTOMSTYLE:自定义样式,用户可以通过指定参数实现特定需求的窗口样式。

设置窗口的例子:

...
#include<graphics.h>
int main()
{
	initgraph(200, 200,NOCLOSE);//设置宽度为200,高度为200,NOCLOSE指无法关闭的窗口
	//以下语句暂不做解释
    getchar();
	closegraph();
	return 0;
}
...

运行截图:可以看到该窗口是无法关闭的。

2. 关闭窗口函数:closegraph()关闭图形库(窗口有打开就有关闭)
3. 清空绘图函数:cleardevice()

ⅲ. 窗口坐标的知识:

坐标系长这个样子:

确定一个矩形框,只需要给出左上角和右下角的坐标即可

ⅳ. 绘制窗口例子:

#include<stdio.h>
//1.包含图形库头文件,就能使用图形库函数
#include<graphics.h>
int main()
{
	initgraph(640, 480);	//2.初始化图形库,确定矩形窗口大小 
	line(100, 100, 200, 200);	//3.画图
    (此处画的是由(100,100)和(200,200)这两个点构成的连线)
	
    getchar();	//4.暂停,防止窗口闪退
	closegraph();	//5.关闭图形库(窗口有打开就有关闭)
	return 0;
}
  • 运行截图:

2. 基本绘图函数

简介:利用不同的函数绘制各种图形

a. 设置填充样式函数

绘图函数从填充样式分类可分为无填充、有边框填充、无边框三种。

ⅰ. 以画圆为例

  1. circle():无填充
  2. fillcircle():有边框填充
  3. solidcircle():无边框填充
可视化例子:

(无边框填充)↑ ↑(有边框填充)

b. 设置图形样式函数

  • 按所画的图形形状分类,有以下常用几类图形函数
  1. circle()画圆
  2. elipse()画椭圆
  3. pie()画扇形
  4. polygon()画多边形
  5. rectangle画矩形
  6. roundrect画圆角矩形
  7. line画线
  8. putpixel画点

ⅰ. 代码实现示例:

问:如何画?怎么写代码?

答:以画圆为例:

根据数学知识,确定一个圆只需圆心坐标+半径即可:

所以输入的三个参数依次为:

  1. 圆心的x坐标(x)
  2. 圆心的y坐标(y)
  3. 圆的半径(radius)

这里以圆心坐标(500,500),半径为200,为例子。

  • 运行结果截图:

c. 设置颜色函数

  1. 设置填充颜色函数:setfillcolor(颜色)
  2. 设置线条颜色函数:setlinecolor(颜色)
  3. 设置线条样式函数:setlinestyle(线型, 宽度)

常见的线条样式:

  • PS_SOLID:实线型
  • PS_DASH:短划线型
  • PS_DOT:点线型
  • PS_DASHDOT:短划线加点线型
  • PS_DASHDOTDOT:短划线加两个点线型
  • PS_NULL:不画线,只移动当前点
setlinestyle(PS_SOLID,5);//设置线条样式:实线,宽度5
setfillcolor(YELLOW);//设置填充颜色:黄色
setlinecolor(BLUE);//设置线条颜色:蓝色

 4.设置背景颜色函数:setbkcolor(颜色);

  • 注意:设置背景颜色通常需要两步,简称两部曲
initgragh(200,200);设置窗口大小
setbkcolor(WHITE);//第一步:设置颜色
cleardevice();//第二步:清屏

注:该文章中,为了表达简洁,一般不展示头文件等其他内容,只展示重要代码

  • 运行截图:

d. 文字绘制函数

  1. 输出字符串函数:outtextxy(int x, int y, LPCTSTR str);

作用:在指定位置输出字符串

参数解释:(x,y)是坐标,str决定文本内容

例子:outtextxy(50,50,'a');//在(50,50)位置输出字符a

若改为outtextxy(50,50,"asarfewf");即把单个字符换为字符串,可能出现错误,以下是解决方案:

  1. 在字符串前面加上大写 L
  2. 用TEXT( )或_T( )把字符串括住,原理同上
  3. 不需要添加任何代码,进项目→属性→配置属性→常规→项目默认值→字符集→改为多字节字符集(推荐使用这个方式)

2.设置文字样式函数:settextstyle(int font, int direction, int charsize)

参数解释:

  1. font: 这个参数是一个整数,代表要使用的字体类型
  2. direction: 这个参数是一个整数,用于指定文本的方向

通常,0 表示水平方向(HORIZ_DIR),1 表示垂直方向(VERT_DIR)

     3.charsize: 这个参数是一个字符串,用于指定字体的大小

settextstyle(50,0,20)//表示字体类型为编号为50,方向水平,大小20

3.设置文字背景模式:setbkmode(int mode);

参数解释:

参数mode可以是以下两个值之一:

  1. TRANSPARENT:表示文本绘制时背景是透明的,即文本绘制时不修改背景。这意味着文本绘制的地方保持原来的背景,不会被覆盖。
  2. OPAQUE:表示文本绘制时背景是不透明的,即文本绘制时会先用当前背景色填充背景区域,然后在其上绘制文本。
/*...*/
setbkmode(TRANSPARENT);//设置文字背景为透明
outtextxy(50,50,"dsadd");//在(50,50)位置输出dsadd字样
/*...*/

 4.设置文字颜色函数:settextcolor(COLORREF color);

通常使用 RGB(r, g, b) 宏来创建颜色值,其中 r、g、b 分别代表红色、绿色和蓝色的分量。

例如,如果你想要将文本颜色设置为红色,可以这样调用settextcolor() 函数:

settextcolor(RGB(255, 0, 0)); // 红色

  • RGB配置指颜色调色板中三原色对应的数值:

5.获取文本宽和高函数:textheight(LPCTSTR str) textwidth(LPCTSTR str)

char arr[]= "我是字符串";
textheight(arr);//获取字符串宽度
textwidth(arr);//获取字符串高度

3. 图像处理函数

在使用图像之前,需要定义一个对象,然后把图片加载进变量才能使用。

  • 在使用图片的时候需要EasyX提供给我们的类型:IMAGE ,如IMAGE img;
  1. 图像加载函数:loadimage(IMAGE *img, LPCTSTR filename, int width, int height, BOOL shared = FALSE);
int loadimage(
    IMAGE *img,           // 指向 IMAGE 结构体的指针,用于存储加载的图像数据
    LPCTSTR filename,     // 指定图片文件的路径
    int width,            // 指定图片的宽度
    int height,           // 指定图片的高度
    BOOL shared = FALSE  // 指定是否加载为共享图像,默认为 FALSE
);

2.图像输出函数:BOOL putimage( HDC hdcDest, int xDest, int yDest, constvoid *img, DWORD rop = SRCCOPY )

BOOL putimage(
    HDC hdcDest,         // 目标设备上下文句柄
    int xDest,           // 目标矩形区域左上角的 x 坐标
    int yDest,           // 目标矩形区域左上角的 y 坐标
    const void *img,     // 要输出的图像数据
    DWORD rop = SRCCOPY  // 光栅操作码,指定如何将图像数据输出到目标设备上下文
);

加载并输出一张图片示例:

//实例化一个img对象
IMAGE img;
//加载图片
//相对路径:"./"表示当前文件夹,"../"表示当前文件夹的上一级文件夹,比如:./图片名.png 
//绝对路径:比如:D:\\biancheng\\Project31\\图片名.png,得用双反斜杠
loadimage(&img,"./图片.png");//以相对路径加载一张图片
//loadimage(&img,"D:\\biancheng\\Project31\\图片名.png");以绝对路径加载一张图片
putimage(0,0,&img);//从窗口的(0,0)位置输出图片

4. 鼠标消息函数

//包含在easyx.h头文件中
// Message Structure
struct ExMessage
{
	USHORT message;	// 用于区分鼠标消息
	union
	{
		// Data of the mouse message
		struct
		{
			bool ctrl		:1;		// 指示CTRL键是否按下
			bool shift		:1;		// 指示SHIFT键是否按下
			bool lbutton	:1;		// 指示鼠标左键是否按下
			bool mbutton	:1;		// 指示鼠标中键是否按下
			bool rbutton	:1;		// 指示鼠标右键是否按下
			short x;				// 鼠标光标的y的坐标
			short y;				// 鼠标光标的x的坐标
			short wheel;			// The distance the wheel is rotated, expressed in multiples or divisions of 120
		};
};
  • 常见鼠标消息宏定义:

若想了解更多的关于鼠标消息的定义,可以鼠标右键点击该定义→转到定义,即可。

#define WM_MOUSEMOVE       //鼠标移动消息            
#define WM_LBUTTONDOWN      //鼠标左键按下消息    
#define WM_LBUTTONUP        //鼠标左键弹起消息        
#define WM_LBUTTONDBLCLK     // 鼠标左键双击消息         
#define WM_RBUTTONDOWN       //鼠标左键按下消息            
#define WM_RBUTTONUP       //鼠标左键弹起消息              
#define WM_RBUTTONDBLCLK    //鼠标右键双击消息            
#define WM_MBUTTONDOWN    //鼠标中键按下消息               
#define WM_MBUTTONUP     //鼠标中键弹起消息                
#define WM_MBUTTONDBLCLK  //鼠标中键双击消息  

一般记住以下四种就行了,其他不会了可以速查:

WM_MOUSEMOVE: m.message== WM_MOUSEMOVE; //鼠标移动
WM_LBUTTONDOWN: 左键按下
WM_RBUTTONDOWN: 右键按下
WM_LBUTTONUP: 左键弹起

a. 接收鼠标消息代码示例1

程序功能介绍:若点击左键,会在控制台打印出“左键按键......”;若点击右键,会在控制台打印出“右键按键......”

#include<stdio.h>
#include<easyx.h>
#include<stdbool.h>
int main() {
    ExMessage m; // 定义一个ExMessage类型的变量m,用于存储消息
    initgraph(640, 480, 1); // 初始化图形窗口,设置窗口大小为640x480像素,1表示使用默认模式

    while (1) // 进入无限循环
    {
        peekmessage(&m, EM_MOUSE); // 检查是否有鼠标消息,将消息存储在m中

        if (m.message == WM_LBUTTONDOWN) // 如果检测到鼠标左键按下消息
        {
            printf("左键按键......\n"); // 输出提示信息:左键按键
        }
        else if (m.message == WM_RBUTTONDOWN) // 如果检测到鼠标右键按下消息
        {
            printf("右键按键......\n"); // 输出提示信息:右键按键
        }
    }

    return 0;
}

b. 接收鼠标消息代码示例2

程序介绍:这个程序不显示控制台,若在淡蓝色窗口中点击左键,会立即在鼠标点击位置绘制出半径为10的红色的圆;若在窗口中点击右键,会立即在鼠标点击位置绘制出半径为10的黑色的圆;

在C++的easyx库中,当使用`flag = 1`来设置全屏模式时,控制台不会显示。全屏模式下,窗口会占据整个屏幕,控制台将不可见。

  • 以下是一些常见的flag参数取值及其含义:
  • flag = 0: 普通窗口模式,窗口有标题栏和边框。
  • flag = 1: 全屏模式,窗口占据整个屏幕,没有标题栏和边框。
  • flag = 2: 窗口模式,窗口有标题栏但没有边框。
  • flag = 3: 最大化窗口模式,窗口占据整个屏幕但有标题栏。
#include<easyx.h>
#include<stdbool.h>
int main() {
    
    ExMessage m; // 定义一个ExMessage类型的变量m,用于存储消息
    initgraph(640, 480); // 初始化图形窗口,设置窗口大小为640x480像素,1表示使用默认模式
    setbkcolor(LIGHTBLUE); // 设置背景颜色为白色
	cleardevice(); // 清空屏幕
    while (1) // 进入无限循环
    {
        peekmessage(&m, EM_MOUSE); // 检查是否有鼠标消息,将消息存储在m中

        if (m.message == WM_LBUTTONDOWN) // 如果检测到鼠标左键按下消息
        {
            setfillcolor(RED); // 设置填充颜色为红色
            solidcircle(m.x, m.y, 10); // 绘制一个实心圆,圆心坐标为m.x, m.y,半径为10
        }
        else if (m.message == WM_RBUTTONDOWN) // 如果检测到鼠标右键按下消息
        {
            setfillcolor(BLACK);// 设置填充颜色为黑色
            solidcircle(m.x, m.y, 10);// 绘制一个实心圆,圆心坐标为m.x, m.y,半径为10
        }
         if (m.message == WM_MOUSEMOVE)
        {
             line(0, 0, m.x, m.y);
        }
    }
    closegraph();
    return 0;
}

知识拓展:hook(钩子)是什么?

编程中的“hook”指的是一种机制,允许你拦截和修改系统或应用程序的行为。通过使用钩子,开发人员可以在程序执行流程的特定点注入自己的代码,以执行额外的操作、改变程序的行为或响应事件。钩子通常以函数指针、回调函数、事件监听器或框架或库提供的特定接口的形式实现。在软件开发中,钩子经常用于事件驱动编程、图形用户界面、Web开发(例如React钩子)以及其他需要定制和扩展性的场景中。

比如:自动鼠标点击软件通常会使用钩子来实现自动化点击功能。通过钩子,这类软件可以拦截鼠标事件并模拟鼠标点击操作,从而实现自动点击的功能。钩子在这种情况下被用来监视和干预鼠标事件,以便程序能够自动执行点击操作。

5. 鼠标画线操作

  • 画图功能:(定时器逻辑)
  1. 画图过程可拆解为两个步骤:鼠标点击+鼠标拖动→画线
  2. 停止画线的逻辑:即鼠标停止拖动(不松开),并且再拖动可以继续画

做一个画图工具,要求包含以上逻辑示例:

#include<iostream> // 引入基本输入输出流
#include<graphics.h> // 引入图形库头文件(可能是指EasyX或其他图形库)
#include<stdio.h> // 引入标准输入输出库
#include<stdlib.h> // 引入标准库,用于内存分配等
#include<conio.h> // 引入控制台输入输出库
#include<easyx.h> // 引入EasyX图形库头文件
#include<stdbool.h> // 引入标准布尔库
#include<assert.h>//包含assert.h以使用assert宏

struct Point    // 定义一个结构体,用于存储点的坐标
{
    int x; // 横坐标
    int y; // 纵坐标
};

void setPoint(struct Point* point, int x, int y) // 设置点的坐标函数
{
    point->x = x; // 设置横坐标
    point->y = y; // 设置纵坐标
}

struct LineTool  // 定义一个画线工具的结构体
{
    int size; // 线的大小(粗细)
    COLORREF color; // 线的颜色
    bool isDown;  // 标记鼠标是否按下
    Point begin;    // 起始点坐标
};

// 创建画线工具的函数
struct LineTool* creatLineTool(int size, COLORREF color)
{
    struct LineTool* pLine = (struct LineTool*)malloc(sizeof(struct LineTool)); // 分配内存
    assert(pLine); // 确保内存分配成功
    pLine->size = size; // 设置线的大小
    pLine->color = color; // 设置线的颜色
    pLine->isDown = false; // 初始化鼠标未按下
    return pLine; // 返回工具指针
}

// 设置线的颜色
void setLineColor(struct LineTool* pLine, COLORREF color)
{
    pLine->color = color;
}

// 设置线的粗细
void setLineSize(struct LineTool* pLine, int size)
{
    pLine->size = size;
}

// 根据鼠标事件绘制线条的函数
void drawLine(struct LineTool* pLine, ExMessage m)
{
    // 鼠标左键按下,记录位置
    if (m.message == WM_LBUTTONDOWN)
    {
        pLine->isDown = true;
        pLine->begin.x = m.x;
        pLine->begin.y = m.y;
    }
    // 鼠标左键弹起
    if (m.message == WM_LBUTTONUP)
    {
        pLine->isDown = false;
    }
    // 鼠标移动且左键按下,绘制线条
    if (pLine->isDown == true && m.message == WM_MOUSEMOVE)
    {
        setlinestyle(PS_ENDCAP_ROUND, pLine->size); // 设置线条样式和粗细
        setlinecolor(pLine->color); // 设置线条颜色
        line(pLine->begin.x, pLine->begin.y, m.x, m.y); // 绘制线条
        // 更新起始点坐标,以便连续绘制
        pLine->begin.x = m.x;
        pLine->begin.y = m.y;
    }
    // 鼠标右键按下,清空画布
    if (m.message == WM_RBUTTONDOWN)
    {
        setbkcolor(WHITE); // 设置背景色为白色
        cleardevice(); // 清空画布
    }
}

int main() {
    initgraph(800, 800); // 初始化图形窗口大小为800x800
    setbkcolor(WHITE); // 设置背景色为白色
    struct LineTool* pLine = creatLineTool(5, BLACK); // 创建一个线条工具,线条为黑色,粗细为5
    ExMessage m; // 定义一个消息结构体,用于接收鼠标消息
    cleardevice(); // 清空画布
    while (1) // 无限循环
    {
        peekmessage(&m, EM_MOUSE); // 获取鼠标消息
        drawLine(pLine, m); // 根据鼠标消息绘制线条
         if (_kbhit())//判断是否存在按键,存在按键则按键操作 返回非零值表示存在
 {
     char userKey = _getch();//阻塞函数
     switch (userKey)
     {
     case'+':
         //setLineSize(pLine,pLine->size++);
         pLine->size++;
         break;
     case'-':
         break;
     case'\r':
         exit(0);
         break;
     }
    }
    closegraph(); // 关闭图形窗口
    return 0;
}

6. 音乐播放

● #include<windows.h> //如果包含这个graphics.h,windows.h可以不包含
● 包含多媒体库:#include<mmsystem.h> 
● 包含静态库资源:#pragma comment("lib","winmm.lib") 

a. 播放音乐的函数

mciSendString("指令",0,0,0);(只支持MP3)

指令:open:打开 pause:暂停 resume:继续 close:关闭

  • play:播放:repeat /wait方式
  • 函数原型:
// 函数原型
DWORD mciSendString(
  LPCTSTR  lpszCommand,  // 指向以 NULL 结尾的字符串,包含要发送的命令
  LPTSTR   lpszReturnString, // 接收命令执行的结果的缓冲区
  UINT     cchReturn,   // 指定 lpszReturnString 缓冲区的大小
  HANDLE   hwndCallback // 指定用于接收通知消息的窗口句柄,通常为 NULL
);

mciSendString函数的第一个参数是LPCWSTR类型,它是一个指向常量宽字符的指针

  • 示例用法:做一个简单音乐播放程序
#include <Windows.h> // 包含 Windows.h 头文件,提供 Windows 平台下的 API 支持
#include <stdio.h> // 包含标准输入输出库头文件
#include <mmsystem.h> // 包含多媒体系统 API 头文件
#pragma comment(lib,"winmm.lib") // 指定链接到 winmm.lib 库

void makeMenu() // 定义函数 makeMenu,用于打印菜单
{
    printf("-------[音乐播放]-------\n"); // 打印菜单标题
    printf("\t\t0.退出\n"); // 打印退出选项
    printf("\t\t1.播放音乐\n"); // 打印播放音乐选项
    printf("\t\t2.暂停音乐\n"); // 打印暂停音乐选项
    printf("\t\t3.继续音乐\n"); // 打印继续音乐选项
    printf("\t\t4.关闭音乐\n"); // 打印关闭音乐选项
    printf("-------------------\n"); // 打印分隔线
}

void keyDown() // 定义函数 keyDown,用于处理用户输入并执行相应操作
{
    int userkey = 0; // 定义变量 userkey 存储用户输入
    scanf_s("%d", &userkey); // 读取用户输入
    switch (userkey) // 根据用户输入执行相应操作
    {
    case 0: // 用户输入为 0,退出程序
        exit(0);
    case 1: // 用户输入为 1,播放音乐
        mciSendString(L"play ./music/1.mp3 repeat", NULL, 0, NULL); // 播放音乐文件,并设置为循环播放
        break;
    case 2: // 用户输入为 2,暂停音乐
        mciSendString(L"pause ./music/1.mp3 ", NULL, 0, NULL); // 暂停音乐播放
        break;
    case 3: // 用户输入为 3,继续音乐
        mciSendString(L"resume ./music/1.mp3 ", NULL, 0, NULL); // 继续音乐播放
        break;
    case 4: // 用户输入为 4,关闭音乐
        mciSendString(L"close ./music/1.mp3 ", NULL, 0, NULL); // 关闭音乐文件
        break;
    }
}

int main() // 主函数
{
    mciSendString(L"open ./music/1.mp3", NULL, 0, NULL); // 打开音乐文件
    while (1) // 进入主循环,持续显示菜单并处理用户输入
    {
        makeMenu(); // 显示菜单
        keyDown(); // 处理用户输入
    }
}

7. 阻塞式和非阻塞式

a. 阻塞式(Blocking):

    • 在阻塞式事件处理中,程序会一直等待某个事件的发生,直到该事件发生后才会继续执行后续代码。
    • 例如,在游戏中,如果采用阻塞式事件处理,程序会等待玩家的按键输入,直到玩家按下某个键后才会响应并继续执行游戏逻辑。
    • 在 EasyX 中,常见的阻塞式事件处理函数是 GetMouseMsg() 和 GetKey(),它们会一直等待鼠标消息和键盘消息的到来。

b. 非阻塞式(Non-blocking):

    • 在非阻塞式事件处理中,程序会定期地轮询或检查事件是否发生,如果发生了则立即处理,如果没有则继续执行后续代码。
    • 例如,在游戏中,如果采用非阻塞式事件处理,程序会定期检查玩家是否按下了某个键,如果按下了则立即响应,如果没有则继续执行游戏逻辑。
    • 在 EasyX 中,可以通过定时器来实现非阻塞式事件处理,比如使用 SetTimer() 设置一个定时器,然后在定时器回调函数中处理事件。

c. 相关函数

1.使用 kbhit() 等待键盘输入:

while (!kbhit()) {
    // 等待键盘输入事件发生
}


这段代码会使程序进入一个循环,直到键盘输入事件发生(用户按下键盘上的任意键)才会跳出循环继续执行后续代码。

2. 使用 GetMouseMsg() 等待鼠标消息:

MOUSEMSG m;
while (!MouseHit()) {
    m = GetMouseMsg(); // 获取鼠标消息
    // 处理鼠标消息
}


这段代码会不断地获取鼠标消息,直到有鼠标消息到达才会跳出循环继续执行后续代码。

3.使用 GetKey() 等待键盘按键消息:

while (!kbhit()) {
    // 等待键盘按键消息发生
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值