Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: marty от Июль 15, 2009, 17:14



Название: Как рисовать не в paintEvent?
Отправлено: marty от Июль 15, 2009, 17:14
 Здравствуйте!
 Собственно сабж, и возможнро ли это?

 Хочу рисовать (дорисовывать) изображение/его части, реагируя на действия пользователя - мышь, клавиатура, при этом не хотелось бы самому заводить QPixmap, в который будет производится отрисовка, и который будет копироваться в widget по paintEvent'у.
 Хочу так:
1) Создаем Qpixmap размером с виджет.
2) копируем в него изображение виджета (или только его части), планируется использовать hint о том, какая область будет обновляться
3) Рисуем на QPixmap'е
4) Копируем QPixmap или его часть на виджет.

 Не пойму, как реализовать пп 2, 4? Нашел вроде метод grabWidget, но как я понял, он вызывает событие paintEvent для виджета, чего как раз хочется избежать.



Название: Re: Как рисовать не в paintEvent?
Отправлено: f-r-o-s-t от Июль 15, 2009, 17:31
Мало понятно чего ты хочешь, подробней можно зачем оно тебе
как рисовать первое что нашел в Assistent это Plug & Paint Example


Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 15, 2009, 18:01
 Я хочу модифицировать изображение на виджете по различным событиям - так, пример, на котором я пытаюсь добиться нужного мне поведения, по движению мышки печатает в углу ее координаты.
 Что я не хочу - не хочу заводить pixmap, на котором все будет рисоваться, для последующей переброски на экран по paintEvent (так сделано в Plug & Paint); не хочу запоминать данные и потом учитывать при перерисовке по paintEvent.
 Backbuffer pixmap я не хочу заводить - тогда для каждого класса виджета, который хочет рисовать не только по paintEvent, я должен наследоваться не от QWidget, а от моего класса, в котором этот бэкбуфер реализован.
 Запоминать данные и потом делать update - перерисовка виджетов может быть длительной, и поэтому не хочется все перерисовывать.
 В принципе, если ничего другого не остается, то наверно так и сделаю. Тогда вопрос возникает, по каким событиям следует изменять размеры беэкбуфера? И второй возникающий вопрос - QPainter (или QWidget) вроде реализует двойную буфферизацию, если я сам буду буфер использовать, как отключить стандартную?


Название: Re: Как рисовать не в paintEvent?
Отправлено: f-r-o-s-t от Июль 15, 2009, 18:15
А какой нибудь пример кроме мышки, подробнее что должно быть в конце.
отключить буферезацию QWidget::setAttribute(Qt::WA_PaintOnScreen).


Название: Re: Как рисовать не в paintEvent?
Отправлено: lit-uriy от Июль 15, 2009, 18:44
marty может проще рисовать повех виджета, как здесь (http://www.crossplatform.ru/node/887), задачка изначальная была другая, но на основе неё можно сделать.


Название: Re: Как рисовать не в paintEvent?
Отправлено: lit-uriy от Июль 15, 2009, 18:53
Вот ещё: Рисование_поверх_дочерних_виджетов (http://wiki.crossplatform.ru/index.php/Рисование поверх дочерних виджетов)


Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 15, 2009, 20:31
А какой нибудь пример кроме мышки, подробнее что должно быть в конце.
отключить буферезацию QWidget::setAttribute(Qt::WA_PaintOnScreen).

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

 Написал такой класс:
Код:
template <typename T>
class QOffscreenDraw : public T
{
    QPixmap backBuffer;
    bool    bOnlyCopyBackBuffer;

public:

   QOffscreenDraw(QWidget * parent = 0, Qt::WindowFlags f = 0)
       : T(parent, f)
       , backBuffer()
       , bOnlyCopyBackBuffer(false)
       {
        backBuffer = QPixmap ( width(), height() );
         /* T:: */ setAttribute(Qt::WA_PaintOnScreen);
       }

    QPixmap* getOffscreenBuffer()
       {
        return &backBuffer; // QPainter(&backBuffer);
       }

    void updateFromBackBuffer()
       {
        bOnlyCopyBackBuffer = true;
        repaint();
        bOnlyCopyBackBuffer = false;
       }

protected:

    virtual void doPaint( QPainter *painter, QPaintEvent *event )
       {}

    void resizeEvent( QResizeEvent * event )
       {
        T::resizeEvent(event);
        if (!event->isAccepted()) return;
        backBuffer = QPixmap( event->size() );
       }

    void paintEvent(QPaintEvent *event)
       {
        if (!bOnlyCopyBackBuffer)
           {
            QPainter painter(&backBuffer);
            doPaint( &painter, event );
           }

        QPainter thisPainter(this);
        thisPainter.drawPixmap( QRect( 0, 0, width(), height()), backBuffer, QRect( 0, 0, width(), height()) );
       }
};


class RenderArea : QOffscreenDraw<QWidget>




Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 15, 2009, 20:38
Что-то редактор сообщений стало колбасить, не смог дописать
Код:
// испорльзовать так
class RenderArea : QOffscreenDraw<QWidget>
{
    // переопределяем для рисования всей картинки целиком
    void doPaint( QPainter *painter, QPaintEvent *event );
};

// Если нужно асинхронно (не в paintEvente) порисовать, делаем так
{
 ...
    { // QPainter заключили в отдельную область видимости, чтоб он разрушился перед копированием картинки,
      // можно сделать p.end();
     QPainter p(renderArea->getOffscreenBuffer());
     // рисуем
    }
    renderArea->updateFromBackBuffer();
}




Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 15, 2009, 20:43
 А вообще, что-то Qt не такая уж хорошая, раз для такого простого действия надо так извращаться.


Название: Re: Как рисовать не в paintEvent?
Отправлено: Rcus от Июль 15, 2009, 21:14
Точно, не бери Qt, жизнь себе поломаешь! ©
Такие заявления мне кажутся забавными, особенно на фоне серьезных обсуждений багов и недостатков архитектуры Qt :)


Название: Re: Как рисовать не в paintEvent?
Отправлено: lit-uriy от Июль 15, 2009, 21:27
>>Точно, не бери Qt, жизнь себе поломаешь! ©
Плюс милиён, говно этот Qt, надо на Визуал Басике, тем более, что там уже всё за тебя сделано.


Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 16, 2009, 01:32
Точно, не бери Qt, жизнь себе поломаешь! ©
Такие заявления мне кажутся забавными, особенно на фоне серьезных обсуждений багов и недостатков архитектуры Qt :)
Что в этом заявлении кажется вам забавным? По-моему, данное ограничение по рисованию только во время paintEvent довольно большой недостаток, которого нет у WinAPI (и его оберток) и у wxWidgets.


Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 16, 2009, 01:35
>>Точно, не бери Qt, жизнь себе поломаешь! ©
Плюс милиён, говно этот Qt, надо на Визуал Басике, тем более, что там уже всё за тебя сделано.
Я бы с удовольствием, но надо на плюсах, да так, чтоб работало с WTL, wxWidgets да с Qt. С последним постоянно какой-то гемморой возникает, в отличии от первых двух библиотек.
 Вы бы лучше приведенный код попинали, вместо ёрничания.


Название: Re: Как рисовать не в paintEvent?
Отправлено: lit-uriy от Июль 16, 2009, 01:42
Продолжать сравнение Qt лучше здесь (http://www.prog.org.ru/index.php?topic=9980)


Название: Re: Как рисовать не в paintEvent?
Отправлено: lit-uriy от Июль 16, 2009, 01:44
>>Вы бы лучше приведенный код попинали, вместо ёрничания.
ну я тебе ссылки давал, во втрой ссылке  как раз расматривается вопрос рисования не в paintEvent


Название: Re: Как рисовать не в paintEvent?
Отправлено: lit-uriy от Июль 16, 2009, 01:48
Цитировать
по их приходу надо подправить картинку, не перерисовывая целиком, потому что больно долго будет. Также данные куда-то складываются, и когда надо все перерисовать, то они тоже используются.
подобная ситуация возникает например при построении графика. Есть такая библиотека Qwt, можно наверняка глянуть её исходники.

Я не замечал существенных тормозов на динамически обновляемых графиках в их примерах.


Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 16, 2009, 01:55
>>Вы бы лучше приведенный код попинали, вместо ёрничания.
ну я тебе ссылки давал, во втрой ссылке  как раз расматривается вопрос рисования не в paintEvent
За ссылки спасибо, правда это не совсем то, что нужно. То, что нужно, пришлось самому написать ;)


Название: Re: Как рисовать не в paintEvent?
Отправлено: BRE от Июль 16, 2009, 07:28
По-моему, данное ограничение по рисованию только во время paintEvent довольно большой недостаток, которого нет у WinAPI (и его оберток) и у wxWidgets.
Рисовать на большинстве PaintDevice можно в любой момент, а вот рисовать на виджете можно только в paintEvent. Кстати как и в WinAPI и у wxWidgets (если мне память не изменяет).  :)

Я бы с удовольствием, но надо на плюсах, да так, чтоб работало с WTL, wxWidgets да с Qt. С последним постоянно какой-то гемморой возникает, в отличии от первых двух библиотек.
В свете последних обсуждений, вырисовывается закон:
Чем меньше человек знаком с Qt, тем больше он находит в ней несуществующих проблем.  ;)


Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 16, 2009, 10:45
По-моему, данное ограничение по рисованию только во время paintEvent довольно большой недостаток, которого нет у WinAPI (и его оберток) и у wxWidgets.
Рисовать на большинстве PaintDevice можно в любой момент, а вот рисовать на виджете можно только в paintEvent. Кстати как и в WinAPI и у wxWidgets (если мне память не изменяет).  :)

С wxWidget по этому вопросу еще не разбирался, а в винде можно рисовать в любой момент. Есть только небольшое отличие - контекс HDC надо получать через BeginPaint (в обработчике WM_PAINT) или GetDC в остальных случаях.


Название: Re: Как рисовать не в paintEvent?
Отправлено: Авварон от Июль 16, 2009, 10:54
контекст устройства это вообще ппц... перегрузка пейнт эвента гораздо проще и адекватнее выглядит


Название: Re: Как рисовать не в paintEvent?
Отправлено: marty от Июль 16, 2009, 13:14
контекст устройства это вообще ппц... перегрузка пейнт эвента гораздо проще и адекватнее выглядит
А чего тут ппц? В paintEvent'е все равно используется контекст рисования, а как он называется, HDC, wxDC или QPainter, это по-моему не принципиально.


Название: Re: Как рисовать не в paintEvent?
Отправлено: Авварон от Июль 16, 2009, 13:28
эти классы имеют _разный_ смысл. контекст устройства - непонятная примочка, нафиг откуда мне взявшаяся. qpainter - класс-рисовальщик. А всякие bitblt это вообще вынос мозга...


Название: Re: Как рисовать не в paintEvent?
Отправлено: break от Июль 16, 2009, 22:27
Прочитал тему с самого начала!
Не пойму одного почему автор не хочет рисовать в PaintEvent - даже если предположить что в Qt с этим проблемы - зачем не использовать то подо что в Qt все рисования и затачивались. Кроме того контекст QPainter - сильно от личается от других контекстов- он имеет кучу доп. возможностей - например трансформации(scale, rotate и др.).


Название: Re: Как рисовать не в paintEvent?
Отправлено: fuCtor от Июль 18, 2009, 21:24
Заходя на форум хотел создать почти такой же топик.

Автора топика очень даже понимаю. самого интересует вопрос как бы ускорить вывод порядка тысячи полигонов с переключением стилей  и тд. По замерам на отрисовку окна 1024 на 768 на котором помещалось порядка пары тысяч полигонов уходило порядка секунды. Отрисовку выполнял в буфер а только потом вывод буфера на бэкграунд GraphicsView.
Скорость явна маленькая и неприемлимая. При том что под WinAPI удалось точно такой же код разогнать до 300мс. Вознивает вопрос. Что не так делаю и куда копать чтоб ускорить отрисовку. Буфер используется для частичной перерисовки при смещении., путем дорисовки необходимого.


Название: Re: Как рисовать не в paintEvent?
Отправлено: ufna от Июль 18, 2009, 21:28
вопрос - а стили и т.п., да и сами полигоны на поле у тебя динамичные или статичные?


Название: Re: Как рисовать не в paintEvent?
Отправлено: fuCtor от Июль 18, 2009, 21:37
статичны и хранятся в дереве (полигоны). Стили тоже статичны. Если загрузить ВСЕ полигоны в виде GraphicsItem то это cъедает не мало памяти. Если фрагментами, то нарушается порядок следования (z-order). Судя по профайлеру то 80% времени съедает вывод именно полигонов, а не смена стиля рисования.


Название: Re: Как рисовать не в paintEvent?
Отправлено: ufna от Июль 18, 2009, 21:48
а можно сам код отрисовки глянуть?


Название: Re: Как рисовать не в paintEvent?
Отправлено: fuCtor от Июль 18, 2009, 21:56
Сейчас нет под рукой кода. Там просто хитрая система накручина с различными абстракциями и тд ). Собственно сами данные это картография.

а сама отрисовки сводится к:
- вывод обычных полигонов + плюс накопление для отложенного вывода при определенных условиях
- обработка списка отложенных полигонов
- вывод их


Название: Re: Как рисовать не в paintEvent?
Отправлено: ufna от Июль 19, 2009, 08:57
Просто любой рендеринг - это оптимизация. В данном случае я бы вообще выводил не на GVC (по-моему излишне, если все и так в буфер только пишется). И тут нужно смотреть флаги с которыми ты рисуешь картинку, как ее рисуешь, что еще важнее, потому как оптимизация вывода - штука, на которой все игрушки работают черт знает сколько лет уже :)

если картография, то наверняка у тебя нет каждый раз изменения стилей, вывод - пока не сменили стиль, перерисовывать не нужно. Можно разбить на куски, которые только будем перерисовывать, если возможно. Порядок z-следования - нужно считать отдельно заранее (раз он должен сохраниться). И т.д., и т.п. Потому и хочу посмотреть, т.к. код сказал бы куда большее :)


Название: Re: Как рисовать не в paintEvent?
Отправлено: fuCtor от Июль 19, 2009, 12:36
Полученный буфер потом выводится на QGV и поверх него еще элементы интерфейса рисуются (QGI).
Оптимизировал алгоритм, стало все очень даже прилично ) Осталось с надписями разобраться, раза в 3 4 просаживают.
Для дополнительной оптимизации вобще думаю переписать paintEvent у QGV (взять его родной и убрать оттуда буфер фона, заменив его на свой).

А код в итоге не разрешили показывать :( мол коммерческая тайна...


Название: Re: Как рисовать не в paintEvent?
Отправлено: ufna от Июль 19, 2009, 15:14
вот видишь :)

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