Изменения в специализированном интерфейсе QValidator и QML

Я изучаю Qt 5.5 и QML.

Каркас является мощным, и иногда есть много способов сделать одну вещь. Я думаю, что некоторые, вероятно, более эффективны, чем другие, иЯ хотел бы понять, когда и почему использовать один, а не другой.
Я хотел бы получить ответ, который может объяснить сделанный выбор. Поскольку я работаю с новым кодом, можно использовать синтаксис C ++ 11 и C ++ 14, если он полезен на стороне C ++.

Чтобы решить проблему это:
у меня естьTextField связан с кнопкой, которая может выдвинутьFileDialog, Я хочу текст вTextField бытьred когда он недействителен, и остался без изменений в противном случае (я установил егоgreen потому что я не знаю, как получить цвет по умолчанию). ЗначениеTextField должен использоваться на стороне C ++ и сохраняется при выходе из приложения.

Я закодировал версию, используя кастомQValidatorнекоторые свойства на стороне QML, использованиеonTextChanged: а такжеonValidatorChanged: изменить цвет текста. Цвет текста устанавливается в соответствии со свойством (valid) в QML, который устанавливается со стороны C ++ (в валидаторе). Чтобы установить свойство, C ++ должен найти по имени вызывающегоTextField названныйdirectoryToSave) потому что я еще не нашел способ передать сам объект в качестве аргумента.

Вот код QML, содержащийся вMainForm.ui.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 чтобы сделать что-то в приложении, я создал другой класс для сохранения значения при его изменении: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: чтобы быть уверенным, чтобы инициализировать интерфейс с правильным цветомпоиск по имени дерева, чтобы найти абонента (выглядит неэффективно; может не быть)спагетти-подобное кодирование среди нескольких экземпляров C ++ и частей QML

Я могу думать о 5 других решениях:

избавиться от пользовательского валидатора и сохранить толькоonTextChanged: потому что мы не можем избавиться от сигнализации со стороны QML. Большинство вещей сделано вMyClassисправление Qt для реализациизначение свойства записывает перехватчик для чего-то еще, чемBehavior (увидетьВот)регистрация типа C ++ для присоединения к объекту QML. (увидетьВот)зарегистрировать тип и использовать его как контроллер и структуру данных (в виде бобов) для последующей передачи на модель (см.Вот)используя сигналы вручную, как я уже делаю сsignalDirectoryChanged

Ну, как вы видите, плеторические способы делать вещи сбивают с толку, поэтому совет сэмпай ценится.

Полный исходный код доступенВот.

Ответы на вопрос(1)

Ваш ответ на вопрос