C++ 专门的QValidator和QML UI更改
我正在学习Qt5.5和QML 该框架功能强大,有时可以通过多种方式完成一件事。我认为一些可能比其他更有效,我想了解何时以及为什么使用一个而不是另一个。C++ 专门的QValidator和QML UI更改,c++,validation,qml,qt5,qtquick2,C++,Validation,Qml,Qt5,Qtquick2,我正在学习Qt5.5和QML 该框架功能强大,有时可以通过多种方式完成一件事。我认为一些可能比其他更有效,我想了解何时以及为什么使用一个而不是另一个。 我想要一个能解释所做选择的答案。当我使用新代码时,C++ 11和C++ 14语法可以在C++方面使用。 要解决的问题是: 我有一个文本字段链接到一个按钮,可以弹出一个文件对话框。我希望文本字段中的文本在无效时为红色,否则保持不变(我将其设置为绿色,因为我不知道如何获得“默认”颜色)。“代码>文本字段< /代码>的值将在C++侧使用,并且在应用程序
我想要一个能解释所做选择的答案。当我使用新代码时,C++ 11和C++ 14语法可以在C++方面使用。 要解决的问题是:
我有一个
文本字段
链接到一个按钮,可以弹出一个文件对话框
。我希望文本字段中的文本在无效时为红色
,否则保持不变(我将其设置为绿色
,因为我不知道如何获得“默认”颜色)。“代码>文本字段< /代码>的值将在C++侧使用,并且在应用程序退出时将继续执行。
我已经使用自定义的QValidator
编码了一个版本,QML端的一些属性,使用onTextChanged:
和onValidatorChanged:
修改了文本的颜色。根据C++中设置的QML(验证器)中的属性(<代码>有效< /代码>)设置文本颜色。要设置属性,C++必须按名称查找调用方(<代码>文本字段< /COD>命名为代码> DeReTrayToWave/Cuth>),因为我还没有找到一种方法将对象本身作为参数传递。
以下是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;
}
controllerMyClass
和validatorQDirectoryValidator
的实例是在应用程序初始化期间使用以下代码创建的:
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
// ...
}
我不满足于:
- 已更改
onvalidator中的污垢:
以确保使用正确的颜色初始化UI
- 按名称树搜索以查找调用者(看起来无效;可能无效)
- C++中几个实例与QML
部分的意大利面编码
我可以想出其他5种解决方案:
- 摆脱自定义验证器,只保留
onTextChanged:
,因为我们无法摆脱来自QML端的信号。大多数事情都是在MyClass
- 修补Qt以实现除
行为
之外的其他内容的属性值写入拦截器(请参阅)
注册一个C++类型以附加到QML对象。(见附件)
- 注册一个类型并将其用作控制器和数据结构(类似bean),以便随后传递到模型(请参阅)
- 手动使用信号,就像我在
signalDirectoryChanged
好吧,正如你所看到的,做事情的方式过于随意,令人困惑,所以森派的建议是值得赞赏的
完整的源代码可用。我不认为一个单一的答案可以解决您所有的问题,但我仍然认为一些关于应用程序结构的指导原则可以帮助您解决这些问题
恐怕没有讨论应用程序结构的中心位置。实际上,在QML中也没有关于UI结构的建议(例如,请参阅讨论)。也就是说,我们可以确定QML应用程序中的一些常见模式和选择,我们将在下面进一步讨论
在到达那里之前,我想强调一个重要方面。QML离C++不那么远。QML基本上是QObject
派生对象的对象树,其生存期由QMLEngine
实例控制。从这个意义上讲,一段代码
TextField {
id: directoryToSave
placeholderText: qsTr("placeHolder")
validator: IntValidator { }
}
<>代码与C++命令语法编写的<代码>验证器< /代码>没有什么不同。如前所述,除了一生。鉴于此,在C++中实现验证器是错误的:验证器是 TeXField的一部分,并且应该有一个与之一致的生存期。在这种特殊情况下,这是最好的方法。生成的代码更易于阅读和维护
现在,这个案子很特别。validator
属性接受从validator
派生的对象(请参见声明和一些用法,以及)。因此,我们可以定义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;
}
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();
}
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"
}
}
}
现在,您可以在main
中注册新类型,如下所示:
#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;
}
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();
}
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
,因为它是由与其关联的验证器设置的
通过注册另一个不是从验证器派生的类型
——失去与可接受输入
的关联,也可以获得相同的效果。请看以下代码:
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\u属性
元素定义:
- 一个
QString
以textToCheck
bool
属性作为valid
公开给QML
当绑定到first.text
时,当TextField
文本更改时,将设置并重置该属性。更改时,您可以检查文本,例如使用相同的代码,并更新valid
属性。看见
有关Q_属性
更新的详细信息,请回答或访问上面的注册链接。我把这个方法的实现交给你们,就像exerci一样