crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

> QPaintEvent, installEventFilter, QPainter, Overpainting
SABROG
  опции профиля:
сообщение 26.5.2009, 11:52
Сообщение #1


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Есть желание рисовать на дочерних окошках, причем хочется оставить их оригинальный render и рисовать поверх уже готовой картинки. Ввиду того, что дочерних элементов может быть сколько угодно, я не могу (а скорее не хочу) унаследоваться от каждого из них. Поэтому я использую installEventFilter.

Получился такой код:

bool MainWindow::eventFilter(QObject *o, QEvent *e)
{
    if (e->type() == QEvent::Paint && o->isWidgetType()) {
        QPaintEvent *pe = (QPaintEvent *)e;
        QWidget *w = (QWidget *)o;
        qDebug() << "painting:" << o;

        QObject::eventFilter(o, e); //пытаемся вызвать оригинальный обработчик, чтобы сам себя нарисовал
        QPainter p(w); // пытаемся нарисовать что-то поверх уже нарисованного
        p.setRenderHint(QPainter::Antialiasing,true);
        p.drawLine(QPointF(0,0),QPointF(20,90));
        return true; //говорим системе, что мы перехватили, чтобы не отправлял событие дальше, а следовательно и не перерисовывал
    } else {
        return QObject::eventFilter(o, e);
    }
}


Но не получается. Если убрать первый QObject::eventFilter(o, e);, то виджет рисуется поверх линии, т.е. она остается на заднем плане. Если оставить, то рисуется вообще только одна линия, оригинального изображения виджета нет. paintEvent вызвать напрямую не могу, т.к. он protected, render() и repaint() приводят к рекурсии, естественно.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
11 страниц V  « < 2 3 4 5 6 > »   
Начать новую тему
Ответов (30 - 39)
kwisp
  опции профиля:
сообщение 27.5.2009, 9:01
Сообщение #31


астарожна ынтжинэр
*****

Группа: Участник
Сообщений: 1404
Регистрация: 26.11.2008
Из: ТаганрогРодинаЧехова
Пользователь №: 435

Спасибо сказали: 113 раз(а)




Репутация:   23  


Цитата(igor_bogomolov @ 27.5.2009, 9:52) *
линии практически совпадают
Цитата(igor_bogomolov @ 27.5.2009, 9:52) *
возвращает неправильные координаты и размеры.

так и не понял получается или нет.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
igor_bogomolov
  опции профиля:
сообщение 27.5.2009, 9:32
Сообщение #32


Профессионал
*****

Группа: Сомодератор
Сообщений: 1215
Регистрация: 22.3.2009
Из: Саратов
Пользователь №: 630

Спасибо сказали: 235 раз(а)




Репутация:   29  


Цитата(SABROG @ 27.5.2009, 9:51) *
если установить фильтр только на главное окно
Тогда, в eventFilter(QObject *obj, QEvent *event) obj == главное окно, например QDialog
Далее
Цитата(SABROG @ 27.5.2009, 9:51) *
через sendEvent отправить событие отрисовки
QApplication::sendEvent(obj, event); т.е. ты отрисуешь только сам QDialog. Никаких child-ов здесь отрисовано не будет!!!!!
Цитата(SABROG @ 27.5.2009, 9:51) *
правление вернется обратно в фильтр событий
Здесь мы отрисуюем то что мы хотим, только по QDialog.

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

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



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

kwisp, Если соберешь проект, увидешь, что через кнопку линия рисуется правильно. Для QTextEdit метод geometry почему то возвращает неправильные координыты? Как с этим бороться?
Прикрепленные файлы
Прикрепленный файл  temporary.zip ( 1.54 килобайт ) Кол-во скачиваний: 123
 
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
kwisp
  опции профиля:
сообщение 27.5.2009, 10:20
Сообщение #33


астарожна ынтжинэр
*****

Группа: Участник
Сообщений: 1404
Регистрация: 26.11.2008
Из: ТаганрогРодинаЧехова
Пользователь №: 435

Спасибо сказали: 113 раз(а)




Репутация:   23  


igor_bogomolov,
готово
проблема в viewport() он возвращает не совсем точную геометрию текседита.
я так написал криво извини я всетаки на работе думаю понятно

Раскрывающийся текст

#include <QtCore/QEvent>
#include <QtCore/QTimer>
#include <QtGui/QTextEdit>
#include <QtGui/QGridLayout>
#include <QtGui/QVBoxLayout>
#include <QtGui/QPainter>
#include <QtGui/QApplication>
#include <QtGui/QPushButton>
#include <QtDebug>

#include "form.h"

form::form(QWidget *parent) : QMainWindow(parent)
{
    setMinimumSize(200, 180);
    centralwidget = new QWidget(this);
    centralwidget->setObjectName("centralwidget");
    setCentralWidget(centralwidget);

    QTextEdit *textedit = new QTextEdit(centralwidget);
    textedit->setObjectName("textedit");
    QPushButton *btn = new QPushButton("text", this);
    btn->setObjectName("btn");

    QVBoxLayout *vl = new QVBoxLayout(centralwidget);
    vl->addWidget(textedit);
    vl->addWidget(btn);

    centralwidget->setLayout(vl);

    centralwidget->installEventFilter(this);
    textedit->viewport()->installEventFilter(this);
    btn->installEventFilter(this);
}


bool form::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == QEvent::Paint) {
        obj->removeEventFilter(this);
        QApplication::sendEvent(obj, event);
        obj->installEventFilter(this);

//        qDebug()<< qobject_cast<QWidget*>(obj)
  //              << qobject_cast<QWidget*>(obj)->geometry()
    //            <<" "
    //            << qobject_cast<QWidget*>(obj)->visibleRegion();

        QPainter p(qobject_cast<QWidget*>(obj));
        p.setPen(Qt::red);
        if(qobject_cast<QWidget*>(obj)->objectName() == "qt_scrollarea_viewport")
            p.setWindow(centralwidget->findChild<QTextEdit*>("textedit")->geometry());
        else
        p.setWindow(qobject_cast<QWidget*>(obj)->geometry());
        p.drawLine(rect().topLeft(), rect().bottomRight());
        return true;
    }
    return false;
}

Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
igor_bogomolov
  опции профиля:
сообщение 27.5.2009, 10:32
Сообщение #34


Профессионал
*****

Группа: Сомодератор
Сообщений: 1215
Регистрация: 22.3.2009
Из: Саратов
Пользователь №: 630

Спасибо сказали: 235 раз(а)




Репутация:   29  


kwisp, все правильно. Сам только что до этого додумался. Дело в том что viewport возвращем координаты относительно QTextEdit.

Блин, как все усложняется, относительно составных виджетов. Через QCalendarWidget, вообще непонятно как линию провести.
Но уже почти. Немного осталось додумать.

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

Сообщение отредактировал igor_bogomolov - 27.5.2009, 10:35
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Kagami
  опции профиля:
сообщение 27.5.2009, 10:47
Сообщение #35


Старейший участник
****

Группа: Участник
Сообщений: 601
Регистрация: 2.2.2009
Пользователь №: 523

Спасибо сказали: 101 раз(а)




Репутация:   9  


Можно подумать в эту сторону
Раскрывающийся текст
Цитата
QPoint QWidget::mapTo ( QWidget * parent, const QPoint & pos ) const

Translates the widget coordinate pos to the coordinate system of parent. The parent must not be 0 and must be a parent of the calling widget.

See also mapFrom(), mapToParent(), mapToGlobal(), and underMouse().

QPoint QWidget::mapToGlobal ( const QPoint & pos ) const

Translates the widget coordinate pos to global screen coordinates. For example, mapToGlobal(QPoint(0,0)) would give the global coordinates of the top-left pixel of the widget.

See also mapFromGlobal(), mapTo(), and mapToParent().

QPoint QWidget::mapToParent ( const QPoint & pos ) const

Translates the widget coordinate pos to a coordinate in the parent widget.

Same as mapToGlobal() if the widget has no parent.

See also mapFromParent(), mapTo(), mapToGlobal(), and underMouse().
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
igor_bogomolov
  опции профиля:
сообщение 27.5.2009, 11:06
Сообщение #36


Профессионал
*****

Группа: Сомодератор
Сообщений: 1215
Регистрация: 22.3.2009
Из: Саратов
Пользователь №: 630

Спасибо сказали: 235 раз(а)




Репутация:   29  


Цитата(Kagami @ 27.5.2009, 11:47) *
Можно подумать в эту сторону
Надо посмотреть.


Пока решил так. Работает на ура.
Раскрывающийся текст
bool form::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == QEvent::Paint) {
        obj->removeEventFilter(this);
        QApplication::sendEvent(obj, event);
        obj->installEventFilter(this);

        QWidget *widget = qobject_cast<QWidget*>(obj);
        QPoint point = widget->pos();
        while(widget && (widget->parentWidget() != this)) {
            widget = widget->parentWidget();
            point += widget->pos();
        }

        QRect r = qobject_cast<QWidget*>(obj)->rect().translated(point);

        QPainter p(qobject_cast<QWidget*>(obj));
        p.setPen(Qt::red);
        p.setWindow(r);

        p.drawLine(rect().topLeft(), rect().bottomRight());
        return true;
    }
    return false;
}
:yahoo:
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
igor_bogomolov
  опции профиля:
сообщение 27.5.2009, 11:54
Сообщение #37


Профессионал
*****

Группа: Сомодератор
Сообщений: 1215
Регистрация: 22.3.2009
Из: Саратов
Пользователь №: 630

Спасибо сказали: 235 раз(а)




Репутация:   29  


Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 27.5.2009, 12:30
Сообщение #38


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


В общем затык на отрисовке чайлдов. Как я понял схема такая repaint()->repaint_sys()->QWidgetPrivate::drawWidget(и тут рекурсия этого drawWidget на всех чайлдах). Но похоже я что-то упускаю, как-то надо QPainter инициализировать чтоль, но это уже похоже надо лезть в трусы к QWidgetPrivate членам.

Насчет всех чайлдов. Я такую функцию написал:

static void childsRecursive(QObject *object, QWidget *watcher, bool install)
{
    if (object->isWidgetType()) {
        if (install) object->installEventFilter(watcher);
        else object->removeEventFilter(watcher);
    }
    QObjectList children = object->children();
    foreach(QObject *child, children) {
        childsRecursive(child, watcher, install);
    }
}


Юзать так:
childsRecursive(this, this, true); //устанавливаем обработчик на все чайлды и само окно
childsRecursive(this, this, false); //снимаем обработчик со всех чайлдов и окна


P.S.: теперь вариант с прозрачным QWidget'ом поверх всех окон мне кажется не таким уж и плохим вариантом. Но я еще не сдался :)

Сообщение отредактировал SABROG - 27.5.2009, 12:38
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
igor_bogomolov
  опции профиля:
сообщение 27.5.2009, 12:59
Сообщение #39


Профессионал
*****

Группа: Сомодератор
Сообщений: 1215
Регистрация: 22.3.2009
Из: Саратов
Пользователь №: 630

Спасибо сказали: 235 раз(а)




Репутация:   29  


Цитата(SABROG @ 27.5.2009, 13:30) *
В общем затык на отрисовке чайлдов. Как я понял схема такая repaint()->repaint_sys()->QWidgetPrivate::drawWidget(и тут рекурсия этого drawWidget на всех чайлдах). Но похоже я что-то упускаю, как-то надо QPainter инициализировать чтоль, но это уже похоже надо лезть в трусы к QWidgetPrivate членам.
Ничего не понял, если честно. Но с трусами и членами силно завернул.

childsRecursive заюзал. Все работает. Удобно.

Так я что-то и не понял, что тебя не устраивает??? Задачу решили, на мой взглян, хорошо. Работает и рисует как надо.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 27.5.2009, 13:19
Сообщение #40


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Цитата(igor_bogomolov @ 27.5.2009, 13:59) *
Цитата(SABROG @ 27.5.2009, 13:30) *
В общем затык на отрисовке чайлдов. Как я понял схема такая repaint()->repaint_sys()->QWidgetPrivate::drawWidget(и тут рекурсия этого drawWidget на всех чайлдах). Но похоже я что-то упускаю, как-то надо QPainter инициализировать чтоль, но это уже похоже надо лезть в трусы к QWidgetPrivate членам.
Ничего не понял, если честно. Но с трусами и членами силно завернул.

childsRecursive заюзал. Все работает. Удобно.

Так я что-то и не понял, что тебя не устраивает??? Задачу решили, на мой взглян, хорошо. Работает и рисует как надо.

Оверхед сплошной :) Тут такая ситуация вырисовывается: в порядке рекурсии испускаются QPaintEvent'ы начиная от родителя до последнего самого глубокого ребенка (это если они вообще видимы, конечно). Мне надо как-то сделать так, чтобы обойти стандартный механизм рекурсии и отрисовать только одно окно не вызывая при этом перерисовку (генерацию эвентов перерисовки) для дочерних окон. Наткнулся на любопытный код:

    QPaintEvent *pe = new QPaintEvent(ui->groupBox->rect());
    QPixmap pix(ui->groupBox->size());
    pix.fill(Qt::white);
    QPainter::setRedirected(ui->groupBox, &pix);
    QApplication::sendEvent(ui->groupBox, pe);
    QPainter::restoreRedirected(ui->groupBox);
    pix.save("file.jpg");


По сути тоже самое, что и grabWidget(). Но тут отличие в том, что сгенеренный эвент срабатывает и окно (без детей) рисуется в файл. Если вместо &pix редиректнуть на другой виджет, то уже не пашет.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

11 страниц V  « < 2 3 4 5 6 > » 
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 2.6.2024, 8:06