Russian Qt Forum
Ноябрь 24, 2024, 02:35 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Вынос в поток  (Прочитано 7116 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Август 21, 2016, 08:51 »

Добрый день

Читаю файлы с помощью либы которая сначала грузит/разбирает файл в память. Файлы могут быть большими (напр 100 метров) и тогда загрузка может занять десятки секунд. Callback'а (для мониторинга загрузки) не нашел. Поэтому нужно

- вынести это чтение в поток

- показать юзеру индикатор с бегунком ползающим туда-сюда (мол, приложение живет, просто занято)

- организовать ожидание в главной нитке до конца чтения. При этом не позволять юзеру ничего нажимать (нет даже cancel), пусть ожидает конца загрузки

Понятно что это можно сделать всяко-разно, но как лучше (удобнее, без слот-сигнал соплей)?

Спасибо
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1 : Август 21, 2016, 10:02 »

QFuture?
Записан

ArchLinux x86_64 / Win10 64 bit
Bepec
Гость
« Ответ #2 : Август 21, 2016, 11:18 »

Без сигнал-слотов в Qt, построенной на сигнал слотах... Зачем вы ставите глупые задачи?
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #3 : Август 21, 2016, 17:10 »

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

А вынести чтение можно, либо реализовав чтение в run потомка QThread, либо сформировать читателя (от QObject) и перенести его в QThread. Ну или использовать QFuture.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #4 : Август 21, 2016, 19:37 »

Даже если вынести чтение в отдельный поток, то все-равно потребуется обработка сигнала окончания чтения, так как блокировка главной нитки для ожидания не позволит отобразить индикатор. Интерфейс зависнет из-за такой блокировки, так как это будет имитация последовательного вызова.
Это смотря как блокировать. Улыбающийся
Если запустить локальный цикл обработки событий (QEventLoop), который будет завершаться при окончании загрузки, то индикатор будет спокойно работать.

А вынести чтение можно, либо реализовав чтение в run потомка QThread, либо сформировать читателя (от QObject) и перенести его в QThread. Ну или использовать QFuture.
Достаточно запустить функцию загрузки в отдельном потоке (std::thread) с callback-функцией, которая будет вызываться при завершении загрузки.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Август 22, 2016, 14:52 »

Попытался сделать в общем виде, но что-то не очень выходит

Код
C++ (Qt)
struct CThreadUserProc : public QThread {
CThreadUserProc( QRunnable & runner ) : mRunner(runner)
{
}
 
virtual void timerEvent ( QTimerEvent * e )
{
if (e->timerId() == mTimerId)
UpdateIndicator();
}
 
virtual void run( void )
{
mRunner.run();
}
 
void ExecTask( int interval = 100 )
{
mTimerId = startTimer(interval);
start();
 
while (!isFinished())
qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
}
 
private:
int mTimerId;
QRunnable & mRunner;
};
 
Использование
Код
C++ (Qt)
StartIndicator();  // напр заряжаем QProgressDialog
CThreadUserProc(*this).ExecTask();
Соорудить QRunnable не проблема, напр
Код
C++ (Qt)
struct MyClass : public SomeBase, public QRunnable {
virtual void run( void )
{
   SomeLoooongFunc(...);  // этот метод мы хотим вынести в поток
}
};
А вот с UpdateIndicator ничего удачного не придумалось, хотелось бы избежать наследования CThreadUserProc всякий раз, просто что-то подать ему в конструктор, но не вижу как. Также QEventLoop::ExcludeUserInputEvents несовершенно - кнопка Cancel может иметься
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #6 : Август 22, 2016, 18:52 »

Бегунок бегает в модальном диалоге, который обновляется по таймеру, например, каждые 100 мс.
Загрузка крутится в потоке, по его окончанию QThread пошлет сигнал finished(), по которому диалог можно закрывать.
По моему, это самое простое и короткое решение...
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Август 23, 2016, 09:35 »

Бегунок бегает в модальном диалоге, который обновляется по таймеру, например, каждые 100 мс.
Загрузка крутится в потоке, по его окончанию QThread пошлет сигнал finished(), по которому диалог можно закрывать.
По моему, это самое простое и короткое решение...
Ну по существу я так и сделал (см код предыдущего поста). Вопрос в том как лучше (удобнее, компактнее, выразительнее) это реализовать
Записан
Bepec
Гость
« Ответ #8 : Август 23, 2016, 12:59 »

Зачем обновление по таймеру?
Насколько я помню почти 90% прогресс баров имеют режим "бесконечная загрузка", при установке минимума и максимума в 0. Так что нужен в принципе то только сигнал завершения.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Август 23, 2016, 13:18 »

Зачем обновление по таймеру?
Иначе до обновления индикатора дело не дойдет.
Записан
Bepec
Гость
« Ответ #10 : Август 23, 2016, 16:26 »

Мы в другом потоке.
ProgressBar крутится сам в основном (как я уже замечал, данный функционал есть у всех более-менее новых прогрессбаров). Крутится он в бесконечном цикле сам. Ну типа влево зелёная, вправо зелёная и так до бесконечности.
Единственно что нам тогда необходимо - сообщить о завершении работы.
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #11 : Август 23, 2016, 16:58 »

Мы в другом потоке.
ProgressBar крутится сам в основном (как я уже замечал, данный функционал есть у всех более-менее новых прогрессбаров). Крутится он в бесконечном цикле сам. Ну типа влево зелёная, вправо зелёная и так до бесконечности.
Единственно что нам тогда необходимо - сообщить о завершении работы.

"Сама" только земля крутится)
Диалог кто-то ж должен обновлять. Если не таймер, то кто?
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #12 : Август 23, 2016, 17:11 »

Вызов setRange ( 0, 0 ) формирует прогресс бар с анимацией "туда-сюда".

Цитировать
If minimum and maximum both are set to 0, the bar shows a busy indicator instead of a percentage of steps.
« Последнее редактирование: Август 23, 2016, 17:14 от ssoft » Записан
Bepec
Гость
« Ответ #13 : Август 23, 2016, 18:04 »

Само крутится. Пока вы крутилку не открутите Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Август 24, 2016, 08:15 »

Да, конкретно для QProgressDialog можно обойтись без таймера
Код
C++ (Qt)
#include <QtWidgets>
 
class MyWidget : public QWidget {
public:
MyWidget( void )
{
resize(320, 240);
QPushButton * btn = new QPushButton("Test", this);
QObject::connect(btn, &QPushButton::clicked, this, &MyWidget::TestDialog);
}
 
void TestDialog( void )
{
QProgressDialog progress("Copying files...", "Abort Copy", 0, 0);
progress.setWindowModality(Qt::ApplicationModal);
progress.show();
 
while (!progress.wasCanceled())
qApp->processEvents(QEventLoop::WaitForMoreEvents);
}
};
 
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyWidget * win = new MyWidget;
win->show();
 
return app.exec();
}
 
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.238 секунд. Запросов: 23.