crossplatform.ru

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

2 страниц V   1 2 >  
Ответить в данную темуНачать новую тему
> [Решено] Странности с QGraphicsItem::setSelected(bool selected), выделение происходит минуя эту функцию
Obey-Kun
  опции профиля:
сообщение 27.3.2010, 23:30
Сообщение #1


Студент
*

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

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




Репутация:   0  


Сделал итем Cell, унаследованный от QGraphicsItem. Он рисует прямоугольник (я знаю про QGraphicsRectItem, но мой итем сложнее и он тут не катит). Располагаю их впритык (то есть, допустим, первый от 0 до 10, второй от 10 до 20 и т.д). Так как изначально у них всех границы рисуются одним и тем же цветом и нулевой толщиной, проблем нет. Но я хочу, чтобы при выделении граница становилась синей. Сказано — сделано. Чуть изменил строчку в void Cell::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget).

painter->setPen(QPen((isSelected() ? Qt::blue : Qt::gray), 0, Qt::SolidLine));


И всплыла проблема. Позднее созданные виджеты перекрывают ранее созданные, из-за чего граница их кое-где перекрывается более «молодыми» итемами. Вот как это выглядит:



Обходится это очень просто. Надо поднимать выделенные ячейки путём смены z. По умолчанию z=0, так что можно поступить вполне элегантно — переопределить void setSelected ( bool selected ):
void Cell::setSelected(bool selected)
{
    setZValue(selected);
    QGraphicsItem::setSelected(selected);
}


Хоть решение и элегантно, но так почему-то не работает. Картина остаётся такой же. Если же поступаю дурацким способом и запихиваю setZValue(isSelected()) прямо в paint, то всё ок:



А через переопределении функции setSelected -- фиг. Почему так?

Сообщение отредактировал Obey-Kun - 28.3.2010, 1:44
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
igor_bogomolov
  опции профиля:
сообщение 28.3.2010, 0:18
Сообщение #2


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

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

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




Репутация:   29  


Obey-Kun, приложи рабочий пример, есть подозрение что у тебя отрисовка не правильная. Нижнюю правую точку необходимо сместить на 1px.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Obey-Kun
  опции профиля:
сообщение 28.3.2010, 0:29
Сообщение #3


Студент
*

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

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




Репутация:   0  


Копипастю код целиком:
Раскрывающийся текст
class Cell: public QGraphicsItem
{
public:
    Cell(const QRectF& inRect, const QColor& inColor=Qt::white);
    Cell(const Cell &inCell);

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *item, QWidget *widget);

    void setSelected ( bool selected );

    void setColor(QColor inColor) {
        m_color = inColor;
    }
    QColor color() const {
        return m_color;
    }
    QRectF rect() const {
        return m_rect;
    }
    QRectF boundingRect() const {
        return m_rect;
    }

private:
    /**
     * Прямоугольник ячейки.
     * @warning см. документацию Qt про QRect - там непонятные вещи:
     *          "The right() function returns left() + width() - 1
     *           and the bottom() function returns top() + height() - 1",
     *          поэтому использовал QRectF.
     */
    QRectF m_rect;
    QColor m_color;
};

Раскрывающийся текст
Cell::Cell(const QRectF& inRect, const QColor& inColor) : QGraphicsItem(),
    m_rect(inRect),
    m_color(inColor)
{
    setFlags(ItemIsSelectable);
   // setAcceptedMouseButtons(Qt::NoButton);
}

Cell::Cell(const Cell& inCell) : QGraphicsItem(),
    m_rect(inCell.rect()),
    m_color(inCell.color())
{ }

void Cell::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(widget);

    //TODO: учитывать размер ячейки!
    const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());

    painter->setRenderHint(QPainter::Antialiasing, false);

    setZValue(isSelected()); //FIXME?: странное решение, казалось бы, можно
                             //запихнуть это в переопределённый setSelected, но
                             //вроде как оно вызывается не при любом выделении

    painter->setPen(QPen((isSelected() ? Qt::blue : Qt::gray),
                         0,
                         ((lod > 0.007 || isSelected()) ? Qt::SolidLine : Qt::NoPen)));

    painter->setBrush(QBrush(m_color, Qt::SolidPattern));
    painter->drawRect(m_rect);

    //рисуем информацию по прямоугольнике при сильном приближении
    if (lod > 1) {
        painter->setRenderHint(QPainter::TextAntialiasing, false);
        QString info;
        info = "Top left point: " + QString::number(m_rect.x()) + ", " + QString::number(m_rect.y()) + "\n" +
        "Bottom right point: " + QString::number(m_rect.bottomRight().x()) + ", " + QString::number(m_rect.bottomRight().y()) + "\n" +
        "Width: " + QString::number(m_rect.width()) + ", " + "Height: " + QString::number(m_rect.height());
        painter->drawText(boundingRect(), Qt::AlignCenter, info );
    }
}

void qfgui::Cell::setSelected(bool selected) //лень тереть функцию, может ещё пригодится
{
  //  setZValue(selected);
    QGraphicsItem::setSelected(selected);
}
Для удобства создаю в сцене (она у меня унаследована от QGraphicsScene) так:
Раскрывающийся текст
void MyScene::addCell(const QRectF &inRect, const QColor &inColor)
{
    Cell *item = new Cell(inRect, inColor);
    addItem(item);
}
В mainwindow соединяю сцену со view и делаю так:
Раскрывающийся текст
void MainWindow::drawRectangles()
{
    QColor mycolor;
    for(int i = 0; i < 100; ++i) {
        for(int j = 0; j < 100; ++j) {
            if(j < 33) {
                mycolor = Qt::yellow;
            } else if ( j < 66 ) {
                mycolor = Qt::darkGreen;
            } else {
                mycolor = Qt::cyan;
            }
            QRect myrect(400 * i + 40, 300 * j + 30, 400, 300);
            scene->addCell(myrect, mycolor);
        }
    }
}


Стоит отметить, что когда я создавал QGraphicsRectItem, он так же себя вёл (в смысле, перекрывался соседями, если они идут друг за другом). Так что вряд ли дело в одном пикселе. Логичное предположение — я так понимаю, вы подумали, что я использую QRect, но я его не использую.

Сообщение отредактировал Obey-Kun - 28.3.2010, 0:41
Причина редактирования: Пользуйтесь тэтом [ expand ]
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
igor_bogomolov
  опции профиля:
сообщение 28.3.2010, 1:27
Сообщение #4


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

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

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




Репутация:   29  


Цитата(Obey-Kun @ 27.3.2010, 23:30) *
А через переопределении функции setSelected -- фиг. Почему так?
void QGraphicsItem::setSelected ( bool selected ) - не виртуальная функция, ты не можешь её переопределить.
Лучшего варианта, что ты уже сделал, я пока не нашёл.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Obey-Kun
  опции профиля:
сообщение 28.3.2010, 1:31
Сообщение #5


Студент
*

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

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




Репутация:   0  


Цитата(igor_bogomolov @ 28.3.2010, 1:27) *
Цитата(Obey-Kun @ 27.3.2010, 23:30) *
А через переопределении функции setSelected -- фиг. Почему так?
void QGraphicsItem::setSelected ( bool selected ) - не виртуальная функция, ты не можешь её переопределить.


Шутите что ли? (При публичном наследовании) переопределять можно любые public и protected функции родительских классов :). Иначе вся структура C++ порушится :).
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
igor_bogomolov
  опции профиля:
сообщение 28.3.2010, 1:38
Сообщение #6


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

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

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




Репутация:   29  


Цитата(Obey-Kun @ 28.3.2010, 1:31) *
Шутите что ли? (При публичном наследовании) переопределять можно любые public и protected функции родительских классов :). Иначе вся структура C++ порушится :).
Срочно учить матчасть
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 28.3.2010, 1:39
Сообщение #7


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9669
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

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




Репутация:   94  


Цитата(Obey-Kun @ 28.3.2010, 5:31) *
переопределять можно любые public и protected функции родительских классов
терминологически ты ошибся. Переопределяются только виртуальные функции. Не виртуальные перегружаются.
Виртуальные хороши тем, что кто-то может вызвать по указателю на базовый класс функцию конкретного наследника. Скорее всего так и происходит. Т.е. функция не виртуальная, поэтому вызывается функция базового класса, а не наследника
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Obey-Kun
  опции профиля:
сообщение 28.3.2010, 1:43
Сообщение #8


Студент
*

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

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




Репутация:   0  


Цитата(Litkevich Yuriy @ 28.3.2010, 1:39) *
Цитата(Obey-Kun @ 28.3.2010, 5:31) *
переопределять можно любые public и protected функции родительских классов
терминологически ты ошибся. Переопределяются только виртуальные функции. Не виртуальные перегружаются.
Виртуальные хороши тем, что кто-то может вызвать по указателю на базовый класс функцию конкретного наследника. Скорее всего так и происходит. Т.е. функция не виртуальная, поэтому вызывается функция базового класса, а не наследника

Ах вот оно как! Спасибо, хорошую такую дырку вы залатали в моих знаниях.

Да, тогда действительно другого метода я не вижу. Нехорошо получается...
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Obey-Kun
  опции профиля:
сообщение 28.3.2010, 5:21
Сообщение #9


Студент
*

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

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




Репутация:   0  


Вот мне кстати интересно, что быстрее:
    if (zValue() != isSelected()) {
        setZValue(isSelected());
    }

или просто
    setZValue(isSelected());


Пока сделал первый способ.

И между делом, был замечен ещё один подводный камень. Выделение сценой моего итема (Cell) в итоге приводит к такой последовательности:
1. итем помечается отмеченным
2. айтем перерисовывается, во время перерисовки меняется его z
3. сцена реагирует на изменение z и перерисовывает айтем ещё раз

В итоге, если выделяется сразу много моих айтемов, то заметно, как сначала окрашивается "не перекрытая" часть границ, а потом вся остальная.
Мне такое поведение не нравится. Да и операций приходится больше совершать.

Думается мне, надо в сцене переопределить setSelectionArea и setSelectionRect, там определять, какие айтема попадают в выделение и вызывать для них setZ(1). Ну а в clearSelection вместе со снятием со всех выделения опускать их слоем ниже.

При этом код setZValue(isSelected()) из paint у Cell нельзя убирать, так как ещё остаётся setSelected(bool), который может действовать минуя сцену.

Как-то так. Но я, наверное, не буду это делать. Может, оно и станет чуть красивее, но ведь медленно всё это... Хотя чёрт знает, может и не медленно. Надо посмотреть.
Сцена использует кеш? Ведь если я сделаю, как хочу, то поиск айтемов в одной и той же области будет проводиться дважды.

Сообщение отредактировал Obey-Kun - 28.3.2010, 5:24
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 28.3.2010, 13:50
Сообщение #10


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9669
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

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




Репутация:   94  


Цитата(Obey-Kun @ 28.3.2010, 9:21) *
айтем перерисовывается, во время перерисовки меняется его z
это ты сам так делаешь?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V   1 2 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


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


RSS Рейтинг@Mail.ru Текстовая версия Сейчас: 19.2.2025, 2:19