Всплывающее диалоговое окно для сохранения файла в отдельном потоке

У меня есть функция, которая вызывается в отдельном потоке от основного, когда нажимается кнопка, и она вызывает функцию QFileDialog::getSaveFileName(), чтобы получить дескриптор файла для файла, сохраненного пользователем, но я не могу сделать это в отдельном потоке, потому что он изменяет графический интерфейс, и вам не разрешено это делать.

Как я могу обойти это?


person user2584587    schedule 10.08.2013    source источник


Ответы (2)


В дополнение к комментариям, касающимся дизайна и чтения имен файлов из потока GUI перед созданием другого и передачей дескрипторов в качестве аргументов, я понимаю, что могут быть некоторые сценарии, в которых вам нужно вызывать диалоги GUI из других потоков.

Одним из решений может быть подача сигнала из вашего потока и его захват в графическом интерфейсе. Недостаток этого подхода в том, что может быть сложно получить результат (имя файла в вашем случае).

Аналогичным решением является использование Qt::BlockingQueuedConnection для вызова метода объекта, живущего в потоке графического интерфейса, блокируя другой поток до тех пор, пока метод не вернется.

Следующий пример иллюстрирует это с помощью вспомогательного объекта:

class FileDialogCaller : public QObject {
  Q_OBJECT

public:
  FileDialogCaller(QObject* parent = 0) : QObject(parent) {
    // The helper object will live in the GUI thread
    moveToThread(qApp->thread());
  }

  // Add the rest of parameters as needed
  QString getSaveFileName(QWidget* parent, const QString& caption, const QString& dir,
                          const QString& filter) {
    QString fileName;

    if (QThread::currentThread() != qApp->thread()) { // no GUI thread
      QMetaObject::invokeMethod(this, "getSaveFileName_", Qt::BlockingQueuedConnection,
                                Q_RETURN_ARG(QString, fileName),
                                Q_ARG(QWidget*, parent),
                                Q_ARG(QString, caption),
                                Q_ARG(QString, dir),
                                Q_ARG(QString, filter));
    } else { // in GUI thread, direct call
      fileName = getSaveFileName_(parent, caption, dir, filter);
    }

    return fileName;
  }

private:
  Q_INVOKABLE
  QString getSaveFileName_(QWidget* parent, const QString& caption, const QString& dir,
                          const QString& filter) {
    return QFileDialog::getSaveFileName(parent, caption, dir, filter);
  }
};

Чтобы использовать его просто:

QString fileName = FileDialogCaller().getSaveFileName(nullptr, "Save", "", "Any (*.*)");
person cbuchart    schedule 03.11.2017
comment
Вы, вероятно, хотите проверить, что QThread::currentThread() != qApp->thread() перед использованием Qt::BlockingQueuedConnection. В этом случае просто позвоните getSaveFileName_ - person Caleth; 03.11.2017
comment
@Калет Конечно! предполагался сценарий ОП, но я согласен с вашим предложением - person cbuchart; 03.11.2017

QFileDialog::getSaveFileName() возвращает имя файла, он не пытается открыть файл и не возвращает дескриптор файла.

Из вашего вопроса непонятно, но я предполагаю, что ваша кнопка запускает поток для выполнения какой-то длительной задачи и сохранения результатов в файл. Поэтому вызовите QFileDialog::getSaveFileName() прямо в слоте нажатия кнопки, получите имя файла и укажите это имя в потоке. Ваш поток просто прочитает это имя файла, поэтому, вероятно, нет необходимости в синхронизации. А затем вы просто открываете файл с указанным именем файла в этом потоке без графического интерфейса.

person Saša Mrvoš    schedule 29.08.2013