Alterações especializadas na QValidator e na interface QML

Estou aprendendo Qt 5.5 e QML.

A estrutura é poderosa e, às vezes, existem muitas maneiras de fazer uma coisa. Eu acho que alguns são provavelmente mais eficientes que outros, eEu gostaria de entender quando e por que usar um ao invés de outro.
Eu gostaria de uma resposta que possa explicar as escolhas feitas. Como estou trabalhando com um novo código, a sintaxe C ++ 11 e C ++ 14 pode ser usada se útil no lado C ++.

Problema a resolver é:
eu tenho umaTextField vinculado a um botão que pode abrir umFileDialog. Eu quero o texto noTextField ser estarred quando é inválido e permaneceu inalterado de outra forma (eu o defino comogreen porque não sei como obter a cor "padrão"). O valor doTextField deve ser usado no lado do C ++ e persiste quando o aplicativo é encerrado.

Eu codifiquei uma versão usando um personalizadoQValidator, algumas propriedades no lado da QML, um usoonTextChanged: eonValidatorChanged: para modificar a cor do texto. A cor do texto é definida de acordo com uma propriedade (valid) no QML configurado no lado do C ++ (no validador). Para definir a propriedade, o C ++ precisa localizar pelo nome o chamador (TextField nomeadodirectoryToSave) porque ainda não encontrei uma maneira de passar o próprio objeto como argumento.

Aqui está o código QML contido emMainForm.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;
        }
    }

O código do validador customizado:

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();
};

Associado a estas definições:

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 é umQVariant porque eu queria que a instância QML fosse referenciada em vez de seu nome, inicialmente.

Como o validador está preocupado apenas com a validação, ele não contém nenhum estado sobre os dados que valida.
Como devo obter o valor doTextField para fazer algo no aplicativo, criei outra classe para salvar o valor quando ele mudar:MyClass. Eu vejo isso como meucontrolador. Atualmente, eu armazeno dados diretamente no objeto do aplicativo, que podem ser vistos como meusmodelo. Isso vai mudar no futuro.

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass() {}

public slots:
    void cppSlot(const QString &string) {
       ((LAACApplication *) qApp)->setLocalDataDirectory(string);
    }
};

As instâncias do controladorMyClass e validadorQDirectoryValidator são criados durante a inicialização do aplicativo com o seguinte código:

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);

o//delete serve apenas para descobrir o que acontece quando a instância é excluída ou não.

omain.qml amarra as coisas:

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)

    }

E, finalmente, a cola MainForm.ui.qml:

Item {

    // ...
    property alias directoryToSavePlaceholderText: directoryToSave.placeholderText
    property alias directoryToSaveText: directoryToSave.text

    // ...
}

Não estou satisfeito por ter:

sujeira emonValidatorChanged: certifique-se de inicializar a interface do usuário com a cor corretaárvore de nomes pesquisando para encontrar o chamador (parece ineficiente; pode não ser)codificação do tipo espaguete entre várias instâncias do C ++ e partes da QML

Eu posso pensar em 5 outras soluções:

livrar-se do validador personalizado e manter apenasonTextChanged: porque não podemos nos livrar da sinalização do lado da QML. A maioria das coisas é feita emMyClassremendar Qt para implementar uminterceptor de gravação do valor da propriedade para algo mais do queBehavior (Vejoaqui)registrando um tipo C ++ para anexar ao objeto QML. (Vejoaqui)registrar um tipo e usá-lo como um controlador e uma estrutura de dados (semelhante a um bean), para passar para o modelo posteriormente (consulteaqui)usando sinais manualmente como eu já faço comsignalDirectoryChanged

Bem, como você vê, as maneiras pletóricas de fazer as coisas são confusas, então os conselhos da senpai são apreciados.

Código fonte completo disponívelaqui.

questionAnswers(1)

yourAnswerToTheQuestion