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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Как контролировать отрисовку в виджете?  (Прочитано 12038 раз)
DAMAL
Гость
« : Август 31, 2008, 15:15 »

Возникла довольно интересная ситуация, которую не получается решить:
имеется виджет QLabel, у которого переописан метод paintEvent(): (image - поле класса myQLabel)

Код:
void myQLabel::setNextFrame(unsigned char *bitmap)
{
    memcpy(image, bitmap, size);
    QCoreApplication::postEvent(this, new QEvent(QEvent::Paint));
}
void myQLabel::paintEvent(QPaintEvent *e)
{
    some_image_transformation(image);
    QPixmap pixmap = QPixmap::fromImage( image );
    QPalette palette;
    palette.setBrush(backgroundRole(), QBrush(pixmap));
    setPalette(palette);
}
Далее довольно интенсивно виджету посылаются события о перерисовке (область рисования большая, до 704х576 пикселов должна меняться до 25 раз в секунду). На быстром компьютере всё работает замечательно, но стоит запустить приложение на медленном компьютере - Qt начинает не успевать обрабатывать все события о перерисовке в рельном времени. И вот тут самое неприятное: вместо того, чтобы сбрасывать избыточные события, они начинают копиться где-то в недрах Qt. Через пару часов работы оказываются накопленными чуть ли не сотни неотрисованных вовремя кадров, которые выводятся на экран по мере их обработки.
Хочу поинтересоваться, если возможность контролировать процесс отрисовки? Безуспешно искал какой-нибудь счётчик очереди на отрисовку чтобы не посылать "лишних" кадров, ставил различные флаги виджету типа WNoAutoErase и NoBackground - безуспешно.
Ситуация наигрывается в Qt 3-ей и 4-ой версий.
« Последнее редактирование: Август 31, 2008, 17:56 от pastor » Записан
SASA
Гость
« Ответ #1 : Август 31, 2008, 16:32 »

Можно поставить окошку атрибут WA_PaintOnScreen, чтоб рисовалось быстрей.
Так же есть замечательные функции QCoreApplication::sendPostedEvents(), QCoreApplication::flush () и QCoreApplication::processEvents ().
Записан
DAMAL
Гость
« Ответ #2 : Август 31, 2008, 17:25 »

Спасибо за идеи. Попробую метод flush(). С processEvents() часто имел дело, использовал его в ситуациях, когда не было возможности запустить автоматическую обработку сообщений командой exec().
Записан
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #3 : Август 31, 2008, 17:55 »

Заинтересовала строка:

Код:
QCoreApplication::postEvent(this, new QEvent(QEvent::Paint));

Может вы лучше обратите внимание на repaint\update?
Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
DAMAL
Гость
« Ответ #4 : Август 31, 2008, 18:45 »

Заинтересовала строка:

Код:
QCoreApplication::postEvent(this, new QEvent(QEvent::Paint));

Может вы лучше обратите внимание на repaint\update?

Согласен, выглядит сложно и странно. Не стал полностью описывать ситуацию сразу, чтобы не загромождать сообщение. Дело в том, что окно для рисования создаётся у меня вне главного потока окна. Я работаю в Linux, и при использовании всех синхронных сообщений (repaint, update и т.д.) во внепоточное окно в моём случае иксы начинают генерировать ошибку "Acynchrone reply", единственным решением было посылать сингал о рисовании асинхронно - т.е. через postEvent.
Записан
ритт
Гость
« Ответ #5 : Август 31, 2008, 20:43 »

по идее QWidget::update() делает то же самое
Записан
DAMAL
Гость
« Ответ #6 : Август 31, 2008, 21:52 »

по идее QWidget::update() делает то же самое

Цитата для QWidget::update() из help: "The paint event is processed after the program has returned to the main event loop". А у меня виджет с рисованием в другом потоке, где нет main event loop. Очень может быть, что в винде действительно всё работает проще когда виджеты в разных потоках. А Linux к этому очень критично относится, иксы начинают пачками бросать в консоль ошибки и предупреждения по тексту которых вообще сложно понять об их причине.

Реализацию свою я, кстати, взял из одного учебника по Qt (не вспомню какого имено). Там был пример графического редактора. Чтобы не "замораживать" главное окно на момент выполнения операций по преобразованию обрабатываемых изображений - операции преобразования выполнялись в отдельном потоке, передача изображения из главного диалога в поток и обратно как раз выполнялась посылкой сигнала postEvent().
Записан
ритт
Гость
« Ответ #7 : Август 31, 2008, 22:23 »

благодарю за экскурс в документацию )
Код:
void QWidget::update()
{
    if (updatesEnabled() && isVisible()) {
        if (testAttribute(Qt::WA_WState_InPaintEvent)) {
            QApplication::postEvent(this, new QUpdateLaterEvent(rect()));
//...
а QCoreApplication::postEvent(this, new QEvent(QEvent::Paint)) в каком потоке выполняется? )

и я всё же не понимаю: если вся отрисовка виджета выполняется в отдельном потоке, то как происходит синхронизация с основным потоком? - в коде выше ничего подобного нет
Записан
DAMAL
Гость
« Ответ #8 : Август 31, 2008, 23:15 »

В главном потоке как и положено создаётся главное диалоговое окно. Во втором потоке создаётся объект класса myQLabel (см. первый пост в этой теме). В главном диалоговом окне имеется указатель на созданный объект myQLabel, по этому указателю главный диалог может вызывать метод myQLabel::setNextFrame(unsigned char *bitmap). Объект myQLabel при получении очередного буфера с изображением сам себе шлёт postEvent с событием о перерисовке.

Вижу, что QWidget::update() делает то же самое, но я первым делом пробовал такой вариант - иксы писали ту самую ошибку об асинхронности. Может для Linux QWidget::update() определён иначе?
На самом деле, пусть даже был бы в моём коде QWidget::update() вместо QCoreApplication::postEvent(this, new QEvent(QEvent::Paint)). Накопление событий в очереди это бы не отменило. Так или иначе, хочется контролировать очередь отрисовки. Допустим, по моей схеме рисуется график с ритмами сердца - на данный момент: пациент уже давно умер, а на экране ещё долго зигзаги вместо прямой линии. :-)
Записан
ритт
Гость
« Ответ #9 : Сентябрь 01, 2008, 01:40 »

т.е. во втором потоке создаётся виджет, ложится на диалог, выполняющийся в первом потоке, а затем дёргаются события на перерисовку из первого потока во второй и результат - снова в первый? и всё это без механизмов синхронизации потоков?
мне кажется, что правильнее было бы оставить виджет там, где ему и место, подготавливать изображение во втором потоке, а по готовности через QueuedConnection слать сигнал, связанный со слотом update() виджета. дополнительно можно предусмотреть синхронизацию потоков, что может дать некоторую оптимизацию
Записан
DAMAL
Гость
« Ответ #10 : Сентябрь 01, 2008, 07:48 »

Виджет во втором потоке не имеет видимых и логических связей с диалогом, он открывается в отдельном окне. Синхронизация выполняется на уровне защитой мьютексом буфера "image" во втором потоке. Готовить изображение можно только в первом потоке - там оно снимается с физического устройства.
А про "QueuedConnection" я почитаю, не пользовался пока этип классом!
Записан
ритт
Гость
« Ответ #11 : Сентябрь 01, 2008, 08:15 »

QueuedConnection - это значение опционального параметра QObject::connect Улыбающийся

если синхронизацию потоков выполняешь /* правильно */, то не вижу где могут накапливаться события и от чего может лагать
единственное что, QCoreApplication::postEvent(this, new QEvent(QEvent::Paint)) ставит событие в очередь и если делать именно так, то чтобы события не скапливались в очереди, перед каждой постановкой очередь нужно очищать...а там могут находиться вовсе и не QEvent::Paint

ну, что могу ещё предложить? сделай два буффера - пока один заблокирован, заполняй второй; а потом наоборот (правда, не очень-то понял к чему тут memcpy(image, bitmap, size) - может это и есть "второй" буффер? в таком случае который из них локаешь?)
Записан
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #12 : Сентябрь 01, 2008, 11:59 »

Во втором потоке создаётся объект класса myQLabel

Вот это уже интересно. Все гуевые элементы должны создаваться в гуевом (главном) потоке.

Цитировать
In GUI applications, the main thread is also called the GUI thread because it's the only thread that is allowed to perform GUI-related operations.

Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread. As noted earlier, QCoreApplication::exec() must also be called from that thread.

« Последнее редактирование: Сентябрь 01, 2008, 12:01 от pastor » Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
DAMAL
Гость
« Ответ #13 : Сентябрь 01, 2008, 13:01 »

ну, что могу ещё предложить? сделай два буффера - пока один заблокирован, заполняй второй; а потом наоборот (правда, не очень-то понял к чему тут memcpy(image, bitmap, size) - может это и есть "второй" буффер? в таком случае который из них локаешь?)

image - буфер класса myQLabel, он подлежит лочению, ведь в момент вызова setNextFrame() он может читаться в методе paintEvent(). Копирование необходимо потому как при выходе из setNextFrame() входной буфер bitmap будет удалён.
Причины накопления во многом понятны. Qt при рисовании вроде как пытается искать изменённые от предыдущего кадра регионы и перерисовывать только их. А это вычислительные операции. Вот так и не смог "заставить" Qt сразу посылать кадры на отображение, в идеале бы ещё и через overlay (т.е. в память видеокарты).

По поводу виджета вне гуёвого потока - согласен, не очень по рекомендациям. Но ведь Qt не выдаёт критическую ошибку от такой архитектуры! Да и видел я что-то такое в примерах...
Записан
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #14 : Сентябрь 01, 2008, 13:43 »

Но ведь Qt не выдаёт критическую ошибку от такой архитектуры! Да и видел я что-то такое в примерах...

Вам чесно сказать просто ПОКА везет с такой архитектурой. Обычно приложение валиться в очень неожиданых местах. А в каких примерах вы такое видели? Какую версию Qt используете?

И ещё... можно ли взглнянуть на код второго потока где создаеться экземпляр myQLabel?
« Последнее редактирование: Сентябрь 01, 2008, 13:44 от pastor » Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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