Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: [Решено] Рисование во View?
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Система рисования. Печать
Obey-Kun
Нужно создать класс, который действовал бы аналогично QRubberBand. То есть чтобы его можно было создавать во вьюпорте View и изменять его параметры (размер, позицию), не заботясь о его перерисовке (чтобы он при этом сам перерисовывался).

Можете привести пример для, например, эллписа (и чтоб описывался он, по традиции Qt, как QRect). Использовать хочется по некоторой аналогии с QRubberBand:
В хедере View делается
QPoint m_origin;
AbstractRubberBand m_rubberband;


Использовать хочу так:
 void Widget::mousePressEvent(QMouseEvent *event)
{
     m_origin = event->pos();
     if (m_rubberband == NULL) {
         m_rubberband = new (m_instrument == Ellipse ? EllipseRubberBand(this) : RectangleRubberBand(this) );
     }
     m_rubberband->setGeometry(QRect(origin, QSize()));
     m_rubberband->show();
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    if (m_rubberban != NULL) {
       m_rubberband->setGeometry(QRect(origin, event->pos()).normalized());
    }
}

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    if (m_rubberban != NULL) {
     m_rubberband->hide();
     // делаем что надо с полученной фигурой и удаляем m_rubberband
    }
}


Также нужен инструмент полигональной линии — в него последовательно (сигналами или ещё как) отправляются QPointF'ы или сигналы изменения позиции одного из QPointF, потом удаляем его и получаем shape.

Итак, что наследовать, что переопределять? :)

Опечатка, в хедер хочу делать так
QPoint m_origin;
AbstractRubberBand *m_rubberband;
Litkevich Yuriy
Obey-Kun, ты вообще имеешь представление как рисуют на виджетах?
Есть демки, например, Сглаживание траектории
Имеет смысл посмотреть её код, в ней используется система рисования Arthur, т.е. рисование QPainter'ом в обработчике события рисования (QWidget::paintEvent())
Obey-Kun
Если честно, демку не осилил (всё в одну кучу сброшено, лень было разбираться), но волшебного слова «paintEvent» хватило, спасибо :)
Думал, что всё гораздо сложнее. Экспериментирую.
Obey-Kun
Хедер:
#pragma once
#ifndef MYRECTANGLE_H
#define MYRECTANGLE_H

#include <QWidget>

class MyRectangle : public QWidget
{
public:
    MyRectangle(QWidget* parent = 0);
    void changeRect(QRectF inRect);

private:
    QRectF m_rect;

protected:
    void paintEvent(QPaintEvent* );
};

#endif // MYRECTANGLE_H


Реализация:
#include "myrectangle.h"

#include <QtGui/QPainter>

MyRectangle::MyRectangle(QWidget* parent): QWidget(parent),
m_rect(10,10,10,10)
{
    setAttribute(Qt::WA_TransparentForMouseEvents);
    update();
}


void MyRectangle::paintEvent(QPaintEvent* event)
{
    Q_UNUSED(event);
    QPainter painter;
    painter.begin(this);
    painter.setPen(Qt::blue);
    painter.drawRect(m_rect);
    painter.end();
}

void MyRectangle::changeRect(QRectF inRect)
{
    m_rect=inRect;
    update();
}


В свой view для эксперимента для MyRectangle *m_recttest. В конструкторе сделал m_recttest = new MyRectangle(this), в ивент нажатия мыши для левой кнопки m_recttest->changeRect(QRectF(20,20,20,20)), для правой m_recttest->changeRect(QRectF(10,10,20,20)).

Рисуется, но как-то странно. Он обрезается какой-то областью. Вот, смотрите:
После создания (m_rect равен QRectF(10,10,10,10)):

После m_recttest->changeRect(QRectF(10,10,20,20)):

После m_recttest->changeRect(QRectF(20,20,20,20)):


Почему так?
Litkevich Yuriy
Цитата(Obey-Kun @ 27.3.2010, 22:01) *
Он обрезается какой-то областью
если мышкой работать медленно, то артефакты (следы, остатки) какие-нибудь появляются?
Obey-Kun
нет, никаких артефактов не видно ни при каких условиях
Litkevich Yuriy
А объект класса MyRectangle, как используется?
Obey-Kun
Я же говорю, в хедере View:
MyRectangle *m_recttest;

В конструкторе:
m_recttest = new MyRectangle(this);

И
void Area::mousePressEvent(QMouseEvent* event)
{
    if(event->button() == Qt::LeftButton) {
        m_recttest->changeRect(QRectF(20,20,20,20));
    }
    if(event->button() == Qt::RightButton) {
        m_recttest->changeRect(QRectF(10,10,20,20));
    }
}
Litkevich Yuriy
Obey-Kun, Т.е. ты положил этот виджет, в другой и без компоновщика. При этом не задав ни размер виджета ни его координату. Изменяя только прямоугольник рисования. соответвенно виджет пытается себя нарисовать за пределами своих размеров, за которыми Qt просто ничего не нарисует.

См. QWidget::setGeometry(), QWidget::resize(), ...

если вообще использовать такой подход как у тебя, с виджетом. То в представлении вместо:
m_recttest->changeRect(QRectF(20,20,20,20));
использовать:
m_recttest->setGeometry(QRectF(20,20,20,20));

А в самом виджете вместо:
painter.drawRect(m_rect);
использовать:
painter.drawRect(rect());

Тогда метод changeRect вообще не нужен
Obey-Kun
Спасибо! Поправил конструктор:
MyRectangle::MyRectangle(QWidget* parent): QWidget(parent),
m_rect(10,10,10,10)
{
setAttribute(Qt::WA_TransparentForMouseEvents);
resize(parent->size());
update();
}
Правильное решение?

Вопрос немного не по теме — если планируется использовать несколько виджетов, которые будут рисоваться поверх моего вью, стоит ли использовать компоновщик?

Ух, сообщение тогда ещё не обновилось. Так и сделаю.

А какие ещё могут быть подходы?

setGeometry(QRectF) не бывает, только setGeometry(QRect) и setGeometry(int, int, int, int).

В общем, геометрия виджета описывается как QRect. Смотрим в описание QRect:
Цитата
Note that for historical reasons the values returned by the bottom() and right() functions deviate from the true bottom-right corner of the rectangle

И нарисована всеописывающая картинка. Я из-за этой путаницы c QRect голову ломал пару дней назад. Короче, получается такая фигня:


Верно понимаю, что обходить это вот так:
painter.drawRect(rect().x(), rect().y(), rect().width() -1, rect().height()-1);

?
Litkevich Yuriy
Тут всё по-русски: Описание класса QRect
Цитата
Мы рекомендуем вам использовать x() + width() и y() + height(), чтобы найти истинный нижний-правый угол, и избегать использования функций right() и bottom().


Проще написать так:
painter.drawRect(rect().adjusted(0, 0, 1, 1));
Obey-Kun
Цитата
painter.drawRect(rect().adjusted(0, 0, 1, 1));

Да уж, по-индусски получилось... спасибо :).

только painter.drawRect(rect().adjusted(0, 0, -1, -1)) :)
Litkevich Yuriy
Цитата(Obey-Kun @ 28.3.2010, 0:13) *
0, 0, -1, -1
почему минус, QRect ведь возвращает укороченый размер на еденицу, в отличие от QRectF, и нужно его на единицу по каждой координате увеличить
Obey-Kun
По ходу, последняя проблема осталась. Так как создание подобных вещей мне нужно для визуализации инструментов, используемых пользователем (например, чтобы рисовать рамку, пока пользователь выделяет область), то начальная точка может лежать вне видимой зоны.
Попытался дать ему верхнюю левую точку в отрицательных координатах и получилась такая штука: http://rghost.ru/1257920/image.png

Если посмотреть внимательно, то видно, как белый прямоугольник налазит на элемент декорации моего вью (этот элемент декорации — часть системной темы, я к его созданию отношения не имею).

Вот тут также видно, что он затирает оконные декорации: http://rghost.ru/1257959/image.png (это я сделал очень большой прямоугольник).

Как тут быть?

Проблема решена, надо было банально делать m_recttest = new MyRectangle(viewport()), а не m_recttest = new MyRectangle(this)

Цитата(Litkevich Yuriy @ 27.3.2010, 21:20) *
Цитата(Obey-Kun @ 28.3.2010, 0:13) *
0, 0, -1, -1
почему минус, QRect ведь возвращает укороченый размер на еденицу, в отличие от QRectF, и нужно его на единицу по каждой координате увеличить

Рассмотрим по частям.
painter.drawRect(rect().adjusted(0, 0, -1, -1));

Не знаю, в чём дело, если честно, то лень разбираться, но работает как надо оно именно так, а не для +1 +1.

Цитата
Проблема решена, надо было банально делать m_recttest = new MyRectangle(viewport()), а не m_recttest = new MyRectangle(this)

И вообще, рисовать в самом вью было глупой идеей, разумеется надо было рисовать во viewport.
Litkevich Yuriy
Obey-Kun, для инструмента выделения, я бы всё таки посмотрел, как это самое выделение сделано в QGraphicsView. Я думаю это не должен быть отдельный виджет.
Obey-Kun
В смысле? Стандартный метод выделения — QRubberBand, это обычный виджет, унаследованный от QWidget. Имеет свой event рисования. Так что подход полностью схожий. И форма, кстати, тоже через setGeometry меняется. Или ты о чём-то другом?

Кстати, ещё один подводный камень в использовании setGeometry. Если мы ставим жирный Pen или врубаем antialiasing, то он не влазит в свою геометрию. Ну это ясно, просто нужно переопределить setGeometry и вызывать там QWidget::setGeometry с учётом толщины линии. Ну и плюс переопределить rect(), чтобы возвращать нужное значение. Как-то так. Или я неправильно мыслю?
Litkevich Yuriy
Цитата(Obey-Kun @ 28.3.2010, 1:30) *
Стандартный метод выделения — QRubberBand
Вот я посмотрел в исходник QGraphicsView, а там QRubberBand не используется.
Смотри начиная с метода:
void QGraphicsView::mousePressEvent(QMouseEvent *event)
И до:
void QGraphicsView::paintEvent(QPaintEvent *event)
Тип выделения хранится во внутренней переменной d->dragMode

В методе:
void QGraphicsView::mouseReleaseEvent(QMouseEvent *event)
Ещё посылается сообщение сцене, зачем и с каким содержимым не вникал
Obey-Kun
Странно, и правда рисуют напрямую: http://qt.gitorious.org/qt/qt/blobs/master...ew.cpp#line3441


Причём код почти в точности повторяет код рисования из QRubberBand.

Если честно, не понимаю, зачем они так сделали. Ну разве что ради скорости. Более логичным мне кажется использование отдельного объекта. Да и работать так удобнее было бы. Можно сделать вот так:
class AbstarctInstrument : public QWidget
{
public:
    AbstarctInstrument(QWidget* parent = 0);

    /**
     * Функция, которую следует вызывать, когда юзер отпустил мышку
     * после растяжения инструмента. Тут инструмент делает, то что от
     * него требуется (например, говорит сцене родителя выделить область)
     * и удаляет сам себя
     */
    virtual void workDone();
protected:
    virtual void paintEvent(QPaintEvent* );
};


а вообще, вместо workDone проще использовать деструктор :)
Мне тут использование объекта, который может хранить логику своих действий, больше нравится. Логичнее так, что ли. Да и код view не будет перегружаться лишними вещами. Он лишь будет создавать нужный инструмент при нажатии нужной кнопки мыши (а так как все инструменты унаследованы от одного класса, то можно будет хранить указатель на активный инструмент в QAbstactInstrument *m_instrument), при передвижении мыши делает setGeometry, ну а при отпускании запускает деструктор. Просто и со вкусом.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.