QML与c++交互

QMLc++交互学习笔记()

关于导出C++的学习

说明,主要是对QT的文档内例子进行的一些分别解说,希望更容易的理解

C++导出到QML的过程。

 

 

1.导出一个简单的类Person

2.具体导出过程

假设我们要导出一个Person类,

A那么就要考虑如何的一个类他才可以导出呢?

他需要符合一定的条件

1.继承自QObject

2.有默认构造函数

B如何导出呢?

通过一个函数

int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

int qmlRegisterType()

3.具体的例子

 

//person.h

#ifndefPERSON_H

#definePERSON_H

#include

classPerson:publicQObject

{

    Q_OBJECT

public:

    explicitPerson(QObject*parent=0);

};

#endif//PERSON_H

 

//person.cpp

#include"person.h"

Person::Person(QObject*parent):

    QObject(parent)

{

}

 

//main.cpp

#include

#include

#include

#include

#include"person.h"

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    qmlRegisterType<Person>("People",1,0,"Person");

    //qmlRegisterType();

    QDeclarativeViewqmlView;

    qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml"));

    qmlView.show();

    returna.exec();

}

 

//UICtest.qml

importQt4.7

importPeople1.0//如果是qmlRegisterType();导出就可以注释这条

Rectangle{

    width:640

    height:480

    Person{}

}

说明:我们通过qmlRegisterType<Person>("People",1,0,"Person");

QML中导出Person类,这个类在People包中,在QML中需要使用Person类的

话就必须包含People包,通过importPeople1.0来包含,之后就可以使用Person

创建对象使用来。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

QMLc++交互学习笔记()

 

1.导出Person类中的成员方法

2.具体导出过程

导出的方法有

1.使用Q_INVOKABLE

2.使用槽机制

3.具体代码

 

 

//person.h

#ifndefPERSON_H

#definePERSON_H

#include

classPerson:publicQObject

{

    Q_OBJECT

public:

    explicitPerson(QObject*parent=0);

    Q_INVOKABLEvoidFirstEcho(void);

publicslots:

    voidSecondEcho(void);

};

#endif//PERSON_H

 

//person.cpp

#include"person.h"

Person::Person(QObject*parent):

    QObject(parent)

{

}

voidPerson::FirstEcho(void)

{

    //简简单单打印一句话

    qDebug("callPerson::FirstEcho");

}

voidPerson::SecondEcho(void)

{

    qDebug("callPerson::SecondEcho");

}

 

//main.cpp

#include

#include

#include

#include

#include"person.h"

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    qmlRegisterType<Person>("People",1,0,"Person");

    //qmlRegisterType();

    QDeclarativeViewqmlView;

    qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml"));

    qmlView.show();

    returna.exec();

}

 

//UICtest.qml

importQt4.7

importPeople1.0//如果是qmlRegisterType();导出就可以注释这条

Rectangle{

    width:640

    height:480

    Person{id:per;}

    MouseArea{

        anchors.fill:parent;

        onClicked:{

            per.FirstEcho();

            per.SecondEcho();

        }

    }

}

 

说明:

这里导出了两个函数分别是FirstEcho SecondEcho 两个函数,这两个函数本别是使用

FirstEcho使用使用 Q_INVOKABLE导出,SecondEcho直接使用槽。

调用函数在控制台输出一些信息,这里是在鼠标点击界面后出发的。

 

 

 

 

 

 

 

 

QMLc++交互学习笔记()

1.导出Person类中的属性

2.具体导出过程

1.导出Person一个颜色属性,一个int属性

注意

1. 当需要实现属性变化其他引用到此属性的属性也跟着变化的情况的话,需要设置属性相应的信号

2. 设置属性的时候,使用的类型必须是已经导出到QML中的类型

3.具体代码

 

//person.h

#ifndefPERSON_H

#definePERSON_H

#include

#include

classPerson:publicQObject

{

    Q_OBJECT

    //设置设置属性的名字是bgcolor

    //对应读取函数名字bgColor

    //对应写函数名字setBgColor

    //属性发生改变后发送信号sendBgColorChange

Q_PROPERTY(QColorbgcolorREADgetBgColorWRITEsetBgColorNOTIFYsendBgColorChange)

   //设置设置属性的名字是count

   //对应读取函数名字getCount

   //对应写函数名字setCount

   //属性发生改变后发送信号sendCountChange

   Q_PROPERTY(intcountREADgetCountWRITEsetCountNOTIFYsendCountChange)

public:

    explicitPerson(QObject*parent=0);

    QColorgetBgColor(void)const;

    voidsetBgColor(constQColor&color);

    intgetCount(void);

    voidsetCount(intcount);

signals:

    voidsendBgColorChange(void);

    voidsendCountChange(void);

private:

    QColor  m_Color;

    int     m_Count;

};

#endif//PERSON_H

 

//person.cpp

#include"person.h"

//---------------------------------

//

Person::Person(QObject*parent):

    QObject(parent),m_Color("blue"),m_Count(0)

{

}

//---------------------------------

//

QColorPerson::getBgColor(void)const

{

    returnm_Color;

}

//---------------------------------

//

voidPerson::setBgColor(constQColor&color)

{

    m_Color=color;

    emitsendBgColorChange();

}

//---------------------------------

//

intPerson::getCount(void)

{

    returnm_Count;

}

//---------------------------------

//

voidPerson::setCount(intcount)

{

    m_Count=count;

    emitsendCountChange();

}

 

//main.cpp

#include

#include

#include

#include

#include"person.h"

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    qmlRegisterType<Person>("People",1,0,"Person");

    //qmlRegisterType();

    QDeclarativeViewqmlView;

    qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml"));

    qmlView.show();

    returna.exec();

}

 

//UICtest.qml

importQt4.7

importPeople1.0//如果是qmlRegisterType();导出就可以注释这条

Rectangle{

    width:640

    height:480

    color:per.bgcolor;

    Person{id:per;}

    Text{

        id:textlabel;

        text:"text  "+per.count;

    }

    MouseArea{

        anchors.fill:parent;

        onClicked:{

            //当鼠标按下后,由于属性上有信号,当属性发生改变后,

            //所有引用此属性的值的都相应的发生改变

            per.bgcolor="red";

            per.count=20;

        }

    }

}

 

说明:

person类中,设置了两个属性bgcolor, count ,他们分别在发送改变后调用自己对应的信号

具体看源代码,这里是设置来矩形框的颜色,文本框中文本。

 

 

 

 

 

 

 

 

 

 

QMLc++交互学习笔记()

1.导出Person类,并且一个Job类,Job类包含一个Person的指针

2.具体导出过程

1.通过属性来实现,具体的请看代码

 

3.具体代码

 

 

//person.h

#ifndefPERSON_H

#definePERSON_H

#include

#include

classPerson:publicQObject

{

    Q_OBJECT

    //设置设置属性的名字是name

    //对应读取函数名字getName

    //对应写函数名字setName

    //属性发生改变后发送信号sendNameChange

    Q_PROPERTY(QStringnameREADgetNameWRITEsetNameNOTIFYsendNameChange)

   //设置设置属性的名字是age

   //对应读取函数名字getAge

   //对应写函数名字setAge

   //属性发生改变后发送信号sendAgeChange

   Q_PROPERTY(intageREADgetAgeWRITEsetAgeNOTIFYsendAgeChange)

public:

    explicitPerson(QObject*parent=0);

    QStringgetName(void)const;

    voidsetName(constQString&name);

    intgetAge(void);

    voidsetAge(intage);

signals:

    voidsendNameChange(void);

    voidsendAgeChange(void);

private:

    QString     m_Name;

    int         m_Age;

};

/*

 设想一份工作给予一个人

 */

classJob:publicQObject

{

    Q_OBJECT

    Q_PROPERTY(Person*perREADgetPersonWRITEsetPersonNOTIFYsendPersonChange)

    Q_PROPERTY(QStringjnREADgetJobNameWRITEsetJobNameNOTIFYsendJobNameChange)

public:

    explicitJob(QObject*parent=0);

    ~Job();

    voidsetPerson(Person*per);

    Person*getPerson(void)const;

    voidsetJobName(constQString&jobname);

    QStringgetJobName(void)const;

signals:

    voidsendPersonChange();

    voidsendJobNameChange();

private:

    Person*m_Person;

    QStringm_JobName;

};

#endif//PERSON_H

 

//person.cpp

#include"person.h"

//---------------------------------

//

Person::Person(QObject*parent):

    QObject(parent),m_Name("unknow"),m_Age(0)

{

}

//---------------------------------

//

QStringPerson::getName(void)const

{

    returnm_Name;

}

//---------------------------------

//

voidPerson::setName(constQString&name)

{

    m_Name=name;

    emitsendNameChange();

}

//---------------------------------

//

intPerson::getAge(void)

{

    returnm_Age;

}

//---------------------------------

//

voidPerson::setAge(intage)

{

    m_Age=age;

    emitsendAgeChange();

}

//---------------------------------

//

Job::Job(QObject*parent)

    :QObject(parent),m_Person(0),m_JobName("unknown")

{

}

//---------------------------------

//

Job::~Job()

{

}

//---------------------------------

//

voidJob::setPerson(Person*per)

{

    m_Person=per;

    emitsendPersonChange();

}

//---------------------------------

//

Person*Job::getPerson(void)const

{

    returnm_Person;

}

//---------------------------------

//

voidJob::setJobName(constQString&jobname)

{

    m_JobName=jobname;

    emitsendJobNameChange();

}

//---------------------------------

//

QStringJob::getJobName(void)const

{

    returnm_JobName;

}

 

//main.cpp

#include

#include

#include

#include

#include"person.h"

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    qmlRegisterType<Person>("People",1,0,"Person");

    //qmlRegisterType();

    qmlRegisterType<Job>("People",1,0,"Job");

    QDeclarativeViewqmlView;

    qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml"));

    qmlView.show();

    returna.exec();

}

 

//UICtest.qml

importQt4.7

importPeople1.0//如果是qmlRegisterType();导出就可以注释这条

Rectangle{

    width:640

    height:480

    Job{

        id:jobA;

        jn:"Learn";

        per:Person{id:ps;name:"Luly";age:25;}

    }

    //显示这份工作的一些信息

    Rectangle{

        x:100;y:100;

        width:100;height:100;

        Text{text:"Jobname:"+jobA.jn;}

        Text{y:20;text:"Personname:"+ps.name;}

        Text{y:40;text:"Personage:"  +ps.age;}

    }

    MouseArea{

        anchors.fill:parent;

        onClicked:{

            //我要改变工作的名字工作人的信息

            jobA.jn="CleanHouse";

            ps.name="Tom";

            ps.age=30;

        }

    }

}

 

说明:

主要是导出了两个类PersonJob, Job 包含一个Person的指针,这样后,可以看到

QML中,我们需要给予Job对象一个Person来尽心赋值。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

QMLc++交互学习笔记()

1.导出Person类,并且一个PersonGroup类,PersonGroup类是Person的一个组

 

2.具体导出过程

1.通过属性来实现,具体的请看代码

 

3.具体代码

 

 

//person.h

#ifndefPERSON_H

#definePERSON_H

#include

#include

#include

classPerson:publicQObject

{

    Q_OBJECT

    Q_PROPERTY(QStringnameREADgetNameWRITEsetNameNOTIFYsendNameChange)

    Q_PROPERTY(intageREADgetAgeWRITEsetAgeNOTIFYsendAgeChange)

public:

    explicitPerson(QObject*parent=0);

    QStringgetName(void)const;

    voidsetName(constQString&name);

    intgetAge(void);

    voidsetAge(intage);

signals:

    voidsendNameChange(void);

    voidsendAgeChange(void);

private:

    QString     m_Name;

    int         m_Age;

};

classPersonGroup:publicQObject

{

    Q_OBJECT

    Q_PROPERTY(QDeclarativeListProperty<Person>membersREADmembers)

public:

    explicitPersonGroup(QObject*parent=0);

    QDeclarativeListProperty<Person>members(void);

    Q_INVOKABLEintmembersCount(void)const;

    Q_INVOKABLEPerson*member(intindex)const;

private:

    QList<Person*>m_MemberList;

};

#endif//PERSON_H

 

//person.cpp

#include"person.h"

//---------------------------------

//

Person::Person(QObject*parent):

    QObject(parent),m_Name("unknow"),m_Age(0)

{

}

//---------------------------------

//

QStringPerson::getName(void)const

{

    returnm_Name;

}

//---------------------------------

//

voidPerson::setName(constQString&name)

{

    m_Name=name;

    emitsendNameChange();

}

//---------------------------------

//

intPerson::getAge(void)

{

    returnm_Age;

}

//---------------------------------

//

voidPerson::setAge(intage)

{

    m_Age=age;

    emitsendAgeChange();

}

//---------------------------------

//

PersonGroup::PersonGroup(QObject*parent)

    :QObject(parent)

{

}

//---------------------------------

//

QDeclarativeListProperty<Person>PersonGroup::members(void)

{

    returnQDeclarativeListProperty<Person>(this,m_MemberList);

}

//---------------------------------

//

intPersonGroup::membersCount()const

{

    returnm_MemberList.size();

}

//---------------------------------

//

Person*PersonGroup::member(intindex)const

{

    returnm_MemberList.at(index);

}

 

//main.cpp

#include

#include

#include

#include

#include"person.h"

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    qmlRegisterType<Person>("People",1,0,"Person");

    //qmlRegisterType();

    qmlRegisterType<PersonGroup>("People",1,0,"PersonGroup");

    QDeclarativeViewqmlView;

    qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml"));

    qmlView.show();

    returna.exec();

}

 

//UICtest.qml

importQt4.7

importPeople1.0//如果是qmlRegisterType();导出就可以注释这条

Rectangle{

    width:640

    height:480

    propertyintpgcurIndex:0;

    PersonGroup{

        id:group;

        members:[

            Person{name:"A";age:20},

            Person{name:"B";age:21},

            Person{name:"C";age:22},

            Person{name:"D";age:23},

            Person{name:"E";age:24}

        ]

    }

    //显示这份工作的一些信息

    Rectangle{

        x:100;y:100;

        width:100;height:100;

        Text{id:text1;  text:""}

        Text{id:text2;  y:20;text:""}

        Text{id:text3;  y:40;text:""}

    }

    MouseArea{

        anchors.fill:parent;

        onClicked:{

            //if(pgcurIndex<group.membersCount()-1){//这里两种方法都可以

            if(pgcurIndex<group.members.length-1){

                pgcurIndex++;

            }else{

                pgcurIndex=0;

            }

            //显示信息

            text1.text="PersonGroupindex:"+pgcurIndex;

            varperson=group.member(pgcurIndex);

            text2.text="Personname:"+person.name;

            text3.text="Personage:"  +person.age;

        }

    }

}

 

说明:

这里导出了两个类Person PersonGroup PersonGroup保存来一个Person的组,

我们通过导出的函数来调用类面的成员,获取成员的信息.

 

 

 

QMLc++交互学习笔记() 关于qt c++中创建对象,QML获取此对象数据问题

1.假设

1.c++中创建一个Person的对象,

2.QML中获取并显示数据

3.c++中改变数据后,显示的数据能进行相应的改变

也就是说我们实际是在c++new一个对象出来,而把这个对象的数据在QML里面进行显示

 

2.具体代码

 

 

//person.h

#ifndefPERSON_H

#definePERSON_H

#include

#include

#include

#include

classPerson:publicQObject

{

    Q_OBJECT

    Q_PROPERTY(QStringnameREADgetNameWRITEsetNameNOTIFYsendNameChange)

    Q_PROPERTY(intageREADgetAgeWRITEsetAgeNOTIFYsendAgeChange)

public:

    explicitPerson(QObject*parent=0);

    QStringgetName(void)const;

    voidsetName(constQString&name);

    intgetAge(void);

    voidsetAge(intage);

    //一个简单的函数,获取蓝色

    Q_INVOKABLEQColorgetColor(void)const;

    Q_INVOKABLEvoidchangeNameAndAge(void);

signals:

    voidsendNameChange(void);

    voidsendAgeChange(void);

private:

    QString     m_Name;

    int         m_Age;

};

#endif//PERSON_H

 

//person.cpp

#include"person.h"

//---------------------------------

//

Person::Person(QObject*parent):

    QObject(parent),m_Name("unknow"),m_Age(0)

{

}

//---------------------------------

//

QStringPerson::getName(void)const

{

    returnm_Name;

}

//---------------------------------

//

voidPerson::setName(constQString&name)

{

    m_Name=name;

    emitsendNameChange();

}

//---------------------------------

//

intPerson::getAge(void)

{

    returnm_Age;

}

//---------------------------------

//

voidPerson::setAge(intage)

{

    m_Age=age;

    emitsendAgeChange();

}

//---------------------------------

//

QColorPerson::getColor(void)const

{

    returnQColor(Qt::blue);

}

//---------------------------------

//

voidPerson::changeNameAndAge(void)

{

    setName("Luly");

    setAge(31);

}

 

//main.cpp

#include

#include

#include

#include

#include

#include"person.h"

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    PersontmpPerson;

    tmpPerson.setName("Tom");

    tmpPerson.setAge(25);

    QDeclarativeViewqmlView;

    qmlView.rootContext()->setContextProperty("ps",&tmpPerson);

    qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml"));

    qmlView.show();

    returna.exec();

}

 

//UICtest.qml

importQt4.7

Rectangle{

    width:640

    height:480

    Text{text:"Personname:"+ps.name;}

    Text{y:20;text:"Personage:"+ps.age;}

    Rectangle{x:20;y:40;  width:20;height:20;color:ps.getColor();}

    MouseArea{

        anchors.fill:parent;

        //当鼠标按下后改变名字和年龄

        onClicked:{ps.changeNameAndAge();}

    }

}

 

 

说明:

我们在c++中创建来一个对象,并且在把这个对象导出给QML调用用,我们设置来属性,QML中可以直接使用属性来进行赋值.

 

QMLc++交互学习笔记()

1.假设这样一种情况

我这里由一个Wideget 继承自QWidget上面添加来一个QLabel, 一个QPushButton

我如何把这个Wideget放到QML中使用,那么我当QPushButton 按下后我怎么在QML中进行处理呢?

我这里指出一种方法

Wideget 继承QGraphicsProxyWidget,对Wideget进行导出,在QML中创建

此对象,在他导出的信中进行处理,具体代码。

还有就是这个网址上说明来很多QMLc++之间通讯的方法,很悲剧的是我的assistant中却没有者部分,不知道版本低还是怎么的。

http://doc.qt.nokia.com/4.7-snapshot/qtbinding.html

 

 

2.具体代码

 

 

//widget.h

#ifndefWIDGET_H

#defineWIDGET_H

#include

#include

#include

#include

#include

classWidget:publicQGraphicsProxyWidget

{

    Q_OBJECT

public:

    explicitWidget(QGraphicsItem*parent=0);

    ~Widget();

    Q_INVOKABLEvoidchangeText(constQString&s);

signals:

    voidsendOnButton(void);

private:

    QPushButton*m_Btn;

    QLabel      *m_Label;

    QWidget     *m_MainWidget;

};

#endif//WIDGET_H

 

//widget.cpp

#include"widget.h"

Widget::Widget(QGraphicsItem*parent):

    QGraphicsProxyWidget(parent)

{

    m_MainWidget=newQWidget;

    m_Btn=newQPushButton(m_MainWidget);

    m_Label=newQLabel(m_MainWidget);

    m_Btn->setText("PushButton");

    m_Btn->setGeometry(10,10,100,30);

    m_Label->setGeometry(10,40,200,30);

    QObject::connect(m_Btn,SIGNAL(clicked()),this,SIGNAL(sendOnButton()));

    setWidget(m_MainWidget);

}

Widget::~Widget()

{

    deletem_MainWidget;

}

voidWidget::changeText(constQString&s)

{

    m_Label->setText(s);

    qDebug("callWidget::changeText");

}

 

//main.cpp

#include

#include

#include

#include

#include

#include"widget.h"

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    qmlRegisterType<Widget>("UIWidget",1,0,"Widget");

    QDeclarativeViewqmlView;

    qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml"));

    qmlView.show();

    returna.exec();

}

 

//UICtest.qml

importQt4.7

importUIWidget1.0

Rectangle{

    width:640

    height:480

    color:"black"

    Widget{id:uiwidget;x:100;y:100;width:400;height:100;

        //关键在这里,当一个信号导出后他的相应的名字就是第1个字母大写,前面在加上on

        //例如clicked--onClicked   colorchange--onColorchange;

        onSendOnButton:{uiwidget.changeText(textinput.text);}

    }

    Rectangle{

        x:100;y:20;width:400;height:30;  color:"blue"

        TextInput{id:textinput;anchors.fill:parent;color:"white"}

    }

}

说明:

这里实现的是当QPushButton按钮按下后,获取QMLTextInput上的文本,

QLabel进行设置,关键点在于Widget中的信号函数sendOnButton, 他导出后在QML

将引发的是onSendOnButton 只要在QML中对这个编写处理就可以实现,具体看代码。

QMLc++交互学习笔记() qt c++直接调用QML中的函数, 直接设置属性

QT 2011-01-11 10:18:01 阅读392 评论0 字号:大中订阅

1.这里主要是介绍,如何在c++中调用QML中的函数和设置QML中的属性的问题

 

2.具体代码

 

 

//UICtest.qml

importQt4.7

Rectangle{

    id:mainWidget;

    width:640

    height:480

    functioncallbyc(v)

    {

        mainWidget.color=v;

        return"finish";

    }

    Rectangle{

        id:secondRect;

        x:100;

        y:20;

        width:400;

        height:300;

        Rectangle{

            x:10;

            y:20;

            width:30;

            height:40;

            color:"#FF035721"

            Text  {

                objectName:"NeedFindObj";

                anchors.fill:parent;

                text:"";

            }

        }

    }

}

 

//main.cpp

#include

#include

#include

#include

#include

#include

#include

intmain(intargc,char*argv[])

{

    QApplicationa(argc,argv);

    QDeclarativeViewqmlView;

    qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml"));

    qmlView.show();

    //获取根节点,就是QMLidmainWidget的节点

    QDeclarativeItem*item=qobject_cast<QDeclarativeItem*>(qmlView.rootObject());

    item->setProperty("color",QVariant("blue"));

    //查找到我们需要的节点根均objectnameNeedFindObj来获得,并设置他的文本属性

    QDeclarativeItem*item1=item->findChild<QDeclarativeItem*>("NeedFindObj");

    if(item1)

    {

        item1->setProperty("text",QVariant("OK"));

    }

    //调用QML中的函数,分别是函数所在的对象,函数名,返回值,参数

    QVariantreturnVar;

    QVariantarg1="blue";

    QMetaObject::invokeMethod(item,"callbyc",

    Q_RETURN_ARG(QVariant,returnVar),Q_ARG(QVariant,arg1));

    qDebug("%s",returnVar.toString().toLocal8Bit().data());

    returna.exec();

}

 

 

 

 

说明:

这里的根节点是idmainWidget的矩形元素,那么在C++中获取根节点后就可以,直接的设置他的属性了。其他属性也可以同样,调用指定节点内的函数是通过QMetaObject中的invokeMethod 来进行调用的。

 

最后所有关于QMLc++交互部分就基本写完,如果想要更多的东西,或者一些其他方法,强烈看看

http://doc.qt.nokia.com/4.7-snapshot/qtbinding.html,或者帮助文档,(究竟是不是我的文档里面没有还是怎么的)

 

你可能感兴趣的:(qml,c++,qt,signal,工作,c,文档)