How to manipulate items in a scene from outside a graphicsview?



  • Dear all,

    I'm having a hard time finding out how to manipulate items in a graphics scene from outside a graphics view.

    Below is a picture of what I would like to achieve.

    !http://i1372.photobucket.com/albums/ag352/AimedSquid/Screenshotfrom2014-09-02162654_zps32a0f097.png(dialog)!

    The scene shows an item. When I click the item I want the color of the item to be selected in the list widget. Then, by clicking another item in the list widget, let's say 'Red', I want the item in the scene change to red.

    I created the scene using the following code which I got from the tutorials at YouTube (https://www.youtube.com/watch?v=hgDd2QspuDg&index=82&list=PL2D1942A4688E9D63).

    Here's the code (without the code to manipulate the item because I'm rather clueless on how to achieve that):

    dialog.h
    @ifndef DIALOG_H
    #define DIALOG_H

    #include <QDialog>
    #include <QtCore>
    #include <QtGui>
    #include "mysquare.h"

    namespace Ui {
    class Dialog;
    }

    class Dialog : public QDialog
    {
    Q_OBJECT

    public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

    private:
    Ui::Dialog *ui;
    QGraphicsScene *scene;
    MySquare *square;
    MySquare *square2;
    };

    #endif // DIALOG_H@

    mysquare.h
    @#ifndef MYSQUARE_H
    #define MYSQUARE_H
    #include <QPainter>
    #include <QGraphicsItem>
    #include <QDebug>

    class MySquare : public QGraphicsItem
    {
    public:
    MySquare();

    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    bool Pressed;
    

    protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

    };

    #endif // MYSQUARE_H@

    dialog.cpp
    @#include "dialog.h"
    #include "ui_dialog.h"

    Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
    {
    ui->setupUi(this);

    scene = new QGraphicsScene(this);
    ui->graphicsView->setScene(scene);
    
    square = new MySquare();
    scene->addItem(square);
    square2= new MySquare();
    scene->addItem(square2);
    

    }

    Dialog::~Dialog()
    {
    delete ui;
    }
    @

    main.cpp
    @#include "dialog.h"
    #include <QApplication>

    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    Dialog w;
    w.show();

    return a.exec&#40;&#41;;
    

    }
    @

    mysquare.cpp
    @#include "mysquare.h"

    MySquare::MySquare()
    {
    Pressed = false;
    setFlag(ItemIsMovable);
    }

    QRectF MySquare::boundingRect() const
    {
    return QRectF(0,0,100,100);
    }

    void MySquare::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
    QRectF rec = boundingRect();
    QBrush brush(Qt::blue);

    if(Pressed)
    {
        brush.setColor(Qt::red);
    }
    else
    {
        brush.setColor(Qt::green);
    }
    
    painter->fillRect(rec,brush);
    painter->drawRect(rec);
    

    }

    void MySquare::mousePressEvent(QGraphicsSceneMouseEvent *event)
    {
    Pressed = true;
    update();
    QGraphicsItem::mousePressEvent(event);
    }

    void MySquare::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
    {
    Pressed = false;
    update();
    QGraphicsItem::mouseReleaseEvent(event);
    }
    @

    Can someone point me in the right direction on how I can achieve this? I'd also like to add other properties to the items in the scene such as a name and have the name of the selected item displayed in a text label outside the graphics view.

    Thanks!



  • You can make the item selectable by using
    @setFlag(QGraphicsItem::ItemIsSelectable);@
    Now the user can select it by clicking on it.

    QGraphicsScene notifies you about changes to the selection via the selectionChanged() signal. Connect a slot to that signal, get the selectedItems(), and update whatever data you show about the item outside the scene.

    To assign data to items and make it accessible from the outside, you could (just one idea):

    • Let all your items derive from QGraphicsObject
    • Add Q_PROPERTIES to your object, e.g. "FillColor", together with matching getters and setters.

    Now you can

    Get a selected item from the scene's selectedItems()

    Use toGraphicsObject() (check for null)

    On the QGraphicsObject* you can now use property("FillColor") to access the fill color, and setProperty("FillColor", newValue) to set it.



  • Asperamanca, thanks for your speedy and clear reply. I'll try to implement both suggestions.



  • Created a simpler working example but am stuck again. Here's what I've done:

    • Made the item selectable
    • Connected a slot to the selectionChanged signal

    dialog.h

    @#ifndef DIALOG_H
    #define DIALOG_H

    #include <QDialog>
    #include <QtCore>
    #include <QtGui>
    #include <QPainter>
    #include <QGraphicsItem>

    namespace Ui {
    class Dialog;
    }

    class Dialog : public QDialog
    {
    Q_OBJECT

    public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

    private slots:
    void selectionChanged();

    private:
    Ui::Dialog *ui;
    QGraphicsScene *scene;
    QGraphicsRectItem rect1;
    QGraphicsRectItem rect2;
    };

    #endif // DIALOG_H@

    @#include "dialog.h"
    #include "ui_dialog.h"

    Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
    {
    ui->setupUi(this);

    scene = new QGraphicsScene(this);
    ui->graphicsView->setScene(scene);
    connect(scene, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
    // add two objects to the scene
    QGraphicsRectItem* rect1 = new QGraphicsRectItem(0,0,100,100);
    rect1->setFlag(QGraphicsItem::ItemIsSelectable);
    scene->addItem(rect1);
    QGraphicsRectItem* rect2 = new QGraphicsRectItem(50,50,100,100);
    rect2->setFlag(QGraphicsItem::ItemIsSelectable);
    scene->addItem(rect2);
    

    }

    Dialog::~Dialog()
    {
    delete ui;
    }

    void Dialog::selectionChanged()
    {
    qDebug() << "Selection changed";
    QRectF rec;
    QBrush brush(Qt::black);
    QPainter painter;

    foreach (QGraphicsItem *item, scene->selectedItems()) {
        qDebug() << "Item selected";
        item->setOpacity(.5);
    }
    

    }@

    So now I can go through the items but cannot find out how to change properties like the fill colour. Changing the opacity works but google doesn't get me any further.

    My (seemingly incorrect) assumption is that I would create a brush and apply the brush to the selected item. There is a setBrush function but I am unable to use that.

    Changing:

    item->setOpacity(.5);

    to:

    item->setBrush( Qt::gray );

    throws an error: 'class QGraphicsItem' has no member named 'setBrush'

    I assume this means that QGraphicsItems don't have a function 'setBrush' which is confusing me.

    Can someone tell me how to get the fill colour and how to change it?

    Thanks!



  • You can add a QColor as member of the items and then

    @
    foreach (QGraphicsItem *item, scene->selectedItems()) {

        qDebug() << "Item selected";
    
        item->setColor(yourColor);
        item->update(); // this will call the paint method of your item
    

    @



  • Thanks khryleption,

    As a Qt newbie I'm guessing that I would then have to create my own QGraphicsItem class?

    After some more digging I found the following page:

    http://www.walletfox.com/course/qundocommandexample.php

    Looking at the code on this page I changed:

    @foreach (QGraphicsItem *item, scene->selectedItems()) {
    qDebug() << "Item selected";
    item->setOpacity(.5);
    }@

    to:

    @foreach (QGraphicsItem item, scene->selectedItems()) {
    qDebug() << "Item selected";
    item->setOpacity(.5);
    QAbstractGraphicsShapeItem
    aItem = dynamic_cast<QAbstractGraphicsShapeItem*> (item);
    aItem->setBrush(Qt::blue);
    }@

    The QGraphicsItem then changes to red (or whatever color I would choose) after clicking it. Not sure whether this is the proper way to do it.



  • I achieved my first ambition: A scene showing graphic items. Clicking a graphic item selects its colour in the list widget. Clicking another colour (item) in the list widget changes the colour of the graphic item.

    Below is my code (probably not very elegant) but for (hopefully) informative purposes. I will now try to derive the graphic items from QGraphicsObject so that I can add custom properties to the graphic items.

    dialog.h

    @#ifndef DIALOG_H
    #define DIALOG_H

    #include <QDialog>
    #include <QtCore>
    #include <QtGui>
    #include <QPainter>
    #include <QGraphicsItem>
    #include <QListWidgetItem>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QGraphicsRectItem>

    namespace Ui {
    class Dialog;
    }

    class Dialog : public QDialog
    {
    Q_OBJECT

    public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

    private slots:
    void selectionChanged();

    void on_listWidget_itemClicked(QListWidgetItem *item);
    

    private:
    Ui::Dialog *ui;
    QGraphicsScene *scene;
    QGraphicsRectItem rect1;
    QGraphicsRectItem rect2;
    QListWidgetItem *itmr;
    QListWidgetItem *itmg;
    QListWidgetItem *itmb;
    };

    #endif // DIALOG_H@

    dialog.cpp

    @#include "dialog.h"
    #include "ui_dialog.h"

    Dialog::Dialog(QWidget parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
    {
    ui->setupUi(this);
    // create a scene
    scene = new QGraphicsScene(this);
    // set the scene
    ui->graphicsView->setScene(scene);
    // get notified when the selection in the scene has changed
    connect(scene, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
    // add two rectangle objects to the scene
    QGraphicsRectItem
    rect1 = new QGraphicsRectItem(0,0,100,100);
    rect1->setBrush(Qt::red);
    rect1->setFlag(QGraphicsItem::ItemIsSelectable);
    scene->addItem(rect1);
    QGraphicsRectItem* rect2 = new QGraphicsRectItem(50,50,100,100);
    rect2->setBrush(Qt::blue);
    rect2->setFlag(QGraphicsItem::ItemIsSelectable);
    scene->addItem(rect2);
    // create list items and add them to the list item
    itmr = new QListWidgetItem;
    itmr->setText("Red");
    ui->listWidget->addItem(itmr);
    itmg = new QListWidgetItem;
    itmg->setText("Green");
    ui->listWidget->addItem(itmg);
    itmb = new QListWidgetItem;
    itmb->setText("Blue");
    ui->listWidget->addItem(itmb);
    }

    Dialog::~Dialog()
    {
    delete ui;
    }

    void Dialog::selectionChanged()
    {
    qDebug() << "Selection changed";
    QRectF rec;
    QBrush brush(Qt::black);
    QPainter painter;
    // go through the selected items and set the list view accordingly
    foreach (QGraphicsItem item, scene->selectedItems()) {
    qDebug() << "Item selected";
    QAbstractGraphicsShapeItem
    aItem = dynamic_cast<QAbstractGraphicsShapeItem*> (item);
    QColor clr = aItem->brush().color();
    qDebug() << clr.rgb();

        if (clr.rgb() == 4294901760) {
            qDebug() << "it's red";
            ui->listWidget->item(0)->setSelected(true);
            }
        else if (clr.rgb() == 4278255360) {
                qDebug() << "it's green";
                ui->listWidget->item(1)->setSelected(true);
            }
        else if (clr.rgb() == 4278190335) {
                qDebug() << "it's blue";
                ui->listWidget->item(2)->setSelected(true);
        }
    }
    

    }

    void Dialog::on_listWidget_itemClicked(QListWidgetItem item)
    {
    QColor rgbclr;
    qDebug() << item->text();
    if (item->text() == "Red") {
    rgbclr = Qt::red;
    }
    else if (item->text() == "Green") {
    rgbclr = Qt::green;
    }
    else if (item->text() == "Blue") {
    rgbclr = Qt::blue;
    }
    foreach (QGraphicsItem item, scene->selectedItems()) {
    QAbstractGraphicsShapeItem
    aItem = dynamic_cast<QAbstractGraphicsShapeItem
    > (item);
    aItem->setBrush(rgbclr);
    qDebug() << "squiggle";
    }
    }
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.