2

I'm trying to access a FileDialog control from the python file that starts the QQmlApplication engine in order to retrieve the file path property. I have set up a signal in the .qml file, however I cannot access the file dialog by id in the python file to set up the slot. The findChild method in application.py returns None. Here is the code:

application.py

import sys
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine, QQmlFileSelector

sys_argv = sys.argv
sys_argv += ['--style', 'material']
app = QGuiApplication(sys_argv)

window = QQmlApplicationEngine()
window.load("QML/main.qml")

fileDialog = window.findChild(QQmlFileSelector, "fileDialog")
print(fileDialog)

app.exec_()

Page1.qml

import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2

Page {
    width: 600
    height: 400

    header: Label {
        text: qsTr("Prepare Data")
        horizontalAlignment: Text.AlignHCenter
        font.pixelSize: Qt.application.font.pixelSize * 2
        padding: 10
    }

    Button {
        text: qsTr("Load data")
        anchors.centerIn: parent
        onClicked: fileDialog.visible = true
        padding: 10
    }

    signal folderSelected()

    FileDialog {
        id: fileDialog
        selectFolder: true
        title: qsTr("Select the data directory")
        folder: shortcuts.home
        onAccepted: {
            parent.folderSelected()
        }
    }
}

main.qml

import QtQuick 2.0
import QtQuick.Controls 2.12
import QtQuick.Controls.Material 2.12

ApplicationWindow{
    visible: true
    title: qsTr("Main window")
    width: 1000
    height: 800

    Material.theme: Material.Light
    Material.accent: Material.Orange

    SwipeView {
        id: swipeView
        anchors.fill: parent

        Page1 {
        }

        Page2 {
        }

        Page3 {
        }

    }
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
dkalev
  • 639
  • 4
  • 7

1 Answers1

1

In an old answer explain in the section Pushing References to QML how to update some python object from QML, that methodology is the one recommended by Qt and it is the one that should be used now. With your current method you need to establish an objectname that can be problematic in many cases.

So the solution is to create a QObject that we export to QML and update the qproperty, this will emit a signal that we connect to a slot where we can do the logic that we want. On the other hand FileDialog returns a url, so the property must be a QUrl:

main.qml

import os
import sys
from PySide2 import QtCore, QtGui, QtQml

class FileManager(QtCore.QObject):
    file_url_Changed = QtCore.Signal(QtCore.QUrl)

    def __init__(self, parent=None):
        super(FileManager, self).__init__(parent)
        self._file_url = QtCore.QUrl()

    def get_file_url(self):
        return self._file_url

    def set_file_url(self, file_url):
        if self._file_url != file_url:
            self._file_url = file_url
            self.file_url_Changed.emit(self._file_url)

    file_url = QtCore.Property(QtCore.QUrl, fget=get_file_url, fset=set_file_url, notify=file_url_Changed)

@QtCore.Slot(QtCore.QUrl)
def on_file_url_changed(file_url):
    print(file_url.toLocalFile())

if __name__ == '__main__':
    sys.argv += ['--style', 'material']
    app = QtGui.QGuiApplication(sys.argv)

    file_manager = FileManager()
    file_manager.file_url_Changed.connect(on_file_url_changed)

    engine = QtQml.QQmlApplicationEngine()
    engine.rootContext().setContextProperty("file_manager", file_manager)
    file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "QML", "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(file))
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

Page1.qml

import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2

Page {
    width: 600
    height: 400

    header: Label {
        text: qsTr("Prepare Data")
        horizontalAlignment: Text.AlignHCenter
        font.pixelSize: Qt.application.font.pixelSize * 2
        padding: 10
    }

    Button {
        text: qsTr("Load data")
        anchors.centerIn: parent
        onClicked: fileDialog.visible = true
        padding: 10
    }

    FileDialog {
        id: fileDialog
        selectFolder: true
        title: qsTr("Select the data directory")
        folder: shortcuts.home
        onAccepted: {
            file_manager.file_url = fileDialog.fileUrl // <---
        }
    }
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks, that works quite well! However, how does that translate to a whole form for example? Do I have to create a new QObject and then set it as a context property for each of the components in the form? – dkalev Mar 06 '19 at 16:41
  • 1
    @dkalev No, FileManager can have other qproperties so you'll only use a single object – eyllanesc Mar 06 '19 at 18:37