Thalia
Thalia

Reputation: 14635

My QGraphicsItem items are not noticed by the mouse

I am trying to move my item... The only way this works is if I iterate through all my items and check the mouse position:

class Item : public QGraphicsItem
{
    Item() { setFlag(ItemIsMovable); setFlag(ItemIsSelectable); scale = 10; }
    QRectF boundingRect() const;
    void paint(QPainter *painter);   // had to implement this because I don't know how to get the QStyleOptionGraphicsItem to call the paint below
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
               QWidget *widget) {paint(painter);}   // never called
    void update() {}
    int scale, x, y;
};

// scale is Item property set by caller, x and y also set by caller based on viewport position of item center
QRectF Item::boundingRect() const
{
    return QRectF(x - 30 - scale, y - 30 - scale,
                  30 + 3 * scale, 20 + 3 * scale);
}

void Item::paint(QPainter *painter)
{
    update();
    painter->drawRect(boundingRect());
}

class CollectionView : public QGraphicsView
{
    Q_OBJECT    
public:
    CollectionView(QWidget *parent = 0);    
    QList<Item*> *m_items;
protected:
    virtual void paintEvent(QPaintEvent * event);
    virtual void mousePressEvent(QMouseEvent * event);
    virtual void mouseReleaseEvent(QMouseEvent *event);
};

CollectionView::CollectionView(QWidget *parent)
    : QGraphicsView(parent)
{
    QGraphicsScene *s = new QGraphicsScene(this);
    setScene(s);
    setViewportUpdateMode(BoundingRectViewportUpdate);
    m_items = new QList<Item*>();
}

void CollectionView::paintEvent(QPaintEvent * /* event */)
{
    QPainter painter(this->viewport());

    for(int i = 0; i< m_items->size(); i++)
    {
        Item* item = m_items->at(i);
        //scene()->addItem(item);   // this crashes
        item->paint(&painter);
    }
}

void CollectionView::mousePressEvent(QMouseEvent* event)
{
    // if I add this and comment the rest, items don't move
    //QGraphicsView::mousePressEvent(event);

    foreach (QGraphicsItem *item, this->items(event->pos()))
    { /* never gets inside */ }
    foreach (QGraphicsItem *item, this->items(event->globalPos()))
    { /* never gets inside */ }

    // the following works though:
    for (int i = 0; i < m_items->size(); i++)
    {
        Item* currentItem = m_items->at(i);
        if(!currentItem->boundingRect().contains(event->pos()))
            continue;
        if (event->button() == Qt::LeftButton)
            { /* I can get the item index and its relative position to mouse
                 then pass this info to the mouseReleaseEvent, and it works */ }
        break;
    }
}

It would be best if I could use the QGraphicsView / QGraphicsScene / QGraphicsItem methods to move item or get the context menu... I have not figured out how.

But if I have to implement mouse actions, it would be best if I could iterate through he items found at mouse position, instead of all the items in my list (which is likely to be larger).

Why is my try not working ? (alternately, how can I make QGraphicsScene / QGraphicsView do all the work and move the item without my having to write any code ? that is the purpose of the ItemIsMovable.. right ?)

Update: added the code for paintEvent... Unfortunately I was unable to call the

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

version of the paint - it would probably be used by something like scene()->addItem(item); - but that specific call makes program crash... the above paint method is never called, but I am aware that it is the official paint method of he QGraphicsItem... Such a simple code yet such a mess.

Upvotes: 0

Views: 1753

Answers (2)

TheDarkKnight
TheDarkKnight

Reputation: 27639

As we have established in the comments, the problem is the value returned from the boundingRect( ) function.

A QGraphicsItem's bounding rect defines the local coordinates of the item. If the item is regular, matching a rectangle, you would only need to implement the boundingRect(). This is used for collision detection for, amongst other things, detecting the mouse being over the item.

If you have a non-regular (rectangular) object and want a more granular collision detection, then implement the shape() function, in addition to boundingRect(). Both are in local coordinates for the item.

Upvotes: 1

Gabrielle de Grimouard
Gabrielle de Grimouard

Reputation: 2005

Firstly, try with a simple Item and a basic GraphicsScene and View. Like that:

    class GraphicsItem : public QGraphicsItem
{
public:
    GraphicsItem(QWidget* parent) {
        setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
    }
    QRectF boundingRect() const
       {
           qreal penWidth = 1;
           return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,
                         20 + penWidth, 20 + penWidth);
       }

       void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                  QWidget *widget)
       {
           painter->drawRoundedRect(-10, -10, 20, 20, 5, 5);
       }
};

This is a basic Item with will move when you use a basic QGraphicsItem and QGraphicsScene.

    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QGraphicsScene scene;
        scene.addItem(new GraphicsItem(NULL));
        QGraphicsView view(&scene);
        view.show();

        return a.exec();
   }

If the problem in your code seem that you item doesn't move it probably your MousePressEvent.

Upvotes: 1

Related Questions