Russian Qt Forum

Qt => Общие вопросы => Тема начата: Igors от Август 21, 2016, 08:51



Название: Вынос в поток
Отправлено: Igors от Август 21, 2016, 08:51
Добрый день

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

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

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

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

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

Спасибо


Название: Re: Вынос в поток
Отправлено: kuzulis от Август 21, 2016, 10:02
QFuture?


Название: Re: Вынос в поток
Отправлено: Bepec от Август 21, 2016, 11:18
Без сигнал-слотов в Qt, построенной на сигнал слотах... Зачем вы ставите глупые задачи?


Название: Re: Вынос в поток
Отправлено: ssoft от Август 21, 2016, 17:10
Даже если вынести чтение в отдельный поток, то все-равно потребуется обработка сигнала окончания чтения, так как блокировка главной нитки для ожидания не позволит отобразить индикатор. Интерфейс зависнет из-за такой блокировки, так как это будет имитация последовательного вызова.

А вынести чтение можно, либо реализовав чтение в run потомка QThread, либо сформировать читателя (от QObject) и перенести его в QThread. Ну или использовать QFuture.


Название: Re: Вынос в поток
Отправлено: Old от Август 21, 2016, 19:37
Даже если вынести чтение в отдельный поток, то все-равно потребуется обработка сигнала окончания чтения, так как блокировка главной нитки для ожидания не позволит отобразить индикатор. Интерфейс зависнет из-за такой блокировки, так как это будет имитация последовательного вызова.
Это смотря как блокировать. :)
Если запустить локальный цикл обработки событий (QEventLoop), который будет завершаться при окончании загрузки, то индикатор будет спокойно работать.

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


Название: Re: Вынос в поток
Отправлено: Igors от Август 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 может иметься


Название: Re: Вынос в поток
Отправлено: Racheengel от Август 22, 2016, 18:52
Бегунок бегает в модальном диалоге, который обновляется по таймеру, например, каждые 100 мс.
Загрузка крутится в потоке, по его окончанию QThread пошлет сигнал finished(), по которому диалог можно закрывать.
По моему, это самое простое и короткое решение...


Название: Re: Вынос в поток
Отправлено: Igors от Август 23, 2016, 09:35
Бегунок бегает в модальном диалоге, который обновляется по таймеру, например, каждые 100 мс.
Загрузка крутится в потоке, по его окончанию QThread пошлет сигнал finished(), по которому диалог можно закрывать.
По моему, это самое простое и короткое решение...
Ну по существу я так и сделал (см код предыдущего поста). Вопрос в том как лучше (удобнее, компактнее, выразительнее) это реализовать


Название: Re: Вынос в поток
Отправлено: Bepec от Август 23, 2016, 12:59
Зачем обновление по таймеру?
Насколько я помню почти 90% прогресс баров имеют режим "бесконечная загрузка", при установке минимума и максимума в 0. Так что нужен в принципе то только сигнал завершения.


Название: Re: Вынос в поток
Отправлено: Igors от Август 23, 2016, 13:18
Зачем обновление по таймеру?
Иначе до обновления индикатора дело не дойдет.


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


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

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


Название: Re: Вынос в поток
Отправлено: ssoft от Август 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.


Название: Re: Вынос в поток
Отправлено: Bepec от Август 23, 2016, 18:04
Само крутится. Пока вы крутилку не открутите :)


Название: Re: Вынос в поток
Отправлено: Igors от Август 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();
}