第02章 Qt 5模板库、工具类及控件

2.1  字符串类

2.1.1  操作字符串

(2)QString::append()函数具有与“+=”操作符同样的功能,实现在一个字符串的末尾追加另一个字符串,例如:

QString str1 = "Welcome ";
QString str2 = "to ";
 
str1.append(str2);         	//str1=" Welcome to"
str1.append("you! ");      	//str1="Welcome to you! "

(3)组合字符串的另一个函数是QString::sprintf(),此函数支持的格式定义符和C++库中的函数sprintf()定义的一样。例如:

QString str;
str.sprintf("%s"," Welcome ");          		 // str="Welcome "
str.sprintf("%s"," to you! ");           	     // str="to you! "
str.sprintf("%s %s"," Welcome ", "to you! ");    // str=" Welcome to you! "

(4)Qt还提供了另一种方便的字符串组合方式,使用QString::arg()函数,此函数的重载可以处理很多的数据类型。此外,一些重载具有额外的参数对字段的宽度、数字基数或者浮点数精度进行控制。通常,相对于函数QString::sprintf(),函数QString::arg()是一个比较好的解决方案,因为它类型安全,完全支持Unicode,并且允许改变"%n"参数的顺序。例如:

QString str;
str=QString("%1 was born in %2.").arg("John").arg(1982);//str="John was born in 1982."

(5)QString也提供了一些其他组合字符串的方法,包括如下几种。

    ① insert()函数:在原字符串特定的位置插入另一个字符串;

    ② prepend()函数:在原字符串的开头插入另一个字符串;

    ③ replace()函数:用指定的字符串代替原字符串中的某些字符。

(6)很多时候去掉一个字符串两端的空白(空白字符包括回车字符“\n”、换行字符“\r”、制表符“\t”和空格字符“ ”等)非常有用,如获取用户输入的账号时。 ① QString::trimmed()函数:移除字符串两端的空白字符; ② QString::simplified()函数:移除字符串两端的空白字符,使用单个空格字符“ ”代替字符串中出现的空白字符。 例如:

QString str="  Welcome \t to \n you!     ";
str=str.trimmed();                            // str=" Welcome \t to \n you! "

2.1.2  查询字符串数据

(1)函数QString::startsWith()判断一个字符串是否以某个字符串开头。此函数具有两个参数,第一个参数指定了一个字符串,第二个参数指定是否大小写敏感(默认情况下,是大小写敏感的),例如:

QString str="Welcome to you! ";
str.startsWith("Welcome",Qt::CaseSensitive);    // 返回true;
str.startsWith("you",Qt::CaseSensitive);        // 返回false;

(2)函数QString::endsWith()类似于QString::startsWith(),此函数判断一个字符串是否以某个字符串结尾。

(3)函数QString::contains()判断一个指定的字符串是否出现过,例如:

QString str=" Welcome to you! ";
str.contains("Welcome",Qt::CaseSensitive);   	//返回true;

(4)比较两个字符串也是经常使用的功能,QString提供了多种比较手段。

   ① operator<(const QString&):比较一个字符串是否小于另一个字符串,如果是,则返回true。

   ② operator<=(const QString&):比较一个字符串是否小于等于另一个字符串,如果是,则返回true。

   ③ operator==(const QString&):比较两个字符串是否相等,如果相等,则返回true。

   ④ operator>=(const QString&):比较一个字符串是否大于等于另一个字符串,如果是,则返回true。

   ⑤ localeAwareCompare(const QString&,const QString&):静态函数,比较前后两个字符串,如果小于则返回负整数值;如果等于则返回0;如果大于则返回正整数值。

   ⑥ compare(const QString&,const QString&,Qt::CaseSensitivity):该函数可以指定是否进行大小写的比较,而大小写的比较是完全基于字符的Unicode编码值的,而且是非常快的,返回值类似于localeAwareCompare()函数。

2.1.3  字符串的转换

(1)QString::toInt()函数将字符串转换为整型数值,类似的函数还有toDouble()、toFloat()、toLong()、toLongLong()等。下面举个例子说明其用法:

QString str="125";
bool ok;
int hex=str.toInt(&ok,16);       //ok=true,hex=293
int dec=str.toInt(&ok,10);       //ok=true,dec=125

(2)QString提供的字符编码集的转换函数将会返回一个const char* 类型版本的QByteArray,即构造函数QByteArray(const char*)构造的QByteArray对象。QByteArray类具有一个字节数组,它既可以存储原始字节(raw bytes),也可以存储传统的以“\0”结尾的8位的字符串。在Qt中,使用QByteArray比使用const char*更方便,且QByteArray也支持隐式共享。转换函数有以下几种。

    ① toAscii():返回一个ASCII编码的8位字符串。

    ② toLatin1():返回一个Latin-1(ISO8859-1)编码的8位字符串。

    ③ toUtf8():返回一个UTF-8编码的8位字符串(UTF-8是ASCII码的超级,它支持整个Unicode字符集)。

    ④ toLocal8Bit():返回一个系统本地(locale)编码的8位字符串。

下面举例说明其用法:

QString str=" Welcome to you! ";
QByteArray ba=str.toAscii();
qDebug()<<ba;
ba.append("Hello,World! ");
qDebug()<<ba.data();

一个NULL字符串就是使用QString的默认构造函数或者使用“(const char*)0”作为参数的构造函数创建的QString字符串对象;而一个空字符串是一个大小为0的字符串。一个NULL字符串一定是一个空字符串,而一个空字符串未必是一个NULL字符串。例如:

QString().isNull();       	// 结果为true
QString().isEmpty();      	// 结果为true
QString("").isNull();     	// 结果为false
QString("").isEmpty();   	// 结果为true

2.2  容器类

2.2.1  QList类、QLinkedList类和QVector类

在开发一个较高性能需求的应用程序时,程序员会比较关注这些容器类的运行效率,表2.1列出了QList、QLinkedList和QVector容器的时间复杂度比较。

1.QList类

       QList<T>是迄今为止最常用的容器类,它存储给定数据类型T的一列数值。

       QList不仅提供了可以在列表进行追加的QList::append()和Qlist::prepend()函数,还提供了在列表中间完成插入操作的函数QList::insert()。

       QList<T>维护了一个指针数组,该数组存储的指针指向QList<T>存储的列表项的内容。 对于不同的数据类型,QList<T>采取不同的存储策略,存储策略有以下几种。

      (1)如果T是一个指针类型或指针大小的基本类型(即该基本类型占有的字节数和指针类型占有的字节数相同),QList<T>会将数值直接存储在它的数组中。

      (2)如果QList<T>存储对象的指针,则该指针指向实际存储的对象。

       下面举一个例子:       

#include <QDebug>
int main(int argc,char *argv[])
{
	QList<QString> list;
	{
		QString str("This is a test string");
		list<<str;
	}
	qDebug()<<list[0]<< "How are you! ";
	return 0;
}

2.QLinkedList类

QLinkedList<T>是一个链式列表,它以非连续的内存块保存数据。

QLinkedList<T>不能使用下标,只能使用迭代器访问它的数据项。

3.QVector类 QVector<T>

在相邻的内存中存储给定数据类型T的一组数值。

QVector<T>既可以使用下标访问数据项,也可以使用迭代器访问数据项。

4.Java风格迭代器遍历容器

Java风格的迭代器是Qt 4新加入的一个功能。

对于每一个容器类,Qt都提供了两种类型的Java风格迭代器数据类型,即只读访问和读写访问,其分类见表2.2。

5.STL风格迭代器遍历容器 对于每一个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。STL风格迭代器的两种分类见表2.3。

2.2.2  QMap类和QHash类

QMap类和QHash类具有非常类似的功能,它们的差别仅在于:

  • QHash具有比QMap更快的查找速度;
  • QHash以任意的顺序存储数据项,而QMap总是按照键Key顺序存储数据;
  • QHash的键类型Key必须提供operator==()和一个全局的qHash(Key)函数,而QMap的键类型Key必须提供operator<()函数。

   二者的时间复杂度比较见表2.4。   

1.QMap类

QMap<Key,T>提供了一个从类型为Key的键到类型为T的值的映射。

 2.QHash类

QHash<Key,T>具有与QMap几乎完全相同的API。QHash维护着一张哈希表(Hash Table),哈希表的大小与QHash的数据项的数目相适应。

3.Java风格迭代器遍历容器

对于每一个容器类,Qt都提供了两种类型的Java风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。其分类见表2.5。

下面的例子(见代码CH204)完成了在QMap中的插入、遍历和修改。

#include <QDebug>
int main(int argc,char *argv[])
{
	QMap<QString,QString> map;
	map.insert("beijing","111");
	map.insert("shanghai","021");
	map.insert("nanjing","025");
	QMapIterator<QString,QString> i(map);
	for(;i.hasNext();)
		qDebug()<<"  "<<i.key()<<"  "<<i.next().value();
	QMutableMapIterator<QString,QString> mi(map);
	if(mi.findNext("111"))
		mi.setValue("010");
	QMapIterator<QString,QString> modi(map);
	qDebug()<<"  ";
	for(;modi.hasNext();)
		qDebug()<<" "<<modi.key()<<"  "<<modi.next().value();
	return 0;
}

4.STL风格迭代器遍历容器

对于每一个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。其分类见表2.6。

下面的程序(见代码CH205)功能与使用Java风格迭代器的例子基本相同。不同的是,这里通过查找键来实现值的修改。

#include <QDebug>
int main(int argc,char *argv[])
{
	QMap<QString,QString> map;
	map.insert("beijing","111");
	map.insert("shanghai","021");
	map.insert("jinan","0531");

	QMap<QString,QString>::const_iterator i;
	for(i=map.constBegin();i!=map.constEnd();++i)
		qDebug()<<"  "<<i.key()<<"  "<<i.value();

	QMap<QString,QString>::iterator mi;
	mi=map.find("beijing");
	if(mi!=map.end())
		mi.value()="010";

	QMap<QString,QString>::const_iterator modi;
	qDebug()<<"  ";
	for(modi=map.constBegin();modi!=map.constEnd();++modi)
		qDebug()<<"  "<<modi.key()<<"  "<<modi.value();

	return 0;
}

2.3  QVariant类

下面举例介绍QVariant类的用法(见代码CH206)。 新建Qt Gui应用,项目名称为“myVariant”,基类选择“QWidget”,类名保持“Widget”不变,取消选择“创建界面”复选框。建好项目后,在widget.cpp文件中编写代码,具体内容。

// 包含头文件
/* 
#include <QDebug>
#include <QVariant>
#include <QColor>
*/

QVariant v(709);
qDebug()<<v.toInt(); 
QVariant w("How are you! ");
qDebug() << w.toString();
QMap<QString, QVariant>map;
map["int"] = 709;
map["double"] = 709.709;
map["string"] = "How are you! ";
map["color"] = QColor(255, 0, 0);
qDebug() << map["int"] << map["int"].toInt();
qDebug() << map["double"] << map["double"].toDouble();
qDebug() << map["string"] << map["string"].toString();
qDebug() << map["color"] << map["color"].value<QColor>();
QStringList sl;
sl << "A" << "B" << "C" << "D";
QVariant slv(sl);
if (slv.type() == QVariant::StringList)
{
	QStringList list = slv.toStringList();
	for (int i = 0; i < list.size(); ++i)
		qDebug() << list.at(i);
}    

2.4  算法及正则表达式

2.4.1  Qt 5常用算法

Qt的<QtAlgorithms>和<QtGlobal>模块提供了一些算法和函数。下面简单介绍几个常用算法的用法(见代码CH207)。

#include <QDebug>
int main(int argc,char *argv[])
{
	double a=-19.3,b=9.7;
	double c=qAbs(a);        	//c=19.3
	double max=qMax(b,c);    //max=c=19.3 
	
	int bn=qRound(b);        	//bn=10
	int cn=qRound(c);        	//cn=19
	
	qDebug()<<"a="<<a;
	qDebug()<<"b="<<b;
	qDebug()<<"c=qAbs(a)= "<<c;
	qDebug()<<"qMax(b,c)= "<<max;
	qDebug()<<"bn=qRound(b)= "<<bn;
	qDebug()<<"cn=qRound(c)= "<<cn;
	
	qSwap(bn,cn);
	qDebug()<<"qSwap(bn,cn):"<<"bn="<<bn<<" cn="<<cn;
	
	return 0;
}

2.4.2  基本的正则表达式

正则表达式由表达式(expressions)、量词(quantifiers)和断言(assertions)组成。

(1)最简单的表达式是一个字符。要表示字符集的表达式可以使用如“[AEIOU]”表示匹配所有的大写元音字母;使用“[^AEIOU]”则表示匹配所有非元音字母,即辅音字母;连续的字符集可以使用表达式如“[a-z]”,表示匹配所有小写英文字母。

(2)量词说明表达式出现的次数,如“x[1,2]”表示“x”可以至少有一个,至多两个。 在计算机语言中,标识符通常要求以字母或下画线开头,后面可以是字母、数字和下画线。满足条件的标识符表示为:

" [A-Za-z_]+[A-Za-z_0-9]* "

类似的正则表达式量词见表2.8。

(3)“^”、“$”、“\b”都是正则表达式的断言,正则表达式的断言见表2.9。

2.5  控件

2.5.1  按钮组(Buttons)

按钮组(Buttons)如图2.1所示。

(1)新建Qt Gui应用(详见第1章1.3.1节),项目名为“PushButtonTest”,基类选择“QWidget”选项,类名命令为“MyWidget”,取消“创建界面”复选框的选中状态。

(2)在头文件“mywidget.h”中的具体代码如下:

#ifndef MYWIDGET_H
#define MYWIDGET_H
 
#include <QWidget>\
 
class MyWidget : public QWidget
{
    Q_OBJECT
 
public:
    MyWidget(QWidget *parent = 0);
    ~MyWidget();
};
 
#endif // MYWIDGET_H

(3)在源文件“mywidget.cpp”中的具体代码如下:

#include "mywidget.h"
#include <qapplication.h>
#include <qpushbutton.h>
#include <qfont.h>
 
MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
{
       setMinimumSize( 200, 120 );
       setMaximumSize( 200, 120 );
   
       QPushButton *quit = new QPushButton( "Quit", this);
       quit->setGeometry( 62, 40, 75, 30 );
       quit->setFont( QFont( "Times", 18, QFont::Bold ) );
      
       connect( quit, SIGNAL(clicked()), qApp, SLOT(quit()) );
}
MyWidget::~MyWidget()
{
}

(4)在源文件“main.cpp”中的具体代码如下:

#include "mywidget.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyWidget w;
    w.setGeometry( 100, 100, 200, 120 );
    w.show();
    return a.exec();
}

(5)运行结果如图2.2所示。

2.5.2  输入部件组(Input Widgets)

 

1.QDateTime类

在Qt 5中,可以使用QDateTime类来获得系统时间。通过QdateTime::currentDateTime()来获取本地系统的时间和日期信号。可以通过date()和time()来返回datetime中的日期和时间部分,代码如下:

Qlabel * datalabel =new Qlabel();
QdateTime *datatime=new QdateTime(QdateTime::currentDateTime());
datalabel->setText(datatime->date().toString()); 
datalabel->show();

2.Qtimer类

定时器(Qtimer)的使用非常简单,只需要以下几个步骤就可以完成定时器的应用。

(1)新建一个定时器。

QTimer *time_clock=new QTimer(parent); 

(2)连接这个定时器的信号和槽,利用定时器的timeout()。

connect(time_clock,SIGNAL(timeout()),this,SLOT(slottimedone())); 

(3)开启定时器,并设定定时周期。 定时器定时有两种方式,start(int time)和setSingleShot(true)。其中,start(int time)表示每隔“time”秒就会重启定时器,可以重复触发定时,利用stop()将定时器关掉;而setSingleShot(true)则是仅启动定时器一次。工程中常用的是前者,例如:

time_clock->start(2000);

2.5.3  显示控件组(Display Widgets)

下面介绍其中几个控件。

(1)Graphics View对应于QGraphicsView类,其具体用法将在本书第7章详细介绍。

(2)Text Browser对应于QTextBrowser类。QTextBrowser类继承自QTextEdit,而且仅是只读的,对里面的内容并不能进行更改,但是相对于QTextEdit来讲,它还具有链接文本的作用。QTextBrowser的属性有以下几点。

modified : const bool          //通过布尔值来说明其内容是否被修改
openExternalLinks : bool
openLinks : bool
readOnly : const bool
searchPaths : QStringList
source : QUrl
undoRedoEnabled : const bool

QTextBrowser还提供了几种比较有用的槽(SLOTS),即

virtual void backward ()
virtual void forward ()
virtual void home ()

2.5.4  空间间隔组(Spacers)

空间间隔组(Spacers)如图2.5所示。

2.5.5  布局管理组(Layouts)

布局管理组(Layouts)如图2.6所示。

2.5.6  容器组(Containers)

1.创建窗口

       如果Widget未使用父级进行创建,则在显示时视为窗口或顶层Widget。由于顶层Widget没有父级对象类来确保在其不再使用时就删除,所以需要开发人员在应用程序中对其进行跟踪。

       在本例中,使用QWidget创建和显示具有默认大小的窗口:

QWidget *window = new QWidget();
window->resize(320, 240);
window->show();
 
QPushButton *button = new QPushButton(tr("Press me"), window);
button->move(100, 100);
button->show();

2.使用布局

通常,子Widget是通过使用布局对象在窗口中进行排列的,而不是通过指定位置和大小进行排列的。在此,构造一个并排排列的标签和行编辑框Widget:

QLabel *label = new QLabel(tr("Name:"));
QLineEdit *lineEdit = new QLineEdit();
 
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(label);
layout->addWidget(lineEdit);
window->setLayout(layout);

由于Widget可包含其他Widget,所以布局可用来提供按不同层次分组的Widget。这里,要在显示查询结果的表视图上方、窗口顶部的行编辑框旁,显示一个标签:

QLabel *queryLabel = new QLabel(tr("Query:"));
QLineEdit *queryEdit = new QLineEdit();
QTableView *resultView = new QTableView();
 
QHBoxLayout *queryLayout = new QHBoxLayout();
queryLayout->addWidget(queryLabel);
queryLayout->addWidget(queryEdit);
 
QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->addLayout(queryLayout);
mainLayout->addWidget(resultView);
window->setLayout(mainLayout);

2.5.7  项目视图组(Item Views)

项目视图组(Item Views)如图2.8所示。

 

下面介绍此处的Table View与2.5.8节中的Table Widget的区别,其具体区别见表2.10。

QTableWidget继承自QTableView。QSqlTableModel能够与QTableView绑定,但不能与QTableWidget绑定。例如:

QSqlTableModel *model = new QSqlTableModel;
model->setTable("employee");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
model->removeColumn(0); // 不显示 ID
model->setHeaderData(0, Qt::Horizontal, tr("Name"));
model->setHeaderData(1, Qt::Horizontal, tr("Salary"));
 
QTableView *view = new QTableView;
view->setModel(model);
view->show();

下面是错误的写法:

QStandardItemModel model(4,2);
model.setHeaderData(0, Qt::Horizontal, tr("Label"));
model.setHeaderData(1, Qt::Horizontal, tr("Quantity"));
ui.tableView->setModel(&model);
for (int row = 0; row < 4; ++row) 
{
	for (int column = 0; column < 2; ++column) 
	{
            QModelIndex index = model.index(row, column, QModelIndex());
            model.setData(index, QVariant((row+1) * (column+1)));
	}
}

下面是正确的写法:

QStandardItemModel *model;
model = new QStandardItemModel(4,2);
ui.tableView->setModel(model);
 
model->setHeaderData(0, Qt::Horizontal, tr("Label"));
model->setHeaderData(1, Qt::Horizontal, tr("Quantity"));
for (int row = 0; row < 4; ++row) 
{
    for (int column = 0; column < 2; ++column) 
    {
        QModelIndex index = model->index(row, column, QModelIndex());
        model->setData(index, QVariant((row+1) * (column+1)));
    }
}

2.5.8  项目控件组(Item Widgets)

项目控件组(Item Widgets)如图2.9所示。

下面以如何创建具有复选框的树形控件为例说明以上用法(见代码CH209)。在Qt中树形控件称为QTreeWidget,而控件里的树节点称为QTreeWidgetItem。这种控件其实有时很有用处。例如,利用飞信软件群发短信时,选择联系人的界面中就使用了有复选框的树形控件,如图2.10所示。

具体步骤如下。

(1)新建Qt Gui应用(详见本书第1章1.3.1节),项目名称为“TreeWidget”,基类选择“QWidget”,类名保持“Widget”不变,保持“创建界面”复选框的选中状态。

(2)双击“widget.ui”文件,打开Qt的设计器,拖曳出一个QTreeWidget。

(3)在头文件“widget.h”中添加代码:

#include <QTreeWidgetItem>

在类Widget声明中添加代码:

public:
    void init();
    void updateParentItem(QTreeWidgetItem* item);
public slots:
    void treeItemChanged(QTreeWidgetItem* item, int column);

(4)在源文件“widget.cpp”中的类Widget构造函数中添加代码:

init();
connect(ui->treeWidget,SIGNAL(itemChanged(QTreeWidgetItem*, int)),
            this, SLOT(treeItemChanged(QTreeWidgetItem*, int)));

在此文件中实现各个函数的具体代码。

函数treeItemChanged()的具体实现代码。

函数updateParentItem()的具体实现代码。

2.5.9  小综合例子

(1)新建Qt Gui应用(详见本书第1章1.3.1节),项目名称为“Test”,基类选择“QDialog”,类名保持“Dialog”不变,保持“创建界面”复选框的选中状态。

(2)双击dialog.ui文件,打开Qt的设计器,中间的空白视窗即为一个parent widget,接着需要建立一些child widget。在左边的工具箱中找到所需要的widget:拖曳出一个Label、一个Line Edit(用于输入文字)、一个Horizontal Spacer及两个Push Button。

(3)设置widget的属性: 初始的设计效果如图2.11所示。

(4)运行工程,此时看到界面中的label会显示一个“&”。为了解决这个问题,选择“编辑(E)”→“编辑伙伴”(Buddy)     命令,在此模式下,可以设定伙伴。选中label并拖曳至lineEdit,然后放开,此时会有一个红色箭头由label指向lineEdit,如图2.12所示。

(4)运行工程,此时看到界面中的label会显示一个“&”。为了解决这个问题,选择“编辑(E)”→“编辑伙伴”(Buddy)     命令,在此模式下,可以设定伙伴。选中label并拖曳至lineEdit,然后放开,此时会有一个红色箭头由label指向lineEdit,如图2.12所示。

此时,再次运行该程序,label的“&”不再出现,如图2.13所示,此时label与lineEdit这两个widget互为伙伴了。

(5)对widget进行位置编排的布局(layout)。

 

(6)单击“        ”编辑【Tab】键顺序按钮,每个widget上都会出现一个方框显示数字,这就是表示按下【Tab】键的顺序,调整到需要的顺序,如图2.16所示。单击“      ”编辑元件按钮,即可离开此模式,回到原来的编辑模式。此时,运行该程序后的效果如图2.17所示。

(7)在头文件“dialog.h”中的Dialog类声明中添加语句:

private slots:
    void on_lineEdit_textChanged();

(8)在源文件“dialog.cpp”中的构造函数中添加代码如下:

QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");
ui->lineEdit->setValidator(new QRegExpValidator(regExp,this));
connect(ui->okButton,SIGNAL(clicked()),this,SLOT(accept()));
connect(ui->cancelButton,SIGNAL(clicked()),this,SLOT(reject()));

(9)运行此工程。当在lineEdit中输入A12后,单击“OK”按钮后,将自动变为可执行状态,当单击“Cancel”按钮时则会关闭视窗,如图2.18所示。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值