У меня есть функция, которая вызывается в отдельном потоке от основного, когда кнопка нажимается, и она вызывает QFileDialog::getSaveFileName()
функция, чтобы получить дескриптор файла для файла, сохраненного пользователем, но я не могу сделать это в отдельном потоке, потому что он изменяет графический интерфейс, и вам не разрешено это делать.
Как я могу обойти это?
В дополнение к комментариям относительно дизайна и чтения имен файлов из потока GUI перед созданием другого и передачей дескрипторов в качестве аргументов, я понимаю, что могут быть некоторые сценарии, когда вам нужно вызывать диалоги GUI из других потоков.
Одним из решений может быть излучение сигнала из вашего потока и захват в графическом интерфейсе. Недостатком этого подхода является то, что может быть трудно получить результат (имя файла в вашем случае).
Аналогичное решение заключается в использовании Qt::BlockingQueuedConnection
вызывать метод объекта, живущего в потоке GUI, при этом блокируя другой поток, пока метод не вернется.
Следующий пример иллюстрирует это с помощью вспомогательного объекта:
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 (*.*)");
QFileDialog::getSaveFileName()
возвращает имя файла, он не пытается открыть файл или вернуть дескриптор файла.
Из вашего вопроса не ясно, но я предполагаю, что ваша кнопка запускает поток для выполнения некоторой длительной задачи и сохранения результатов в файле. Так что звоните QFileDialog::getSaveFileName()
прямо в ячейке щелчка кнопки, получите имя файла и предоставьте это имя потоку. Ваша ветка просто прочитает это имя файла, поэтому, вероятно, нет необходимости в синхронизации. А затем вы просто открываете файл с указанным именем в этом потоке без графического интерфейса.