专门的 QValidator 和 QML UI 更改
Specialized QValidator and QML UI changes
我正在学习 Qt 5.5 和 QML。
框架很强大,有时候做一件事有很多种方法。我认为有些可能比其他的更有效,我想了解何时以及为什么使用一个而不是另一个。
我想要一个可以解释所做选择的答案。当我使用新代码时,如果在 C++ 方面有用,可以使用 C++ 11 和 C++ 14 语法。
要解决的问题是:
我有一个 TextField
链接到一个可以弹出 FileDialog
的按钮。我希望 TextField
中的文本在无效时为 red
,否则保持不变(我将其设置为 green
因为我不知道如何获取 "default" 颜色)。 TextField
的值将在 C++ 端使用,并在应用程序退出时保留。
我使用自定义 QValidator
编写了一个版本,QML 端的一些属性,使用 onTextChanged:
和 onValidatorChanged:
修改文本的颜色。文本颜色是根据从 C++ 端(在验证器中)设置的 QML 中的 属性 (valid
) 设置的。要设置 属性,C++ 必须通过名称查找调用者(TextField
名为 directoryToSave
),因为我还没有找到将对象本身作为参数传递的方法。
这里是MainForm.ui.qml
中包含的QML代码:
TextField {
property bool valid: false
id: directoryToSave
objectName: 'directoryToSave'
Layout.fillWidth:true
placeholderText: qsTr("Enter a directory path to save to the peer")
validator: directoryToSaveValidator
onTextChanged: if (valid) textColor = 'green'; else textColor = 'red';
onValidatorChanged:
{
directoryToSave.validator.attachedObject = directoryToSave.objectName;
// forces validation
var oldText = text;
text = text+' ';
text = oldText;
}
}
自定义验证码:
class QDirectoryValidator : public QValidator
{
Q_OBJECT
Q_PROPERTY(QVariant attachedObject READ attachedObject WRITE setAttachedObject NOTIFY attachedObjectChanged)
private:
QVariant m_attachedObject;
public:
explicit QDirectoryValidator(QObject* parent = 0);
virtual State validate(QString& input, int& pos) const;
QVariant attachedObject() const;
void setAttachedObject(const QVariant &attachedObject);
signals:
void attachedObjectChanged();
};
与这些定义相关联:
QVariant QDirectoryValidator::attachedObject() const
{
return m_attachedObject;
}
void QDirectoryValidator::setAttachedObject(const QVariant &attachedObject)
{
if (attachedObject != m_attachedObject)
{
m_attachedObject = attachedObject;
emit attachedObjectChanged();
}
}
QValidator::State QDirectoryValidator::validate(QString& input, int& pos) const
{
QString attachedObjectName = m_attachedObject.toString();
QObject *rootObject = ((LAACApplication *) qApp)->engine().rootObjects().first();
QObject *qmlObject = rootObject ? rootObject->findChild<QObject*>(attachedObjectName) : 0;
// Either the directory exists, then it is _valid_
// or the directory does not exist (maybe the string is an invalid directory name, or whatever), and then it is _invalid_
QDir dir(input);
bool isAcceptable = (dir.exists());
if (qmlObject) qmlObject->setProperty("valid", isAcceptable);
return isAcceptable ? Acceptable : Intermediate;
}
m_attachedObject
是一个 QVariant
因为我希望最初引用 QML 实例而不是它的名称。
由于验证器只关心验证,因此它不包含有关它验证的数据的任何状态。
因为我必须获取 TextField
的值才能在应用程序中执行某些操作,所以我构建了另一个 class 以在它更改时保存值:MyClass
。我将其视为我的 控制器 。目前,我将数据直接存储在应用程序对象中,可以看作是我的 model。这将在未来改变。
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass() {}
public slots:
void cppSlot(const QString &string) {
((LAACApplication *) qApp)->setLocalDataDirectory(string);
}
};
控制器 MyClass
和验证器 QDirectoryValidator
的实例是在应用程序初始化期间使用以下代码创建的:
MyClass * myClass = new MyClass;
QObject::connect(rootObject, SIGNAL(signalDirectoryChanged(QString)),
myClass, SLOT(cppSlot(QString)));
//delete myClass;
QValidator* validator = new QDirectoryValidator();
QVariant variant;
variant.setValue(validator);
rootObject->setProperty("directoryToSaveValidator", variant);
//delete
仅用于发现实例被删除时发生的情况。
main.qml
将事物联系在一起:
ApplicationWindow {
id: thisIsTheMainWindow
objectName: "thisIsTheMainWindow"
// ...
property alias directoryToSaveText: mainForm.directoryToSaveText
property var directoryToSaveValidator: null
signal signalDirectoryChanged(string msg)
// ...
FileDialog {
id: fileDialog
title: "Please choose a directory"
folder: shortcuts.home
selectFolder: true
onAccepted: {
var url = fileDialog.fileUrls[0]
mainForm.directoryToSaveText = url.slice(8)
}
onRejected: {
//console.log("Canceled")
}
Component.onCompleted: visible = false
}
onDirectoryToSaveTextChanged: thisIsTheMainWindow.signalDirectoryChanged(directoryToSaveText)
}
最后,MainForm.ui.qml 胶水:
Item {
// ...
property alias directoryToSavePlaceholderText: directoryToSave.placeholderText
property alias directoryToSaveText: directoryToSave.text
// ...
}
我不满意:
- 污垢
onValidatorChanged:
以确保使用正确的颜色 初始化 UI
- 通过名称树搜索来查找调用者(看起来效率低;可能不是)
- 几个 C++ 实例和部分 QML 之间的类似意大利面条的编码
我能想到其他 5 个解决方案:
- 摆脱自定义验证器,只保留
onTextChanged:
因为我们无法摆脱来自 QML 端的信号。大多数事情都在 MyClass
中完成
- 修补 Qt 以实现 属性 值写入拦截器 除了
Behavior
(参见 here)
- 正在注册一个 C++ 类型以附加到 QML 对象。 (参见 here)
- 注册一个类型并将其用作控制器和数据结构(类似 bean),然后传递给模型(参见 here)
- 手动使用信号,就像我已经对
signalDirectoryChanged
所做的那样
好吧,如您所见,过多的做事方式令人困惑,因此非常感谢前辈的建议。
完整的源代码可用 here。
我不认为一个单一的答案可以解决你所有的问题,但我仍然认为一些关于应用程序结构的指南可以帮助你前进。
据我所知,没有集中讨论应用程序结构的地方。实际上,QML 中也没有关于 UI 结构的建议(例如参见 [=60=] 讨论)。也就是说,我们可以确定 QML 应用程序中的一些常见模式和选择,我们将在下面进一步讨论。
在开始之前,我想强调一个重要的方面。 QML 与 C++ 相差无几。 QML 基本上是 QObject
派生对象的对象树,其生命周期由 QMLEngine
实例控制。从这个意义上说,一段代码像
TextField {
id: directoryToSave
placeholderText: qsTr("placeHolder")
validator: IntValidator { }
}
它与用普通命令式 C++ 语法编写的 QLineEdit
和 Validator
没有什么不同。余生如说。鉴于此,用纯 C++ 实现验证器是错误的:验证器是 TextField
的一部分,并且应该具有与其一致的生命周期。在这种特定情况下,registering a new type 是最好的方法。生成的代码更易于阅读和维护。
这个案例比较特殊。 validator
属性 接受从 Validator
派生的对象(参见声明 here and some usages here, here and here)。因此,不是简单地定义一个 Object
派生类型,我们可以定义一个 QValidator
派生类型并用它代替 IntValidator
(或其他 QML 验证类型)。
我们的 DirectoryValidator
头文件如下所示:
#ifndef DIRECTORYVALIDATOR_H
#define DIRECTORYVALIDATOR_H
#include <QValidator>
#include <QDir>
class DirectoryValidator : public QValidator
{
Q_OBJECT
public:
DirectoryValidator(QObject * parent = 0);
void fixup(QString & input) const override;
QLocale locale() const;
void setLocale(const QLocale & locale);
State validate(QString & input, int & pos) const override;
};
#endif
实现文件是这样的:
#include "directoryvalidator.h"
DirectoryValidator::DirectoryValidator(QObject *parent): QValidator(parent)
{
// NOTHING
}
void DirectoryValidator::fixup(QString & input) const
{
// try to fix the string??
QValidator::fixup(input);
}
QLocale DirectoryValidator::locale() const
{
return QValidator::locale();
}
void DirectoryValidator::setLocale(const QLocale & locale)
{
QValidator::setLocale(locale);
}
QValidator::State DirectoryValidator::validate(QString & input, int & pos) const
{
Q_UNUSED(pos) // change cursor position if you like...
if(QDir(input).exists())
return Acceptable;
return Intermediate;
}
现在您可以像这样在 main
中注册新类型:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<DirectoryValidator>("DirValidator", 1, 0, "DirValidator");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
您的 QML 代码可以这样重写:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import DirValidator 1.0 // import the new type
Window {
id: main
visible: true
width: 600
height: 600
DirValidator { // external declaration
id: dirVal
}
Column {
anchors.fill: parent
TextField {
id: first
validator: dirVal
textColor: acceptableInput ? "black" : "red"
}
TextField {
validator: DirValidator { } // declaration inline
textColor: acceptableInput ? "black" : "red"
}
TextField {
validator: DirValidator { } // declaration inline
textColor: acceptableInput ? "black" : "red"
}
}
}
如您所见,用法变得更加直接。 C++ 代码更简洁,但 QML 代码也更简洁。您不需要在自己周围传递数据。这里我们使用与 TextField
完全相同的 acceptableInput
,因为它是由与其关联的 Validator
设置的。
通过注册不是从 Validator
派生的另一种类型可以获得相同的效果 - 失去与 acceptableInput
的关联。看下面的代码:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import ValidationType 1.0
Window {
id: main
visible: true
width: 600
height: 600
ValidationType {
id: validator
textToCheck: first.text
}
TextField {
id: first
validator: dirVal
textColor: validator.valid ? "black" : "red" // "valid" used in place of "acceptableInput"
}
}
此处 ValidationType
可以用两个 Q_PROPERTY
元素定义:
- a
QString
作为 textToCheck
暴露于 QML
- a
bool
属性 暴露为 valid
到 QML
当绑定到 first.text
时,属性 被设置并在 TextField
文本更改时重置。在更改时,您可以检查文本,例如使用完全相同的代码,然后更新 valid
属性。参见 this
回答或上面的注册link以获取有关Q_PROPERTY
更新的详细信息。我把这种方法的实现留给你,作为练习。
最后,当谈到 services-like/global objects/types 时,使用非实例化/单例类型可能是正确的方法。在这种情况下,我会让 documentation 为我说话:
A QObject singleton type can be interacted with in a manner similar to any other QObject or instantiated type, except that only one (engine constructed and owned) instance will exist, and it must be referenced by type name rather than id. Q_PROPERTYs of QObject singleton types may be bound to, and Q_INVOKABLE functions of QObject module APIs may be used in signal handler expressions. This makes singleton types an ideal way to implement styling or theming, and they can also be used instead of ".pragma library" script imports to store global state or to provide global functionality.
qmlRegisterSingletonType
is the function to prefer. It's the approach used also in the "Quick Forecast" app i.e. the Digia showcase app. See the main
and the related ApplicationInfo
类型。
还有context properties are particularly useful. Since they are added to the root context (see the link) they are available in all the QML files and can be used also as global objects. Classes to access DBs, classes to access web services or similar are eligible to be added as context properties. Another useful case is related to models: a C++ model, like an AbstractListModel
can be registered as a context property and used as the model of a view, e.g. a ListView
. See the example available here.
Connections
type can be used to connect signals emitted by both context properties and register types (obviously also the singleton one). Whereas signals, Q_INVOKABLE
functions and SLOT
s can be directly called from QML to trigger other C++ slots like partially discussed here.
总而言之,使用 objectName
并从 C++ 访问 QML 是可能且可行的,但通常不鼓励(参见 QML Camera
类型的警告 here). Also, when necessary and possible, QML/C++ integration is favoured via dedicated properties, see for instance the mediaObject
属性。使用(单例)注册类型、上下文属性并通过 Connections
类型、Q_INVOKABLE
、SLOT
s 和 SIGNAL
s 将它们连接到 QML 应该可以启用大多数用例.
我正在学习 Qt 5.5 和 QML。
框架很强大,有时候做一件事有很多种方法。我认为有些可能比其他的更有效,我想了解何时以及为什么使用一个而不是另一个。
我想要一个可以解释所做选择的答案。当我使用新代码时,如果在 C++ 方面有用,可以使用 C++ 11 和 C++ 14 语法。
要解决的问题是:
我有一个 TextField
链接到一个可以弹出 FileDialog
的按钮。我希望 TextField
中的文本在无效时为 red
,否则保持不变(我将其设置为 green
因为我不知道如何获取 "default" 颜色)。 TextField
的值将在 C++ 端使用,并在应用程序退出时保留。
我使用自定义 QValidator
编写了一个版本,QML 端的一些属性,使用 onTextChanged:
和 onValidatorChanged:
修改文本的颜色。文本颜色是根据从 C++ 端(在验证器中)设置的 QML 中的 属性 (valid
) 设置的。要设置 属性,C++ 必须通过名称查找调用者(TextField
名为 directoryToSave
),因为我还没有找到将对象本身作为参数传递的方法。
这里是MainForm.ui.qml
中包含的QML代码:
TextField {
property bool valid: false
id: directoryToSave
objectName: 'directoryToSave'
Layout.fillWidth:true
placeholderText: qsTr("Enter a directory path to save to the peer")
validator: directoryToSaveValidator
onTextChanged: if (valid) textColor = 'green'; else textColor = 'red';
onValidatorChanged:
{
directoryToSave.validator.attachedObject = directoryToSave.objectName;
// forces validation
var oldText = text;
text = text+' ';
text = oldText;
}
}
自定义验证码:
class QDirectoryValidator : public QValidator
{
Q_OBJECT
Q_PROPERTY(QVariant attachedObject READ attachedObject WRITE setAttachedObject NOTIFY attachedObjectChanged)
private:
QVariant m_attachedObject;
public:
explicit QDirectoryValidator(QObject* parent = 0);
virtual State validate(QString& input, int& pos) const;
QVariant attachedObject() const;
void setAttachedObject(const QVariant &attachedObject);
signals:
void attachedObjectChanged();
};
与这些定义相关联:
QVariant QDirectoryValidator::attachedObject() const
{
return m_attachedObject;
}
void QDirectoryValidator::setAttachedObject(const QVariant &attachedObject)
{
if (attachedObject != m_attachedObject)
{
m_attachedObject = attachedObject;
emit attachedObjectChanged();
}
}
QValidator::State QDirectoryValidator::validate(QString& input, int& pos) const
{
QString attachedObjectName = m_attachedObject.toString();
QObject *rootObject = ((LAACApplication *) qApp)->engine().rootObjects().first();
QObject *qmlObject = rootObject ? rootObject->findChild<QObject*>(attachedObjectName) : 0;
// Either the directory exists, then it is _valid_
// or the directory does not exist (maybe the string is an invalid directory name, or whatever), and then it is _invalid_
QDir dir(input);
bool isAcceptable = (dir.exists());
if (qmlObject) qmlObject->setProperty("valid", isAcceptable);
return isAcceptable ? Acceptable : Intermediate;
}
m_attachedObject
是一个 QVariant
因为我希望最初引用 QML 实例而不是它的名称。
由于验证器只关心验证,因此它不包含有关它验证的数据的任何状态。
因为我必须获取 TextField
的值才能在应用程序中执行某些操作,所以我构建了另一个 class 以在它更改时保存值:MyClass
。我将其视为我的 控制器 。目前,我将数据直接存储在应用程序对象中,可以看作是我的 model。这将在未来改变。
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass() {}
public slots:
void cppSlot(const QString &string) {
((LAACApplication *) qApp)->setLocalDataDirectory(string);
}
};
控制器 MyClass
和验证器 QDirectoryValidator
的实例是在应用程序初始化期间使用以下代码创建的:
MyClass * myClass = new MyClass;
QObject::connect(rootObject, SIGNAL(signalDirectoryChanged(QString)),
myClass, SLOT(cppSlot(QString)));
//delete myClass;
QValidator* validator = new QDirectoryValidator();
QVariant variant;
variant.setValue(validator);
rootObject->setProperty("directoryToSaveValidator", variant);
//delete
仅用于发现实例被删除时发生的情况。
main.qml
将事物联系在一起:
ApplicationWindow {
id: thisIsTheMainWindow
objectName: "thisIsTheMainWindow"
// ...
property alias directoryToSaveText: mainForm.directoryToSaveText
property var directoryToSaveValidator: null
signal signalDirectoryChanged(string msg)
// ...
FileDialog {
id: fileDialog
title: "Please choose a directory"
folder: shortcuts.home
selectFolder: true
onAccepted: {
var url = fileDialog.fileUrls[0]
mainForm.directoryToSaveText = url.slice(8)
}
onRejected: {
//console.log("Canceled")
}
Component.onCompleted: visible = false
}
onDirectoryToSaveTextChanged: thisIsTheMainWindow.signalDirectoryChanged(directoryToSaveText)
}
最后,MainForm.ui.qml 胶水:
Item {
// ...
property alias directoryToSavePlaceholderText: directoryToSave.placeholderText
property alias directoryToSaveText: directoryToSave.text
// ...
}
我不满意:
- 污垢
onValidatorChanged:
以确保使用正确的颜色 初始化 UI
- 通过名称树搜索来查找调用者(看起来效率低;可能不是)
- 几个 C++ 实例和部分 QML 之间的类似意大利面条的编码
我能想到其他 5 个解决方案:
- 摆脱自定义验证器,只保留
onTextChanged:
因为我们无法摆脱来自 QML 端的信号。大多数事情都在MyClass
中完成
- 修补 Qt 以实现 属性 值写入拦截器 除了
Behavior
(参见 here) - 正在注册一个 C++ 类型以附加到 QML 对象。 (参见 here)
- 注册一个类型并将其用作控制器和数据结构(类似 bean),然后传递给模型(参见 here)
- 手动使用信号,就像我已经对
signalDirectoryChanged
所做的那样
好吧,如您所见,过多的做事方式令人困惑,因此非常感谢前辈的建议。
完整的源代码可用 here。
我不认为一个单一的答案可以解决你所有的问题,但我仍然认为一些关于应用程序结构的指南可以帮助你前进。
据我所知,没有集中讨论应用程序结构的地方。实际上,QML 中也没有关于 UI 结构的建议(例如参见 [=60=] 讨论)。也就是说,我们可以确定 QML 应用程序中的一些常见模式和选择,我们将在下面进一步讨论。
在开始之前,我想强调一个重要的方面。 QML 与 C++ 相差无几。 QML 基本上是 QObject
派生对象的对象树,其生命周期由 QMLEngine
实例控制。从这个意义上说,一段代码像
TextField {
id: directoryToSave
placeholderText: qsTr("placeHolder")
validator: IntValidator { }
}
它与用普通命令式 C++ 语法编写的 QLineEdit
和 Validator
没有什么不同。余生如说。鉴于此,用纯 C++ 实现验证器是错误的:验证器是 TextField
的一部分,并且应该具有与其一致的生命周期。在这种特定情况下,registering a new type 是最好的方法。生成的代码更易于阅读和维护。
这个案例比较特殊。 validator
属性 接受从 Validator
派生的对象(参见声明 here and some usages here, here and here)。因此,不是简单地定义一个 Object
派生类型,我们可以定义一个 QValidator
派生类型并用它代替 IntValidator
(或其他 QML 验证类型)。
我们的 DirectoryValidator
头文件如下所示:
#ifndef DIRECTORYVALIDATOR_H
#define DIRECTORYVALIDATOR_H
#include <QValidator>
#include <QDir>
class DirectoryValidator : public QValidator
{
Q_OBJECT
public:
DirectoryValidator(QObject * parent = 0);
void fixup(QString & input) const override;
QLocale locale() const;
void setLocale(const QLocale & locale);
State validate(QString & input, int & pos) const override;
};
#endif
实现文件是这样的:
#include "directoryvalidator.h"
DirectoryValidator::DirectoryValidator(QObject *parent): QValidator(parent)
{
// NOTHING
}
void DirectoryValidator::fixup(QString & input) const
{
// try to fix the string??
QValidator::fixup(input);
}
QLocale DirectoryValidator::locale() const
{
return QValidator::locale();
}
void DirectoryValidator::setLocale(const QLocale & locale)
{
QValidator::setLocale(locale);
}
QValidator::State DirectoryValidator::validate(QString & input, int & pos) const
{
Q_UNUSED(pos) // change cursor position if you like...
if(QDir(input).exists())
return Acceptable;
return Intermediate;
}
现在您可以像这样在 main
中注册新类型:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<DirectoryValidator>("DirValidator", 1, 0, "DirValidator");
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
您的 QML 代码可以这样重写:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import DirValidator 1.0 // import the new type
Window {
id: main
visible: true
width: 600
height: 600
DirValidator { // external declaration
id: dirVal
}
Column {
anchors.fill: parent
TextField {
id: first
validator: dirVal
textColor: acceptableInput ? "black" : "red"
}
TextField {
validator: DirValidator { } // declaration inline
textColor: acceptableInput ? "black" : "red"
}
TextField {
validator: DirValidator { } // declaration inline
textColor: acceptableInput ? "black" : "red"
}
}
}
如您所见,用法变得更加直接。 C++ 代码更简洁,但 QML 代码也更简洁。您不需要在自己周围传递数据。这里我们使用与 TextField
完全相同的 acceptableInput
,因为它是由与其关联的 Validator
设置的。
通过注册不是从 Validator
派生的另一种类型可以获得相同的效果 - 失去与 acceptableInput
的关联。看下面的代码:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import ValidationType 1.0
Window {
id: main
visible: true
width: 600
height: 600
ValidationType {
id: validator
textToCheck: first.text
}
TextField {
id: first
validator: dirVal
textColor: validator.valid ? "black" : "red" // "valid" used in place of "acceptableInput"
}
}
此处 ValidationType
可以用两个 Q_PROPERTY
元素定义:
- a
QString
作为textToCheck
暴露于 QML
- a
bool
属性 暴露为valid
到 QML
当绑定到 first.text
时,属性 被设置并在 TextField
文本更改时重置。在更改时,您可以检查文本,例如使用完全相同的代码,然后更新 valid
属性。参见 this
回答或上面的注册link以获取有关Q_PROPERTY
更新的详细信息。我把这种方法的实现留给你,作为练习。
最后,当谈到 services-like/global objects/types 时,使用非实例化/单例类型可能是正确的方法。在这种情况下,我会让 documentation 为我说话:
A QObject singleton type can be interacted with in a manner similar to any other QObject or instantiated type, except that only one (engine constructed and owned) instance will exist, and it must be referenced by type name rather than id. Q_PROPERTYs of QObject singleton types may be bound to, and Q_INVOKABLE functions of QObject module APIs may be used in signal handler expressions. This makes singleton types an ideal way to implement styling or theming, and they can also be used instead of ".pragma library" script imports to store global state or to provide global functionality.
qmlRegisterSingletonType
is the function to prefer. It's the approach used also in the "Quick Forecast" app i.e. the Digia showcase app. See the main
and the related ApplicationInfo
类型。
还有context properties are particularly useful. Since they are added to the root context (see the link) they are available in all the QML files and can be used also as global objects. Classes to access DBs, classes to access web services or similar are eligible to be added as context properties. Another useful case is related to models: a C++ model, like an AbstractListModel
can be registered as a context property and used as the model of a view, e.g. a ListView
. See the example available here.
Connections
type can be used to connect signals emitted by both context properties and register types (obviously also the singleton one). Whereas signals, Q_INVOKABLE
functions and SLOT
s can be directly called from QML to trigger other C++ slots like partially discussed here.
总而言之,使用 objectName
并从 C++ 访问 QML 是可能且可行的,但通常不鼓励(参见 QML Camera
类型的警告 here). Also, when necessary and possible, QML/C++ integration is favoured via dedicated properties, see for instance the mediaObject
属性。使用(单例)注册类型、上下文属性并通过 Connections
类型、Q_INVOKABLE
、SLOT
s 和 SIGNAL
s 将它们连接到 QML 应该可以启用大多数用例.