第五章 应用程序主窗口——Qt

一、主窗口框架

1.菜单栏和工具栏
(1)菜单栏

①Qt提供与菜单相关的类组件
请添加图片描述

②QMenuBar类、QMenu类和QAction类
  ●QMenuBar:QMenuBar类提供了一个水平的菜单栏,可以在此基础上添加不同的QMenu和QAction。一个主窗口只可以有一个菜单栏。
  ●QMenu:菜单栏里面菜单(如File、Edit、Format等菜单)或者带有三角形符号的菜单项(表示其下仍有子菜单),可以显示文本和图标,但是并不负责执行操作,有点类似label的作用。
  ●QAction: Qt 将用户与界面进行交互的元素抽象为一种“动作”,使用QAction类表示。QAction才是真正负责执行操作的部件。

联系:QMainWindow中可以直接获取它的默认存在的QMenuBar菜单栏对象,向其(QMenuBar菜单栏对象)添加QMenu类型的菜单对象,然后向弹出的菜单中添加QAction类型的动作对象。
代码:

QMenuBar* mb = menuBar(); //调用QMainWidow的成员函数menuBar  用该函数就可以得到QMainWindow里面的菜单栏对象即 主窗口里面的菜单栏对象
QMenu* menu = new QMenu("File(&F)");
QAction* action = new QAction("New",NULL);
					
menu->addAction(action); //将action加入菜单项中
					
mb->addMenu(menu);  //将菜单项加入MenuBar中

③代码方式添加菜单

// 添加编辑菜单
QMenu *editMenu = ui->menuBar->addMenu(tr("编辑(&E)"));
// 添加打开菜单
QAction *action_Open = editMenu->addAction(             
QIcon(:/images/open.png"),tr("打开文件(&O)"));
// 设置快捷键
action_Open->setShortcut(QKeySequence("Ctrl+O"));       
// 在工具栏中添加动作
 ui->mainToolBar->addAction(action_Open);     
(2)工具栏

工具栏QToolBar类提供了一个包含了一组控件的可以移动的面板。它默认只是显示一个动作的图标,这个可以在QToolBar的属性栏中进行更改。
在设计器中可以查看QToolBar的属性栏,其中toolButtonStyle属性设置图标和相应文本的显示及其相对位置等;movabel属性设置状态栏是否可以移动;
allowedArea设置允许停靠的位置;iconsize属性设置图标的大小;floatable属性设置是否可以悬浮。

例如:
	QToolButton *toolBtn = new QToolButton(this);        // 创建QToolButton
	toolBtn->setText(tr("颜色"));
	QMenu *colorMenu = new QMenu(this);                  // 创建一个菜单
	colorMenu->addAction(tr("红色"));
	colorMenu->addAction(tr("绿色"));
	toolBtn->setMenu(colorMenu);                         // 添加菜单
	toolBtn->setPopupMode(QToolButton::MenuButtonPopup); // 设置弹出模式
	ui->mainToolBar->addWidget(toolBtn);           // 向工具栏添加QToolButton按钮
	QSpinBox *spinBox = new QSpinBox(this);         // 创建QSpinBox
	ui->mainToolBar->addWidget(spinBox);            // 向工具栏添加QSpinBox部件		
2.使用资源系统

可以让需要的文件(图片、文本等)包含到程序编程生成的可执行文件(例如exe文件)中。保证了程序中使用的文件不会丢失、不会因为存放路径而导致程序运行错误。

添加资源系统的过程:
	在工程中添加资源文件:
		①在工程名处右击鼠标,选择“添加新文件”。
		②再将添加的文件类型选择为Qt资源文件。
		③接下来,为新添加的资源文件命名。
			注意,这个资源文件名不是你要添加的图片等资源的文件名,而是添加到工程中的资源文件名称(该文件用于管理随后要添加的真正的资源文件)。
		④然后指定将该资源文件添加到哪个工程。如果同时打开了多个工程,在此可以进行选择。如果只打开了一个工程,保持默认即可。
	在资源文件中添加资源:
		①首先,在工程中选中资源文件,然后点击下方添加按钮的下拉箭头,再点击“添加前缀”。
		②前缀的名称可以根据需要来取,一般建议指定为资源的类型,以便于区分。
		③添加前缀之后,我们就可以添加文件了。
		④找到要添加的资源文件,将它加入到工程中来。
	为资源文件取别名:
		要想对资源文件取别名,首先我们需要选中要取别名的文件,然后再为其取一个别名即可。
3.中心部件

在主窗口的中心区域可以放置一个中心部件,它一般是一个编辑器或者浏览器。这里支持单文档部件,也支持多文档部件。
一般的,会在这里放置一个部件,然后使用布局管理器使其充满整个中心区域,并可以随着窗口的大小变化而改变大小。

4.Dock部件

QDockWidget类提供了这样一个部件,它可以停靠在QMainWindow中也可以悬浮起来作为桌面顶级窗口,我们称它为Dock部件或者停靠窗口。
Dock部件一般用于存放其他一些部件来实现一些特殊功能,就像一个工具箱一样。它在主窗口中可以停靠在中心部件的四周,也可以悬浮起来,被拖动到任意的地方,还可以被关闭或隐藏起来。一个Dock部件包含一个标题栏和一个内容区域,可以向Dock部件中放入任何部件。

5.状态栏

QStatusBar类提供了一个水平条,用来显示状态信息。QMainWindow中默认提供了一个状态栏。
状态信息可以被分为三类:临时信息,如一般的提示信息;正常信息,如显示页数和行号;永久信息,如显示版本号或者日期。
可以使用showMessage()函数来显示一个临时消息,它会出现在状态栏的最左边。一般用addWidget()函数添加一个QLabel到状态栏上用于显示正常信息,它会生成到状态栏的最左边,可能会被临时消息所掩盖。如果要显示永久信息,要使用addPermanentWidget()函数来添加一个如QLabel一样的可以显示信息的部件,它会生成在状态栏的最右端,不会被临时消息所掩盖。
在状态栏的最右端,还有一个QSizeGrip部件,用来调整窗口的大小,可以使用setSizeGripEnabled()函数来禁用它。

6.自定义菜单

Qt中的QWidgetAction类可以实现自定义菜单的功能。为了实现自定义菜单,需要新建一个类,它继承自QWidgetAction类,并且在其中重新实现createWidget()函数。
补充:

(1)emit
	当对象改变信号与槽状态时,信号就由该对象发射 (emit) 出去,这就是对象所要做的全部事情,它不知道另一端是谁在接收这个信号。
	代码示例:	
		signals:  
			void findPrevious(const QString &str, Qt::CaseSensitivity cs);  
			
		private slots:  
			void findClicked();  
			void enableFindButton(const QString &text);  
			
		void FindDialog::findClicked() 
		{				
			Qt::CaseSensitivity cs = caseCheckBox->isChecked() ? Qt::CaseInsensitive : Qt::CaseSensitive;    
			if(backwardCheckBox->isChecked()) 
			{  
				emit findPrevious(text, cs);  
			}  
			else 
			{  
				void findNext(const QString &str, Qt::CaseSensitivity cs);  
				emit findNext(text, cs);  
			}  
		} 
		//emit是将信号findNext或信号findPrevious发射出去。
		//说白了,就是调用findPrevious或者findNext信号对应的槽函数,emit之前,在某个地方,肯定有connect把信号和槽进行关联,
		//关联了之后,emit某个信号,就相当于调用这个信号connect时所关联的槽函数
(2)Qt之创建自定义类型
	在开始之前,需要确保创建的这个自定义类型符合QMetaType的规定的所有要求。换句话说,它必须提供:
		●一个公有的默认构造函数
		●一个公有的拷贝构造函数
		●一个公有的析构函数
	下面的Message类的定义包含了这些成员:
		class Message
		{
		public:
			Message();
			Message(const Message &other);
			~Message();
			
			Message(const QString &body, const QStringList &headers);
			
			QString body() const;
			QStringList headers() const;
			
		private:
			QString m_body;
			QStringList m_headers;
		};
		这个类同时还提供了一个经常使用的构造函数,以及两个用于获取私有数据的共有成员函数。	
二、富文本处理

富文本(Rich Text)或者叫做富文本格式,简单来说就是在文档中可以使用多种格式,比如字体颜色、图片和表格等等。

(1)富文本文档结构

在Qt中提供了对富文本处理的支持。Qt中对富文本的处理分为了编辑操作和只读操作两种方式。

●编辑操作是使用基于光标的一些接口函数,这样更好的模拟了用户的编辑操作,更加容易理解,而且不会丢失底层的文档框架。
●而对于文档结构的概览,使用了只读的分层次的接口函数,它们有利于文档的检索和输出。

对于文档的读取和编辑要使用不同方面的两组接口。

●文档的光标主要基于QTextCursor类
●文档的框架主要基于QTextDocument类

一个富文本文档的结构被分为了几种元素来表示:

●框架(QTextFrame)
●文本块(QTextBlock)
●表格(QTextTable)
●列表(QTextList)

每种元素的格式又使用相应的format类来表示:

●框架格式(QTextFrameFormat)
●文本块格式(QTextBlockFormat)
●表格格式(QTextTableFormat)
●列表格式(QTextListFormat)

这些格式一般在编辑文档时使用,所以它们常和QTextCursor类配合使用。
因为QTextEdit类就是一个富文本编辑器,所以在构建QTextEdit类的对象时就已经构建了一个QTextDocument类对象和一个QTextCursor类对象,只需调用它们进行相应的操作即可。
一个空的文档包含了一个根框架(Root frame),这个根框架又包含了一个空的文本块(Block)。框架将一个文档分为多个部分,在根框架里可以再添加文本块、子框架和表格等。
在这里插入图片描述
①设置根框架

QTextDocument *document = ui->textEdit->document(); //获取文档对象 
QTextFrame *rootFrame = document->rootFrame();      // 获取根框架				
QTextFrameFormat format;                            // 创建框架格式				
format.setBorderBrush(Qt::red);                     // 边界颜色				
format.setBorder(3);                                // 边界宽度				
rootFrame->setFrameFormat(format);                  // 框架使用格式

②添加子框架

QTextFrameFormat frameFormat; 
frameFormat.setBackground(Qt::lightGray);                // 设置背景颜色				
frameFormat.setMargin(10);                               // 设置边距				
frameFormat.setPadding(5);                               // 设置填衬				
frameFormat.setBorder(2);
frameFormat.setBorderStyle(QTextFrameFormat::BorderStyle_Dotted); //设置边框样式				
QTextCursor cursor = ui->textEdit->textCursor();         // 获取光标				
cursor.insertFrame(frameFormat);                         // 在光标处插入框架
(2)文本块

文本块QTextBlock类为文本文档QTextDocument提供了一个文本片(QTextFragment)的容器。
一个文本块可以看做是一个段落,但是它不能使用回车换行,因为一个回车换行就表示创建一个新的文本块。QTextBlock提供了只读接口,它是前面提到的文档分层次的接口的一部分,如果QTextFrame看做是一层,那么其中的QTextBlock就是另一层。
文本块的格式由QTextBlockFormat类来处理,它主要涉及对齐方式,文本块四周的边白,缩进等内容。而文本块中的文本内容的格式,
比如字体大小、加粗、下划线等内容,则由QTextCharFormat类来设置。
①遍历框架

QTextDocument *document = ui->textEdit->document();
QTextFrame *frame = document->rootFrame();
QTextFrame::iterator it;                       // 建立QTextFrame类的迭代器
for (it = frame->begin(); !(it.atEnd()); ++it) {
	QTextFrame *childFrame = it.currentFrame();// 获取当前框架的指针
	QTextBlock childBlock = it.currentBlock(); // 获取当前文本块
	if (childFrame)
		qDebug() << "frame";
	else if (childBlock.isValid())
		qDebug() << "block:" << childBlock.text();
}

②遍历子框架

QTextDocument *document = ui->textEdit->document();
QTextBlock block = document->firstBlock();    // 获取文档的第一个文本块
for (int i = 0; i < document->blockCount(); i++) {
	qDebug() << tr("文本块%1,文本块首行行号为:%2,长度为:%3,内容为:").arg(i).arg(block.firstLineNumber()).arg(block.length())<< block.text();
	block = block.next();                         // 获取下一个文本块
}

③编辑文本块及其内容的格式

QTextCursor cursor = ui->textEdit->textCursor();
QTextBlockFormat blockFormat;      // 文本块格式
blockFormat.setAlignment(Qt::AlignCenter); // 水平居中
cursor.insertBlock(blockFormat);   // 使用文本块格式
QTextCharFormat charFormat;// 字符格式
charFormat.setBackground(Qt::lightGray);   // 背景色
charFormat.setForeground(Qt::blue);// 字体颜色
// 使用宋体,12号,加粗,倾斜
charFormat.setFont(QFont(tr("宋体"), 12, QFont::Bold, true)); 
charFormat.setFontUnderline(true); // 使用下划线
cursor.setCharFormat(charFormat);  // 使用字符格式
cursor.insertText(tr("测试字体")); // 插入文本
(3)表格、列表和图片
//插入表格   
QTextCursor cursor = ui->textEdit->textCursor();
QTextTableFormat format;          // 表格格式
format.setCellSpacing(2);         // 表格外边白
format.setCellPadding(10);        // 表格内边白
cursor.insertTable(2, 2, format); // 插入2行2列表格
//插入列表
QTextListFormat format;           // 列表格式
format.setStyle(QTextListFormat::ListDecimal);   // 数字编号
ui->textEdit->textCursor().insertList(format);
//插入图片
QTextImageFormat format;          // 图片格式
format.setName("logo.png");       // 图片路径
ui->textEdit->textCursor().insertImage(format);
(4)查找功能
//查找文本   
QDialog *dlg = new QDialog(this);         // 创建对话框
lineEdit = new QLineEdit(dlg);            // 创建行编辑器
QPushButton *btn = new QPushButton(dlg);  // 创建按钮
btn->setText(tr("查找下一个"));
connect(btn,SIGNAL(clicked()),this,SLOT(findNext())); // 关联信号和槽
QVBoxLayout *layout = new QVBoxLayout;    // 创建垂直布局管理器
layout->addWidget(lineEdit);              // 添加部件
layout->addWidget(btn);
dlg->setLayout(layout);                   // 在对话框中使用布局管理器
dlg->show();
 
//查找下一个
QString string = lineEdit->text();
// 使用查找函数查找指定字符串,查找方式为向后查找
bool isfind = ui->textEdit->find(string, QTextDocument::FindBackward);
if(isfind){                // 如果查找成功,输出字符串所在行和列的编号
	qDebug() << tr("行号:%1 列号:%2")
				.arg(ui->textEdit->textCursor().blockNumber())
				.arg(ui->textEdit->textCursor().columnNumber());
}
(5)语法高亮

在使用Qt Creator编辑代码时可以发现,输入关键字时会显示不同的颜色,这就是所谓的语法高亮。
在Qt的富文本处理中提供了QSyntaxHighlighter类来实现语法高亮。为了实现这个功能,需要创建QSyntaxHighlighter类的子类,然后重新实现highlightBlock()函数,使用时直接将QTextDocument类对象指针作为其父部件指针,这样就可以自动调用highlightBlock()函数了。
自定义的类为MySyntaxHighlighter,像这样来使用:

highlighter = new MySyntaxHighlighter(ui->textEdit->document()); 

重新实现highlightBlock()函数:

QTextCharFormat myFormat;             // 字符格式
myFormat.setFontWeight(QFont::Bold);
myFormat.setForeground(Qt::green);
QString pattern = "\\bchar\\b";       // 要匹配的字符,这里是“char”单词
QRegExp expression(pattern);          // 创建正则表达式
int index = text.indexOf(expression); // 从位置0开始匹配字符串
// 如果匹配成功,那么返回值为字符串的起始位置,它大于或等于0
while (index >= 0) {    
	int length = expression.matchedLength(); // 要匹配字符串的长度
	setFormat(index, length, myFormat);      // 对要匹配的字符串设置格式
	index = text.indexOf(expression, index + length); // 继续匹配
}
//在这里主要是使用了正则表达式和QString类的indexOf()函数来进行字
(6)HTML

在富文本处理中还提供了对HTML子集的支持,可以在QLabel或者QTextEdit添加文本时使用HTML标签或者CSS属性,例如:

ui->textEdit->append(tr("<h1><font color=red>使用HTML</font></h1>")); 

三、拖放操作

所用类及继承关系
在这里插入图片描述

拖放操作包括两个动作:拖动(drag)和放下(drop或称为放置)。当被拖动时拖动的数据会被存储为MIME类型(见第6章文件对话框)的对象,MIME类型使用QMimeData类来描述。
MIME类型通常由剪贴板和拖放系统使用,以识别不同类型的数据。

●拖动点(drag site):拖动的起始位置。
●放下点(drop site):被拖动的对象放下的位置,若部件不能接受拖动的对象,Qt会改变光标的形状(一个禁用形状)来向用户进行说明。
1.拖放的启动和结束

启动拖放:拖放通过调用QDrag::exec()函数而启动,该函数是一个阻塞函数(但不会阻塞主事件循环),这意味着在拖放操作结束之前,不会返回该函数,调用QDrag::exe()函数后,Qt拥有对拖动对象的所有权,并会在必要时将其删除。
结束拖放:当用户放下拖动或取消拖动操作时结束拖放。

2.拖放产生的过程及事件

启动拖放后,会使数据被拖动,这时需要按住鼠标按键才能拖动需要拖动的数据,松开鼠标按键时意味着拖动结束。在这期间会产生如下事件
默认情况下,部件不接受放下事件。使用QWidget::setAcceptDrops()函数可设置部件是否接受放下事件(即,拖放完成时发送的事件)。
只有在部件接受放下事件的情形下,才会产生以下事件。

①QDragEnterEvent:拖动进入事件。当拖动操作进入部件时,该事件被发送到部件,忽略该事件,将会导至后续的拖放事件不能被发送,此时在该部件上光标通常会在外观上显示为禁用的图形。
②QDragMoveEvnet:拖动移动事件。当拖动操作正在进行时,以及当具有焦点时按下键盘的修饰键(比如Ctrl)时,发送该事件,要使部件能接收到该事件,则该部件必须接受QDragEnterEvent事件。
③QDropEvent:放下事件。在完成拖放操作时发送该事件,即当用户在部件上放下一个对象时,发送此事件。要使部件能接收到该事件,则该部件必须接受QDragEnterEvent事件,且不能忽略QDragMoveEvnt事件。
④QDragLeaveEvent:当拖放操作离开部件时发送该事件,注意:要使部件能接收到该事件,必须要使拖动先进入该部件(即产生QDragEnterEvent事件),然后再离开该部件,才会产生QDragLeaveEvent事件。因很少使用该事件,因此本文不做重点介绍。

注:必须接受是指必须重新实现该事件的处理函数并接受该事件,不能忽略是指在处件事理函数中不明确调用ignore()函数忽略该事件。

3.编写拖放程序的步骤

●在需要接受放下数据的部件上调用QWidget::setAcceptDrops()函数以使该部件能接受拖放事件。
●启动拖放:通常在mousePressEvent()或mouseMoveEvent()函数中启动拖放,记住启动拖放就是调用QDrag对象的exec()函数,因此也可以在keyPressEvent()等函数中启动拖放(因很少这样做,所以本文不予介绍)。在此步把需要拖动的数据保存在QMimeData对象中。
●重新实现需要接受放下数据的部件的dragEnterEvent()事件处理函数。
●根据需要重新实现dragMoveEvent或dropEvent()函数

四、打印文档

打印设置
QPrinter类是打印设置的类。

1.OutputFormat

说明:
QPrinter输出文件的格式,QPrinter::PdfFormat会输入pdf文件格式,QPrinter::PostScriptFormat输入ps文件格式.中执行print()后生成的文件类型就是根据这个来的。
相关函数:

OutputFormat QPrinter::outputFormat () //当前格式			
void QPrinter::setOutputFormat ( OutputFormat format )//设置格式			
void QPrinter::setOutputFileName ( const QString & fileName )//文件名后缀设为ps即为ps格式,后缀为pdf即为pdf格式	
2.其他部分参数
Orientation:纸张方向,有QPrinter::Portrait(纵向)和QPrinter::Landscape(横向)
void QPrinter::setOrientation ( Orientation orientation )
Orientation QPrinter::orientation ()
PaperSize:  纸张大小,有A4等
void QPrinter::setPaperSize ( PaperSize newPaperSize )
PaperSize QPrinter::paperSize ()	
3.输出为文件:

可以作为输入的控件有以下几个:
①QWebView,QWebFrame,作为html文件格式的载体;
②QPlainTextEdit,QTextEdit, 单行/多行文本编辑区域;
③QPainter,用于绘图的类,可处理图片格式的文件;
④QPrintPreviewWidget,专门用于打印预览的控件;
⑤QTextDocument,文档类型的文件处理;
以上控件都有一个共同点,都有print()函数,将设置好的QPrinter传入即可,输出对应的文件。

(1)QWebView和QWebFrame
用途:作为html文件格式的载体
处理函数:
	void QWebView::print ( QPrinter * printer )			
	void QTextDocument::print ( QPrinter * printer )		
示例:		
	//全局的变量		
	QWebView *webview=0;
	QPrinter *printer=0;		
	void printWebView(void)		
	{		
		if(webview!=0)		
		{		
			webview = new QWebView();		
		}		
		if(webview!=0)		
		{		
			printer = new QPrinter(QPrinter::PrinterResolution);		
		}		
		 webview->load(QUrl(“file:///home/root/test.html”));		
		//因为加载会花费时间,等加载完成才能处理打印		
		connect(webview,SIGNAL(loadFinished(bool)),this,SLOT(loadend (bool)));		
	}		
	void loadend(bool ok)		
	{		
		webview->print(printer);//输出打印文件		
	}		
(2)QPlainTextEdit和QTextEdit
用途:用于界面文本输入的控件
处理函数:	
	void QPlainTextEdit::print ( QPrinter * printer )			
	void QTextEdit::print ( QPrinter * printer )		
示例:		
	void prinTextEdit(void)		
	{		
		QTextEdit *edt = new QTextEdit();		
		QPrinter *printer = new QPrinter(QPrinter::PrinterResolution); 		
		edt->setText(“This is an text!);    		
		edt->print(printer);	
		delete printer;		
		delete edt;		
	}		
(3)QPainter
用途:用于绘图的类
处理函数:	
	QPainter ( QPaintDevice * device )		
示例:	
	void prinTextEdit(void)		
	{		
		QImage image(/home/root/test.jpg”);		
		QPainter painter(printer);		
		QRect rect = painter.viewport();		
		QSize size = image.size();		
		size.scale(rect.size(), Qt::KeepAspectRatio);		
		painter.setViewport(rect.x(), rect.y(),size.width(), size.height());		
		painter.setWindow(image.rect());		
		painter.drawImage(0, 0, image);
		painter.drawText(10,10,this is image”);		
	}	
(4)QPrintPreviewWidget
用途:专门用于打印预览的控件		
处理函数:		
	QPrintPreviewWidget::QPrintPreviewWidget ( QPrinter * printer, QWidget * parent = 0, Qt::WindowFlags flags = 0 )	
	void QPrintPreviewWidget::print ()	
示例:		
	QTextEdit edt;		
	void prinTextEdit(void)		
	{		
		QPrinter *printer = new QPrinter(QPrinter::PrinterResolution);		
		QPrintPreviewWidget *preview = new QPrintPreviewWidget (printer);		
		connect(previewWidget,SIGNAL(paintRequested(QPrinter*)),this,SLOT(printPreview(QPrinter *)));//关联打印预览的内容		
		preview ->print();		
		delete printer;		
		delete preview;		
	}		
	void printPreview(QPrinter *printer)		
	{		
		edt.setText(“This is an text!);		
		edt.print(printer);		
	}		
(5)QTextDocument
用途:文档类型的文件处理		
处理函数:		
	void QTextDocument::print ( QPrinter * printer )  		
4.输出到设备

输出ps的文件格式可以作为打印机驱动的输入文件。
在qt中可以使用以下命令:
QString cmd;//cmd设置为打印的命令,需要驱动支持。
system(cmd);

5.相关控件

Qt提供了一些标准的控件。
QPrintDialog,打印对话框
QPrintPreviewDialog,打印预览对话框

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值