专业的QValidator和QML UI更改

时间:2015-11-16 14:31:14

标签: c++ validation qml qt5 qtquick2

我正在学习Qt 5.5和QML。

框架很强大,有时候有很多方法可以做一件事。我认为有些可能比其他人更有效率,而且我想知道何时以及为何使用一个而不是另一个。 我想要一个可以解释所做出的选择的答案。当我使用新代码时,如果在C ++端有用,可以使用C ++ 11和C ++ 14语法。

要解决的问题是:
我有一个TextField链接到一个可以弹出FileDialog的按钮。我希望TextField中的文字无效时为red,否则保持不变(我将其设置为green因为我不知道如何获取“默认”颜色)。 TextField的值将在C ++端使用,并在应用程序退出时保持不变。

我使用自定义QValidator编写了一个版本,QML方面的一些属性,使用onTextChanged:onValidatorChanged:来修改文本的颜色。根据Q ++中的属性(valid)设置文本颜色,该属性是从C ++端(在验证器中)设置的。要设置属性,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_attachedObjectQVariant,因为我最初希望引用QML实例而不是其名称。

由于验证器仅关注验证,因此它不包含有关其验证的数据的任何状态 因为我必须得到TextField的值才能在应用程序中执行某些操作,所以我构建了另一个类以在更改时保存值:MyClass。我将其视为我的控制器。目前,我将数据直接存储在应用程序对象中,可以将其视为我的模型。这将在未来发生变化。

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
  • byname树搜索找到调用者(看起来效率不高;可能不是)
  • 在几个C ++实例和部分QML中的意大利面条式编码

我可以想到其他5个解决方案:

  • 摆脱自定义验证器,只保留onTextChanged:因为我们无法摆脱QML端的信令。大部分内容都在MyClass
  • 中完成
  • 修补Qt,以便为Behavior之外的其他内容实现属性值写入拦截器(请参阅here
  • 注册C ++类型以附加到QML对象。 (见here
  • 注册一个类型并将其用作控制器和数据结构(类似bean),然后传递给模型(参见here
  • 手动使用信号,就像我已经使用signalDirectoryChanged
  • 一样

嗯,正如你所看到的,做事的多种方式令人困惑,所以赞赏了senpai的建议。

完整的源代码here

0 个答案:

没有答案