Russian Qt Forum

Qt => Общие вопросы => Тема начата: Гурман от Июнь 04, 2010, 14:59



Название: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 04, 2010, 14:59
хотел сделать, чтобы если перерисовка некоего объекта требует времени больше полсекунды, отображались песочные часы, вроде все просто

Код:
void MainWindow::slWaitCursor()
{
QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
cursorchanged = true;
}

void MainWindow::slNull()
{
slMessage("null\n"); // отладочная печать
}

void MainWindow::redrawScene()
{
QTimer timer; // = new QTimer;
cursorchanged = false;
connect( &timer, SIGNAL(timeout()), this, SLOT(slWaitCursor()) );
timer.start( 500 );
//a->processEvents();
large_object->Redraw();
connect( &timer, SIGNAL(timeout()), this, SLOT(slNull()) );
timer.stop();
if( cursorchanged )
QApplication::restoreOverrideCursor();
}

cursorchanged всадил на всякий случай, поскольку не написано, что restoreOverrideCursor() ничего не делает, если курсор не перегружался

шаманство с реконнектом слотов сделал, поскольку timer->singleShot стреляет всегда, независимо от способа и времени исчезновения или остановки таймера - то есть, если сделать stop до истечения времени, сигнал тоже будет послан

но... не работает, почему-то таймер не запускается, поскольку явно видно, что никогда не вызывается slNull() и не меняется курсор, вообще никогда

но! стоит убрать строку timer.stop(); - таймер начинает удивительным образом работать, сигнал в slNull сыпется каждые 500 мсек  :o

large_object->Redraw(); не содержит ни таймеров, ни параллельных нитей, ничего, кроме много-много рисования на QGraphicsScene (причем именно держит свой код обработки объекта, который там выполняется), либо наоборот, мало-мало (тогда курсор менять не надо), но гарантированно - даже когда рисуется все пару секунд, никакие сигналы от таймера не идут, смотрел в отладчике - перед вторым connect в таких случаях явное зависание, но контрольная точка внутри slWaitCursor() не срабатывает (если убрать timer.stop(); то срабатывает)

думал, может очередь сообщений где-то блокируется, но вызов a->processEvents(); (a это есессно указатель на QApplication) не помогает

тупик... можно подумать и сделать часы как-то иначе, но вроде тут все должно работать


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 04, 2010, 15:44
методом тыка выяснил, что

- таймер таки запускается, после отрисовки он остается активен, и останавливается соответствующим stop()
- независимо от заданного времени таймера, он не срабатывает по истечении заданного времени, поскольку stop() выполняется и после 2х секунд рисования, то, что таймер работает, показывает проверка isActive() НО! при этом таймером не генерится сигнал, и слот slNull() не вызывается, пробовал с разными типами соединений - не имеет значения
- при этом сигнал почему-то генерится, если убрать остановку таймера... ??? - при длительной перерисовке курсор мигает

я баг нашел?


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: BRE от Июнь 04, 2010, 15:54
Код
C++ (Qt)
       large_object->Redraw();
 
       
В методе Redraw я думаю есть большой цикл, в котором и рисуются все необходимые объекты. Добавь в него processEvents, для того что-бы цикл обработки событий крутился.
И думаю сигналы заработают.  ;)


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 05, 2010, 00:06
ооо... там сложно рисуется... надо пробовать, но как-то странно выглядит это все  :(

тем более, что в Redraw используются вызовы Qt для рисования отдельных объектов, по идее оно должно разобраться с событиями

и если затык в том, что сигнал от таймера во время Redraw не приходит, то тогда почему он не приходит и после окончания Redraw?...


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 07, 2010, 08:58
хех... попробовал добавить вызов processEvents() внутрь Redraw(), причем не на каждый элемент, а только перед некими "крупными" действиями, результат удручающий... оно то заработало, но отрисовка замедлилась в несколько раз, и что самое ужасное, стало видно, как сцена рисуется, чего собственно и следовало ожидать >:(

то есть, надо вычленить, и разрешить только событие таймера, чтобы processEvents() разрешало прохождение только его событий, либо хотя бы запретить прохождение событий при рисовании - а такой возможности нет...  :( есть только два флага QEventLoop::ExcludeUserInputEvents и QEventLoop::ExcludeSocketNotifiers, оба не подходят

чтобы гарантированно работало, наверно надо бы второй тред запустить, но небось же нельзя из второго треда просто обратиться к методам QApplication, живущего в первом треде... или по поводу смены курсора можно?



Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: BRE от Июнь 07, 2010, 09:03
IMHO, лучше отказаться от сигналов и в цикле отрисовки метода Redraw проверять сколько времени крутиться цикл и если время больше заданного менять курсор на "занато", а в конце метода восстанавливать его.
Посмотри на:
void QTime::start ()
int QTime::elapsed () const


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 07, 2010, 09:08
хотя это как-то "по-DOS-овски" :) в единичном случае схляет, но хотелось некий регулярный способ получить, чтобы можно было потом просто вызывать одну функцию во всех похожих случаях

и вообще... какой же это "таймер", если события его не проходят, когда приложение занято???  >:( я ожидал бы, чтобы таймер работал в параллельной нити, чтобы его сигналы гарантированно доставлялись через блокирующее соединение - придется наверно такое самому написать


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: BRE от Июнь 07, 2010, 09:26
хотя это как-то "по-DOS-овски" :) в единичном случае схляет, но хотелось некий регулярный способ получить, чтобы можно было потом просто вызывать одну функцию во всех похожих случаях
Одну функцию.... Ты же на C++ пишешь, так и думать нужно по другому.
Набросаю псевдокод, думаю идея будет понятна.
Код
C++ (Qt)
class CursorWaiter
{
public:
CursorWaiter( int timeout = 500 );
 
void process();
};
 
// Использование
void MyClass::Redraw()
{
CursorWaiter cw( 200 );
 
foreach(...)
{
// Считаем
// Рисуем
 
// Проверяем сколько времени прошло, если больше timeout, то меняем его на "Занято"
cw.prosess();
}
 
// При выходе из функции Redraw объект cw разрушиться, в деструкторе восстановить курсор.
}
 

и вообще... какой же это "таймер", если события его не проходят, когда приложение занято???  >:( я ожидал бы, чтобы таймер работал в параллельной нити, чтобы его сигналы гарантированно доставлялись через блокирующее соединение - придется наверно такое самому написать
Ты не ожидай, а посмотри как работают таймеры и все будет понятно.  ;)


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 07, 2010, 09:34
ну такие подсказки мне уже не нужны - я уже только что сделал и проверил, только лучше,  ;) с единственным дополнительным вызовом внутри Redraw, все работает

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

одним только таймером для этого не обойдешься, сделать придется чуть больше


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: ieroglif от Июнь 07, 2010, 10:01
что происходит:
в основном потоке начинает работать "долгая функция" (в нашем случае это отрисовка).
поток её долго и усердно решает. времени на выполнение других задач остаётся поменьше - таймеры (работающие только в основном потоке) начинают зависать, гуй (работающий только в основном потоке) начинает тормозить, прогресс бары перестают корректно работать.. ужас в общем :)
решение правильное (имхо, конечно):
решение происходит из понимания, что ООП не ООП, а выполнение комманд у нас ПОСЛЕДОВАТЕЛЬНОЕ. и пока одна комманда у нас не отработает - остальные в лучшем случае прижимаются к стенке и протискиваются как могут, а в других случаях так они вообще блокируются.
вывод простой - всё что "грузит" основной поток - выводить в отдельный. пусть там работает, сигнальчики отправляет какие хочет. а мы из основного потока, как только стартанём его себе singleShoot(на сколько нужно) запустим. ну и будем вести некий стек типа первый-вошёл=первый-вышел "текуще загружаемых потоков" по которому на каждый singleShoot SLOT метод будет брать элемент из стека, проверять - работает ли тред, и если да, то вешать значок песочных часиков. по окончанию работы тред тоже производит ряд действий каких-то там.. ну и т.д.


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 07, 2010, 10:43
угу, для каких-то приложений примерно так и надо делать, хотя без всяких FIFO можно вполне обойтись

но в моем случае, поскольку пока Redraw не выполнится, гуй и не должен полноценно работать, ибо не с чем

а вот таймеры, и все, что к гую не относится, по идее должно...


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: ieroglif от Июнь 07, 2010, 11:25
угу, для каких-то приложений примерно так и надо делать, хотя без всяких FIFO можно вполне обойтись

но в моем случае, поскольку пока Redraw не выполнится, гуй и не должен полноценно работать, ибо не с чем

поверь, даже самому будет гораздо приятнее, если вместо подвисания гуя (хоть с ним работать и не надо ПОКА ЧТО) у тебя заблокируются элементы которые трогать пока-что не надо и на экране будет написано "Рисуем..." :) а сделать это - 15 минут ;)

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

а вот таймеры, и все, что к гую не относится, по идее должно...

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


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 07, 2010, 12:03
Цитировать
поверь, даже самому будет гораздо приятнее, если вместо подвисания гуя (хоть с ним работать и не надо ПОКА ЧТО) у тебя заблокируются элементы которые трогать пока-что не надо и на экране будет написано "Рисуем..." Улыбающийся а сделать это - 15 минут

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

а вместо надписи "Рисуем" вполне устраивают песочные часы


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Igors от Июнь 07, 2010, 12:58
а вот таймеры, и все, что к гую не относится, по идее должно...
Не должно. События таймера имеют низший приоритет в OC и могут быть получены когда нет др. действий. Надо использовать вторичный цикл событий (см. пост BRE). При этом приходится повозиться чтобы он не тормозил выполнение - это нормальные, стандартные хлопоты.

Отделаться песочными часами вряд ли удастся, насколько я помню human interface

- задержка (вычисления, перерисовка и.т.п)  более полсекунды - app должно показать курсором
- задержка 2 и более секунд - app должно выставить индикатор


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 07, 2010, 15:13
мне больше всего нравится собственная идея с параллельным тредом, который индикаторы включает, если надо, но с простой проверкой сколько времени прошло, в данном частном случае получилось нормально, задержка при отрисовке (с вычислениями) порядка нескольких десятков тысяч графических айтемов - не более 3-х секунд, песочные часы вполне уместны


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: ieroglif от Июнь 08, 2010, 11:30
мне больше всего нравится собственная идея с параллельным тредом, который индикаторы включает, если надо, но с простой проверкой сколько времени прошло, в данном частном случае получилось нормально, задержка при отрисовке (с вычислениями) порядка нескольких десятков тысяч графических айтемов - не более 3-х секунд, песочные часы вполне уместны

хм.. вопрос из отдельной области - как отрисовываются десятки тысяч айтемов за 3 секунды?
чем отрисовываются? стандартным QPainter? в таком случае какое железо?


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Гурман от Июнь 08, 2010, 13:58
стандартным QPainter, айтемы содержат только текст, никакой графики на сцене нет

уточнил - сейчас пару секунд рисуется сцена с >7000 айтемами, посложнее с >33000 айтемов около 17 секунд (в меньшей сцене все равно есть еще чего делать, поэтому скорость неравномерная)

можно и приделать выдачу сообщения для таких сцен, хотя на самом деле, будет в разы быстрее рисоваться, сейчас неоптимально вычисляются bounding rectangle для случаев вложенных айтемов (которых подавляющее большинство), проверял, если кешировать размеры, то отрисовывается в несколько раз быстрее (где-то в 3-5), но надо сесть и аккуратно прописать необходимость перевычисления при изменении вложенных айтемов

железо - квадропень с ddr3


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: ieroglif от Июнь 08, 2010, 14:31
ну вообще да :)
я посчитал, что у меня примерно 40-100 штук элементов отрисовываются :) тогда я спокоен :)


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: Igors от Июнь 08, 2010, 18:06
Если это чисто рисование, то можно делать его по частям, напр.

- отрисовали первые 10К айтемов
- показали прогресс %
- отрисовали следующие 10К
и.т.д.

Завести счетчик отрисованных несложно, с "прерываемостью" проблем нет - обнулить тот же счетчик


Название: Re: непонятки с таймером, не всегда запускается, см. сообщение
Отправлено: ieroglif от Июнь 09, 2010, 11:03
да меня просто вопрос производительности интересовал =)