Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Connect signal from custom class to slot in UI class



  • Hi everyone,

    Right now I am in a proccess of making a some kind of builder using a Diagram Scene example as a source. Main window looks like thisScreenshot
    I want to make it so if an item in custom QGraphicsScene is selected its properties will be shown in the right widget. I have implemented a custom widget with UI Designer and place it in QDockWidget which is in MainWindow UI.
    And so I have an understanding that I must connect a signal emitted from an ObjectProperty class and connect it to my CustomWIdget slot which will update spinboxes' values.
    Here is some code: AbstractUiCell inherits QGraphicsItem and has a member of class ObjectProperty which contains geometry of the item. ObjectPropertyInfoDialog is an actual class of the custom widget which sits in QDockWidget.

    abstract_ui_cell.h

    #ifndef ABSTRACTUICELL_H
    #define ABSTRACTUICELL_H
    
    #include <QtGlobal>
    #include <QGraphicsItem>
    #include <QGraphicsSceneMouseEvent>
    #include <QCursor>
    #include <QObject>
    #include <QPaintEngine>
    #include <QStyleOptionGraphicsItem>
    #include "object_properties.h"
    #include "objectpropertyinfodialog.h"
    
    class AbstractUiCell : public QObject, public QGraphicsItem
    {
        Q_OBJECT
    public:
        AbstractUiCell();
        enum ItemType {ForePump,
                       TurboPump,
                       Valve,
                       GateValve,
                       PressureGauge,
                       VacuumChamber
                      };
        ItemType getItemType() const { return myItemType; };
        void setItemType(ItemType type);
        static AbstractUiCell* getItem(ItemType currentItemType);
        void setProperties();
    
    private:
        ItemType myItemType;
    
    signals:
        void geometryChanged(ObjectProperties*);
    
    protected:
        ObjectProperties *itemProperties;
    
        QVariant itemChange(GraphicsItemChange change, const QVariant &value);
    
    };
    
    #endif // ABSTRACTUICELL_H
    
    

    abstact_ui_cell.cpp

    #include "abstract_ui_cell.h"
    
    #include "fore_pump_ui_cell.h"
    #include "turbo_pump_ui_cell.h"
    #include "valve_ui_cell.h"
    #include "vacuum_chamber_ui_cell.h"
    #include "scene.h"
    #include "math.h"
    #include <QApplication>
    
    AbstractUiCell::AbstractUiCell()
    {
        setFlag(QGraphicsItem::ItemIsMovable, true);
        setFlag(QGraphicsItem::ItemIsSelectable, true);
        setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
    
    }
    
    void AbstractUiCell::setItemType(AbstractUiCell::ItemType type)
    {
        myItemType = type;
    }
    
    AbstractUiCell* AbstractUiCell::getItem(ItemType currentItemType)
    {
        switch (currentItemType) {
            case ForePump:
            {
                ForePumpUiCell *item = new ForePumpUiCell;
                item->setItemType(currentItemType);
                return item;
                break;
            }
            case TurboPump:
            {
                TurboPumpUiCell *item = new TurboPumpUiCell;
                item->setItemType(currentItemType);
                return item;
                break;
            }
            case Valve:
            {
                ValveUiCell *item = new ValveUiCell;
                item->setItemType(currentItemType);
                return item;
                break;
            }
            case VacuumChamber:
            {
                VacuumChamberUiCell *item = new VacuumChamberUiCell;
                item->setItemType(currentItemType);
                return item;
                break;
            }
        default:
            ForePumpUiCell *item = new ForePumpUiCell;
            item->setItemType(currentItemType);
            return item;
        }
    }
    
    void AbstractUiCell::setProperties()
    {
        itemProperties = new ObjectProperties();
        itemProperties->setGeometry(this->mapRectToScene(this->boundingRect()));
    }
    
    QVariant AbstractUiCell::itemChange(GraphicsItemChange change, const QVariant &value)
    {
        if (change == ItemPositionChange && scene()) {
            QPointF newPos = value.toPointF();
            Scene* customScene = qobject_cast<Scene*> (scene());
            QRectF rect = customScene->sceneRect();
            if(QApplication::mouseButtons() == Qt::LeftButton &&
                            qobject_cast<Scene*> (scene())){
                if (!rect.contains(newPos)) {
                    newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
                    newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
                    return newPos;
                }
                int gridSize = 10;
                qreal xV = round(newPos.x()/gridSize)*gridSize;
                qreal yV = round(newPos.y()/gridSize)*gridSize;
                return QPointF(xV, yV);
            }
        }
        return QGraphicsItem::itemChange(change, value);
    }
    

    object_properties.h

    #ifndef OBJECTPROPERTIES_H
    #define OBJECTPROPERTIES_H
    
    #include <QObject>
    #include <QRectF>
    #include <QMultiMap>
    #include <QVariant>
    
    class ObjectProperties : public QObject
    {
        Q_OBJECT
    public:
        explicit ObjectProperties(QObject *parent = nullptr);
    
        int id() const { return id_; };
        void setId(int id) { id_ = id; };
        QRectF geometry() const { return itemRect_; };
        void setGeometry(QRectF const &itemRect);
    
    signals:
        void geometryChanged(ObjectProperties*);
    
    private:
        int id_;
        QRectF itemRect_;
    };
    
    #endif // OBJECTPROPERTIES_H
    

    object_properties.cpp

    #include "object_properties.h"
    #include <QDebug>
    
    ObjectProperties::ObjectProperties(QObject *parent)
    {
        Q_UNUSED(parent);
    }
    
    void ObjectProperties::setGeometry(const QRectF &itemRect)
    {
        itemRect_ = itemRect;
        emit geometryChanged(this);
    }
    

    objectpropertyinfodialog.h

    #ifndef OBJECTPROPERTYINFODIALOG_H
    #define OBJECTPROPERTYINFODIALOG_H
    
    #include <QWidget>
    #include <QDebug>
    #include <QGraphicsView>
    #include "object_properties.h"
    #include <QLayout>
    
    namespace Ui {
    class ObjectPropertyInfoDialog;
    }
    
    class MainWindow;
    
    class ObjectPropertyInfoDialog : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit ObjectPropertyInfoDialog(QWidget *parent = nullptr);
        ~ObjectPropertyInfoDialog();
    
    public slots:
        void geometryUpdated(ObjectProperties*);
    
    private:
        Ui::ObjectPropertyInfoDialog *ui;
    };
    
    #endif // OBJECTPROPERTYINFODIALOG_H
    
    

    objectpropertyinfodialog.cpp

    #include "objectpropertyinfodialog.h"
    #include "ui_objectpropertyinfodialog.h"
    #include <QMessageBox>
    #include "fore_pump_ui_cell.h"
    #include "abstract_ui_cell.h"
    
    ObjectPropertyInfoDialog::ObjectPropertyInfoDialog(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::ObjectPropertyInfoDialog)
    {
        ui->setupUi(this);
    }
    
    ObjectPropertyInfoDialog::~ObjectPropertyInfoDialog()
    {
        delete ui;
    }
    
    void ObjectPropertyInfoDialog::geometryUpdated(ObjectProperties *object)
    {
        ui->spinBoxX->setValue(object->geometry().left());
        ui->spinBoxY->setValue(object->geometry().bottom());
        ui->spinBoxWidth->setValue(object->geometry().width());
        ui->spinBoxHeight->setValue(object->geometry().height());
    }
    
    

    scene.h

    #ifndef SCENE_H
    #define SCENE_H
    
    #include <QObject>
    #include <QGraphicsScene>
    #include <QMimeData>
    #include <QListWidget>
    #include <QDebug>
    #include "sceneitemtext.h"
    #include "push_button_ui_cell.h"
    #include "fore_pump_ui_cell.h"
    #include "scene_item.h"
    
    class Scene : public QGraphicsScene
    {
        Q_OBJECT
    public:
        explicit Scene(QObject *parent = nullptr);
        ~Scene();
    
        enum Mode { InsertItem, InsertLine, InsertText, MoveItem };
    
        QList <AbstractUiCell *> itemList;
    
    public slots:
        void setMode(Mode mode);
        void setItemType(AbstractUiCell::ItemType type);
        void paintGrid(int state);
        int getItemType() { return myItemType;   };
    
    signals:
        void itemInserted(AbstractUiCell *item);
        void textInserted(QGraphicsTextItem *item);
        void itemSelected(QGraphicsItem *item);
    
    protected:
        void mousePressEvent(QGraphicsSceneMouseEvent *event);
        void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent);
    
    private:
        QPointF startPos;
    
        bool isItemChange(int type) const;
        AbstractUiCell *chooseItem(AbstractUiCell::ItemType myItemType);
        AbstractUiCell::ItemType myItemType;
        QMenu *myItemMenu;
        Mode myMode;
        bool leftButtonDown;
        QPointF startPoint;
        QGraphicsLineItem *line;
        QFont myFont;
        SceneItemText *textItem;
    
    };
    
    #endif // SCENE_H
    
    

    scene.cpp

    #include "scene.h"
    #include <QGraphicsTextItem>
    #include "push_button_ui_cell.h"
    #include "valve_ui_cell.h"
    #include "vacuum_chamber_ui_cell.h"
    
    class ObjectPropertyInfoDialog;
    
    Scene::Scene(QObject *parent)
    : QGraphicsScene(parent)
    {
        this->setSceneRect(0, 0, 1000, 1000);
        myMode = MoveItem;
        myItemType = AbstractUiCell::ForePump;
    }
    
    Scene::~Scene()
    {
    }
    
    void Scene::paintGrid(int state)
    {
        if (state > 0)
        this->setBackgroundBrush(
                    QBrush(QPixmap(":/images/grid_cell_10x10_blue.png")));
        else
            this->setBackgroundBrush(QBrush(Qt::NoBrush));
        update();
    }
    
    void Scene::setItemType(AbstractUiCell::ItemType type)
    {
        myItemType = type;
    }
    
    void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
    {
        if (myMode == MoveItem)
            QGraphicsScene::mouseMoveEvent(mouseEvent);
    }
    
    void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event)
    {
        if (event->button() != Qt::LeftButton)
            return ;
        switch (myMode) {
            case InsertItem:
        {
                AbstractUiCell *item = AbstractUiCell::getItem(myItemType);
                addItem(item);
                item->setPos(event->scenePos());
                item->setProperties();
                emit itemInserted(item);
        }
        default:
            ;
        }
        QGraphicsScene::mousePressEvent(event);
    }
    
    void Scene::setMode(Mode mode)
    {
        myMode = mode;
    }
    

    I guess I need to connect every new AbstractUiCell's itemProperties to slot in ObjectPropertyInfoDialog in setProperties method. But how do I get a pointer to already created instance of ObjectPropertyInfoDialog? Or is there some way around it?



  • Hi :)

    @ppetukhov said in Connect signal from custom class to slot in UI class:

    And so I have an understanding that I must connect a signal emitted from an ObjectProperty class and connect it to my CustomWIdget slot which will update spinboxes' values.

    Connect your GraphicsItem AbstractUiCell with your scene by emitting a signal which sends the item's properties. After that, the scene can send the properties to your CustomWidget.

    To emit your signal in AbstractUiCell class, you could use itemChange. Just check whether there were made selection changes or not and send out the signal.
    https://doc.qt.io/qt-5/qgraphicsitem.html#itemChange

    @ppetukhov said in Connect signal from custom class to slot in UI class:

    I guess I need to connect every new AbstractUiCell's itemProperties to slot in ObjectPropertyInfoDialog in setProperties method. But how do I get a pointer to already created instance of ObjectPropertyInfoDialog?

    Sender and receiver don't need to know each other, but the class which sets up the connection must. So your Dialog does not have to know about your AbstractUiCell-Items. It just have to take the properties and display them (by using your setProperties slot)

    @ppetukhov said in Connect signal from custom class to slot in UI class:

    how do I get a pointer to already created instance of ObjectPropertyInfoDialog?

    Your MainWindow should have one.
    And your MainWindow should have a pointer to your GraphicsView / DockWidget

    Edit:

    You could also use scene->selectedItems().first() to get your item pointer (this is used in this Diagram Scene example).

    Something like this might work for you:
    (I just saw, that you use itemChange already to update geometry and position, so you can emit your signal there)

    AbstractUiCell *item = qgraphicsitem_cast<AbstractUiCell *>(selectedItems().first());
    emit sendProperties(item->itemProperties());
    


  • Hi :)

    @ppetukhov said in Connect signal from custom class to slot in UI class:

    And so I have an understanding that I must connect a signal emitted from an ObjectProperty class and connect it to my CustomWIdget slot which will update spinboxes' values.

    Connect your GraphicsItem AbstractUiCell with your scene by emitting a signal which sends the item's properties. After that, the scene can send the properties to your CustomWidget.

    To emit your signal in AbstractUiCell class, you could use itemChange. Just check whether there were made selection changes or not and send out the signal.
    https://doc.qt.io/qt-5/qgraphicsitem.html#itemChange

    @ppetukhov said in Connect signal from custom class to slot in UI class:

    I guess I need to connect every new AbstractUiCell's itemProperties to slot in ObjectPropertyInfoDialog in setProperties method. But how do I get a pointer to already created instance of ObjectPropertyInfoDialog?

    Sender and receiver don't need to know each other, but the class which sets up the connection must. So your Dialog does not have to know about your AbstractUiCell-Items. It just have to take the properties and display them (by using your setProperties slot)

    @ppetukhov said in Connect signal from custom class to slot in UI class:

    how do I get a pointer to already created instance of ObjectPropertyInfoDialog?

    Your MainWindow should have one.
    And your MainWindow should have a pointer to your GraphicsView / DockWidget

    Edit:

    You could also use scene->selectedItems().first() to get your item pointer (this is used in this Diagram Scene example).

    Something like this might work for you:
    (I just saw, that you use itemChange already to update geometry and position, so you can emit your signal there)

    AbstractUiCell *item = qgraphicsitem_cast<AbstractUiCell *>(selectedItems().first());
    emit sendProperties(item->itemProperties());
    


  • Hey, thank you so much!

    I have achieved a desired result connecting a signal geometryChanged() from AbstractUiCell to Scene slot geometryUpdated() and then emit a signal sendProperties(item->itemProperties) from that slot in a Scene.
    After that I connect that signal to my CustomWidget in MainWidnow class.


Log in to reply