【问题标题】:How to close QFileDialog programmatically?如何以编程方式关闭 QFileDialog?
【发布时间】:2014-09-03 08:25:24
【问题描述】:

我需要在应用程序的测试中处理并关闭 QFileDialog。 调用的对话框:

QFileDialog::getOpenFileName( ... );

在测试中,我通过以下方式“捕捉”此对话框:

QApplication::topLevelWidgets();

然后我通过 QFileDialog API 选择需要的文件。

现在我应该关闭对话框以从 QFileDialog::getOpenFileName(); 获得有效的答案(文件名); 但是QDilaog的slots没有效果(accept(), reject(), close()...)。对话框保持打开状态。

here 描述的解决方案有效,但在我的情况下它不是选项。我必须使用标准对话框。

有没有办法正确关闭它?

【问题讨论】:

  • Then I choose need file by QFileDialog API.。您真的在对话框的 GUI 中看到您的更改吗?
  • 你使用什么 Qt 版本?我在问,因为我正在查看 4.8 源,它根本没有创建 QFileDialog(它创建了一个假的 QDialog 代替)。那么如何使用 API 呢?
  • 是的 - 正确选择了目录和文件!
  • 只需调用 QFileDialog::getOpenFileName(nullptr, message, QString(), mask);
  • 但是您说您通过topLevelWidgets 找到了对话框,然后通过API 选择了一个文件。这意味着您使用的是指针而不是静态 API。否则你不需要找到对话框。

标签: qt


【解决方案1】:

QFileDialog::getOpenFileName 是一个静态方法,所以你会受到你能用它做什么的限制。

如果您想要更多控制,我建议创建一个 QFileDialog 实例并使用它。通过调用实例的close() 函数,您可以以编程方式关闭对话框。

针对这不起作用的评论,这里是示例代码:-

// Must create the FileDialog on the heap, so we can call close and the dialog is deleted
// Set the Qt::WA_DeleteOnClose flag if the instance is still required
QFileDialog* fileDlg = new QFileDialog(this, QString("Select Config file"), QDir::homePath(),    QString("Config (*.xml)"));

// One shot timer to close the dialog programmatically
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=]() 
{
    fileDlg->close();
    timer->deleteLater();
} );

timer->start(3000);
fileDlg->exec();

【讨论】:

  • 正如我所说,我“捕捉”了应用程序小部件之间的对话框。 (没有 dlg->getOpenFileName(); dlg->close();)
  • close() - 对标准对话框无效。 (它只适用于 Qt 的对话框)。
  • 我不明白你所说的“标准对话”是什么意思。我添加到答案中的代码会在打开对话框 3 秒后关闭打开的对话框。
  • @Merlin069,我认为“标准对话框”代表 Qt 使用的 Windows 本机文件打开/保存对话框。
  • @YuriyVelichko。当@Merlin069 说getOpenFileName() 是静态方法时,您还没有理解他的意思。如果您将dlg 作为QFileDialog* 的实例,则调用dlg->getOpenFileName()dlg 实例没有任何作用。显示的对话框是在静态函数中创建的 QFileDialog 的新实例。而是使用@Merlin069 在上面的答案中发布的代码。调用fileDlg->close() 将关闭对话框。我建议你研究一下什么是静态函数。
【解决方案2】:

为了显示原生对话框,您必须运行 exec() 或调用其中一个静态函数。

不幸的是,在 Windows 中,这会调用 Windows API 中的阻塞函数,使显示的对话框成为模态,运行它自己的事件循环。如果不返回 Qt 事件循环,则无法使用信号/插槽接口执行 close() 函数。

我试图通过直接从另一个线程调用close() 函数来绕过此问题,但这会导致Qt 尝试将事件发送到底层对话框。由于在 Qt 中不允许跨线程边界发送(而不是发布)事件,因此会产生致命错误。

所以,至少对于 Windows 来说,这是不可能的。

我没有在 Windows 以外的平台上进行过测试。我使用的代码是:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);


    QFileDialog* fileDlg = new QFileDialog(0, QString("Select Config file"), QDir::homePath(),    QString("Config (*.xml)"));

    // spawn a new thread
    QtConcurrent::run([=](){
        QTimer timer;
        timer.setSingleShot(true);

        QEventLoop *loop = new QEventLoop;

        QObject::connect(&timer, &QTimer::timeout, [=](){
            fileDlg->close();
            fileDlg->deleteLater();
            loop->quit();
        });

        timer.start(3000);
        loop->exec();

        delete loop;
    });

    fileDlg->exec();

    return a.exec();
}

【讨论】:

    【解决方案3】:

    在 Windows 中,您可以使用 WinAPI 关闭对话框:

    #define WAIT(A) while (!(A)) {}
    HWND dialogHandle, button;
    WAIT(dialogHandle = FindWindow(NULL, L"Open")); //write here title of the dialog
    WAIT(button = FindWindowEx(dialogHandle, NULL, L"Button", L"&Open")); //write here title of the button to click
    SendMessage(button, BM_CLICK, 0, 0);
    

    【讨论】:

    • 谢谢,但我需要多平台解决方案。
    • @yuriy-velichko 我真的怀疑您能否成功找到跨平台解决方案。 Windows-implementaion 使用 winapi 调用 GetOpenFileName 在对话框关闭之前不会返回。所以我应该是一种特定于平台的方式来关闭这样的对话框。而且看起来它没有在 Qt 中实现。
    猜你喜欢
    • 1970-01-01
    • 2011-07-15
    • 2015-11-26
    • 2020-07-08
    • 2013-07-04
    • 2010-11-17
    • 2016-03-08
    • 1970-01-01
    相关资源
    最近更新 更多