Qt Gui не обновляется из-за проблемы с потоками

Я использую стороннюю библиотеку, которая динамически загружает несколько библиотек за 60-90 секунд. Это неудачный выбор дизайна, но я не могу изменить того, кто создал их код.

Я пытаюсь использовать QSplashScreen, чтобы, по крайней мере, попросить пользователя подождать, пока я выполняю эту однократную загрузку в фоновом режиме. Проблема в том, что заставка не рисует. Я вижу окно с незакрашенным пространством, пока загружается библиотека. После этого я могу увидеть заставку, прежде чем закрыть ее.

Я просмотрел похожие вопросы (например, экран-заставка Qt не отображается), но ничего не решает моя проблема. Я попытался загрузить пустой QPixmap и просто придать ему сплошной цвет. Это тоже не появляется.

QApplication a(argc, argv);
QPixmap pxl("icon.bmp");
QSplashScreen qss(pxl);
qss.show();
qss.showMessage(QString::fromStdString("Please wait... Loading"));
a.processEvents();

MainWindow w;
//thread is blocked
w.preLoad();//this is where the lengthy process takes place
w.show();

qss.finish(&w);

Я хотел бы убедиться, что он хотя бы один раз рисуется, прежде чем я начну процесс загрузки.

------------------------РЕДАКТИРОВАТЬ------------------------- ------

Позвольте мне повторить, что вызов preLoad БЛОКИРУЕТ поток. Это не вариант. Я пробовал отдельный поток для этого процесса. Я также пробовал использовать отдельный поток для экрана-заставки (включив его, а затем закончив, когда другой поток будет завершен). Я попытался использовать семафор между двумя потоками, чтобы добиться этого, и хотя все работает (включая заставку), загрузка занимает 200-800 секунд. Это просто неприемлемо. Таким образом, я хотел бы посмотреть, есть ли вокруг него с этой точки зрения.

-------------------------Окончательное решение----------------------- ---------

Благодаря комментариям ниже я узнал, что у Qt есть собственная функциональность многопоточности. Все проблемы, которые я видел, по-видимому, вызваны взаимодействием std::thread и собственной реализации Qt.

У меня есть частичное решение, которое работает. Это не так аккуратно, как могло бы быть, но я хотел включить его в ветку вопросов.

//in the main method code described above
MainWindow w;
w.preLoad();
while(w.IsLoading())
{
  //waiting on semaphore signaling loading is complete
  //this part could probably be done better with QThread 
  //and signals, but it is a quick fix for now
  std::this_thread::sleepfor(std::chrono::milliseconds(500));
  a.processEvents();
}
w.show();

qss.finish(&w);


//In the MainWindow class
void MainWindow::preLoad()
{
  loading=true;//semaphore to stall main thread
  QFuture<void> future = QtConcurrent::run(this, &MainWindow::LongMethod);
}

void MainWindow::LongMethod()
{
  thirdPartyLibrary.impossibleCall();
  loading=false;//this handles my semaphore
}

person BSD    schedule 01.11.2017    source источник
comment
что такое preLoad?   -  person eyllanesc    schedule 01.11.2017
comment
preLoad — это метод, который вызывает стороннюю библиотеку. Первый звонок занимает 60-90 секунд. Там нет кода, чтобы показать вам. Достаточно сказать, что он блокирует поток в течение этого периода.   -  person BSD    schedule 01.11.2017
comment
Он работает во вторичном потоке?   -  person eyllanesc    schedule 01.11.2017
comment
Неа. Та же нить.   -  person BSD    schedule 01.11.2017
comment
Я думаю, что эта задача блокирует графический интерфейс, если у вас есть тяжелая задача, желательно выполнить ее во втором потоке, явно соблюдая условия Qt.   -  person eyllanesc    schedule 01.11.2017
comment
Я рекомендую вам прочитать следующее: doc.qt.io/qt-5/qtconcurrentrun.html   -  person eyllanesc    schedule 01.11.2017
comment
Другой вариант — вызывать между каждой инструкцией, которую вы запускаете в предварительной загрузке: qApp->processEvents();   -  person eyllanesc    schedule 01.11.2017
comment
@eyllanesc Почему Qt реализует собственную систему потоков? Неудивительно, что мои операции std::thread вызывали хаос. Ссылка, которую вы дали, открыла совершенно новый взгляд на создание приложения в Qt. Я хотел бы дать вам баллы за ответ. Можете ли вы поместить что-то в ответ, а не в комментарий?   -  person BSD    schedule 01.11.2017
comment
Qt реализует собственную систему потоковой передачи? Одной из причин является обратная совместимость. Qt существовал задолго до С++ 11. Также Qt использует сигналы и слоты с поддержкой многопоточности.   -  person drescherjm    schedule 01.11.2017


Ответы (1)


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

Если кто-то хочет выполнить блокирующие задачи, рекомендуется выполнять их в другом потоке, поскольку Qt предоставляет несколько возможностей:

Для вам выбрать правильный вариант для вашего случая.

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

Поток GUI и рабочий поток

Как уже упоминалось, каждая программа имеет один поток при запуске. Этот поток называется «основной поток» (также известный как «поток GUI» в приложениях Qt). Графический интерфейс Qt должен работать в этом потоке. Все виджеты и несколько связанных с ними классов, например QPixmap, не работают во вторичных потоках. Вторичный поток обычно называют «рабочим потоком», поскольку он используется для выгрузки обработки из основного потока.

Другой способ — принудительно обновить графический интерфейс. Для этого мы можем использовать qApp->processEvents. ().

Использованная литература:

person eyllanesc    schedule 01.11.2017