Solved 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 this
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 yourCustomWidget
.To emit your signal in
AbstractUiCell
class, you could useitemChange
. 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 yoursetProperties
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 yourMainWindow
should have a pointer to yourGraphicsView
/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 useitemChange
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 yourCustomWidget
.To emit your signal in
AbstractUiCell
class, you could useitemChange
. 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 yoursetProperties
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 yourMainWindow
should have a pointer to yourGraphicsView
/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 useitemChange
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.