【问题标题】:Open file dialog from PyQt5 connected to QML从连接到 QML 的 PyQt5 打开文件对话框
【发布时间】:2020-10-13 05:39:26
【问题描述】:

我在 QML 中的 ApplicationWindow 中定义了一个按钮(实际上是一个 MouseArea)。我设法从 PyQt5 连接到它的点击事件。现在我正在尝试显示保存文件对话框,但出现错误:

QWidget: Cannot create a QWidget without QApplication

我的代码如下所示:

from PyQt5.QtCore import QUrl, QObject   # pylint: disable-msg=E0611
from PyQt5.QtGui import QGuiApplication, QIcon # pylint: disable-msg=E0611
from PyQt5.QtQml import QQmlApplicationEngine # pylint: disable-msg=E0611
from PyQt5.QtWidgets import QFileDialog # pylint: disable-msg=E0611

def openFile():
    options = QFileDialog.Options()
    options |= QFileDialog.DontUseNativeDialog
    filename = QFileDialog.getOpenFileName(None,"QFileDialog.getOpenFileName()", "","All Files (*);;Python Files (*.py)", options=options)
    print(filename)

def run():
    app = QGuiApplication(sys.argv)
    app.setWindowIcon(QIcon(resource_path("assets\\images\\icon.ico")))

    engine = QQmlApplicationEngine()
    engine.load(resource_path("qml\\Window.qml"))
    engine.quit.connect(app.quit)

    if not engine.rootObjects():
        return -1

    button = engine.rootObjects()[0].findChild(QObject, "openButton")
    button.clicked.connect(openFile)

    return app.exec_()

if __name__ == '__main__':
    sys.exit(run())

我也尝试传递 ApplicationWindow 而不是 None,但随后出现类型错误:

TypeError: getOpenFileName(parent: QWidget = None, caption: str = '', directory: str = '', filter: str = '', initialFilter: str = '', options: Union[QFileDialog.Options, QFileDialog.Option] = 0): argument 1 has unexpected type 'QWindow'

在我看来这没有多大意义,因为QMainWindow 继承了QWidget

如何显示来自openFile() 函数的对话框?

编辑:为了完整起见,这里是我的 .qml 文件的精简版

import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick 2.3

ApplicationWindow {
    id: mainWindow
    visible: true
    width: 600  
    height: 400

    Item {
        anchors.top: titleBar.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        property bool isMainButtonFocused: false
        objectName: "openButton"
        signal clicked()

        Label {
            padding: 5
            text: "<b><font color='#fefefe'>Hello World</font></b>"
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
            font.family: "Helvetica"
            font.pointSize: 9
            background: Rectangle {
                color: mouseAreaOpenFolderButton.containsMouse ? "#777777" : "#333333"
                border.width: isMainButtonFocused ? 2 : 1
                border.color: "#ffffff"
                radius: 5
            }

            MouseArea {
                id: mouseAreaOpenFolderButton
                anchors.fill: parent
                hoverEnabled: true
                onClicked: {
                    isMainButtonFocused = true
                    parent.parent.clicked()
                }
            }
        }
    }
}

【问题讨论】:

  • 首先,将 QML 应用程序逻辑从 QML 中移出是一个非常糟糕的主意。一个更糟糕的想法是混合使用 QtQuick 和 QWidgets。你为什么不改用FileDialog?至于问题——错误很明显——你必须使用QApplication而不是QGuiApplication。有关更多信息,请参阅this 讨论。
  • 好吧,我不想使用 FileDialog,因为我想在 Python 中检索结果,因为我需要从那里读出它。但是我得出的结论是,这将是最简单的方法...

标签: python pyqt pyqt5 qml


【解决方案1】:

错误很清楚:

  • 如果您要使用 QFileDialog 之类的 QWidget,那么您必须创建一个 QApplication。
  • 如果要向父级传递 QWidget,就像 QFileDialog 一样,那么该父级必须是另一个 QWidget,但 ApplicationWindow 不是 QWidget,而是导致该错误的 QWindow。

其他额外但更重要的错误是您不应该从 Python(或 C++)访问 QML 对象,因为它的生命周期不同,因此可能会出现问题(例如,参见 this answer),而是创建一个 QObject 来实现逻辑并将其公开为项目(使用 qmlRegisterType)或上下文属性(通过 setContextProperty)。

考虑到上述情况,您应该使用 FileDialog 并将 QObject 公开为上下文属性:

# ...
class Helper(QObject):
    @pyqtSlot(QUrl)
    def read_file(self, url):
        filename = url.toLocalFile()
        print(filename)


def run():
    app = QGuiApplication(sys.argv)
    app.setWindowIcon(QIcon(resource_path("assets\\images\\icon.ico")))

    engine = QQmlApplicationEngine()
    helper = Helper()
    engine.rootContext().setContextProperty("helper", helper)
    # ...
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick 2.3
import QtQuick.Dialogs 1.3

ApplicationWindow {
    id: mainWindow
    visible: true
    width: 600  
    height: 400

    Item {
        anchors.top: titleBar.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        property bool isMainButtonFocused: false

        Label {
            padding: 5
            text: "<b><font color='#fefefe'>Hello World</font></b>"
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenter: parent.horizontalCenter
            font.family: "Helvetica"
            font.pointSize: 9
            background: Rectangle {
                color: mouseAreaOpenFolderButton.containsMouse ? "#777777" : "#333333"
                border.width: isMainButtonFocused ? 2 : 1
                border.color: "#ffffff"
                radius: 5
            }

            MouseArea {
                id: mouseAreaOpenFolderButton
                anchors.fill: parent
                hoverEnabled: true
                onClicked: {
                    // isMainButtonFocused = true
                    fileDialog.visible = true
                    
                }
            }
        }
    }
    FileDialog {
        id: fileDialog
        title: "Please choose a file"
        selectedNameFilter: "All Files (*);;Python Files (*.py)"
        onAccepted: {
            helper.read_file(fileDialog.fileUrls[0])
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2010-12-11
    • 1970-01-01
    • 2013-05-12
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多