Connecting issus
-
Hi there,
I have written a little script where I can add my own custom item MySquare onscreen. I also included a QDoubleSpinBox and a QSlider to change the size and the angle of the item. If I create only one item everything is fine. I can change size and angle but once I create more than one by clicking several times and try to change the size of the angle this properties of all created items change.
I already tried to implement I method to set Focus on the selected but it doesn’t work and I don’t know what else to do. Someone here who can help?#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QVector> class QGraphicsScene; class MySquar; namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); // Zum anlegen/löschen der dynamisch angelegten Zeiger von MySquar void ptrDelete(); void ptrInsert(MySquar *ptr); public slots: void addSquare(); private: int ptrCounter = 0; Ui::MainWindow *ui; QGraphicsScene *scene; MySquar *rechtEck; // Zeiger auf das Akutelle Rechteck QVector<MySquar*> *ptrVector; }; #endif // MAINWINDOW_H
#include "mainwindow.h" #include "ui_mainwindow.h" #include "mysquar.h" #include <QGraphicsScene> #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), scene(new QGraphicsScene(this)), ptrVector(new QVector<MySquar*>) { ptrVector = new QVector<MySquar *>(); ui->setupUi(this); ui->graphicsView->setScene(scene); ui->horizontalSlider->setMaximum(360); ui->doubleSpinBox->setMaximum(300); ui->spinBox->setMaximum(360); connect(ui->addSquare, SIGNAL(clicked()), this, SLOT(addSquare())); connect(ui->spinBox, SIGNAL(valueChanged(int)), ui->horizontalSlider, SLOT(setValue(int))); connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), ui->spinBox, SLOT(setValue(int))); } MainWindow::~MainWindow() { delete ui; ptrDelete(); delete ptrVector; } // private Methods void MainWindow::ptrDelete() { while(!ptrVector->isEmpty() && ptrCounter >= 0) { rechtEck = ptrVector->value(ptrCounter); delete rechtEck; rechtEck = nullptr; ptrVector->removeLast(); ptrCounter--; } } void MainWindow::ptrInsert(MySquar *ptr) { ptrVector->append(ptr); ptrCounter++; } // public Slots void MainWindow::addSquare() { MySquar *rechtEck = new MySquar(); rechtEck->set_Index(ptrCounter); qDebug() << rechtEck; ptrInsert(rechtEck); scene->addItem(rechtEck); connect(ui->doubleSpinBox, SIGNAL(valueChanged(double)), rechtEck, SLOT(set_S(double))); connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), rechtEck, SLOT(setWinkel(int))); }
#ifndef MYSQUAR_H #define MYSQUAR_H #include <QGraphicsObject> class MySquar : public QGraphicsObject { Q_OBJECT public: MySquar(QGraphicsItem *parent = nullptr); ~MySquar(); QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *); double get_S(); int get_Index(); void set_Index(int); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); public slots: void set_S(double); void setWinkel(int); private: double _s = 50; // default 50, _s size int _index; QColor _farbe = Qt::red; bool _pressed; }; #endif // MYSQUAR_H
#include "mysquar.h" #include <QPainter> MySquar::MySquar(QGraphicsItem *parent) : QGraphicsObject (parent) { this->_pressed = false; setFlags(ItemIsMovable | ItemIsSelectable | ItemIsFocusable); } MySquar::~MySquar() { } // public Methods QRectF MySquar::boundingRect() const { return QRectF(0, 0, this->_s, this->_s); } void MySquar::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { if(this->_pressed) { this->_farbe = Qt::black; update(); } else { this->_farbe = Qt::red; } painter->fillRect(boundingRect(),_farbe); painter->setPen(Qt::green); painter->drawRect(boundingRect()); } double MySquar::get_S() { return this->_s; } int MySquar::get_Index() { return this->_index; } void MySquar::set_Index(int i) { this->_index = i; } void MySquar::mousePressEvent(QGraphicsSceneMouseEvent *event) { this->_pressed = true; update(); QGraphicsItem::mousePressEvent(event); } void MySquar::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { this->_pressed = false; update(); QGraphicsItem::mouseReleaseEvent(event); } // public Slots void MySquar::setWinkel(int winkel) { setTransformOriginPoint(_s/2,_s/2); setRotation(winkel); } void MySquar::set_S(double size) { prepareGeometryChange(); if(this->_s != size) { this->_s = size; } else { return; } }
here is my code.
I have no idea how to seperate the items from each other -
Hi @Erdem said in Connecting issus:
in your
addSquare
function, you asign the doubleSpinBox and the slider to each and every new Square you createvoid MainWindow::addSquare()
{
MySquar *rechtEck = new MySquar();
rechtEck->set_Index(ptrCounter);
qDebug() << rechtEck;
ptrInsert(rechtEck);
scene->addItem(rechtEck);
connect(ui->doubleSpinBox, SIGNAL(valueChanged(double)),
rechtEck, SLOT(set_S(double)));
connect(ui->horizontalSlider, SIGNAL(valueChanged(int)),
rechtEck, SLOT(setWinkel(int)));
}-> all Squares will change. You'll have to remove that connects and handle that yourself.
Either have a classMemberPointer that keeps track of the latest square -> conect the spinbox and slider only to that pointer or you'lle have to come up with something different :-) -
Hi
The issue is that you connect all to same
doubleSpinBox so when its triggered it will send same
signal to all connected.It might work to do
void MySquar::setWinkel(int winkel) { if ( ! hasFocus() ) return; setTransformOriginPoint(_s/2,_s/2); setRotation(winkel); }
But that is not pretty and might have side effects if grouped
or other use cases calling setWinkel. -
@Erdem
well you already have a member variable rechtEck - this one has the same name as the temporary ones you create in addSquare you should change that as it is confusing.inside the constructor you can add 2 signals to either 2 slots or 2 lambdas:
{ ptrVector = new QVector<MySquar *>(); ui->setupUi(this); ui->graphicsView->setScene(scene); ui->horizontalSlider->setMaximum(360); ui->doubleSpinBox->setMaximum(300); ui->spinBox->setMaximum(360); connect(ui->addSquare, SIGNAL(clicked()), this, SLOT(addSquare())); connect(ui->spinBox, SIGNAL(valueChanged(int)), ui->horizontalSlider, SLOT(setValue(int))); connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), ui->spinBox, SLOT(setValue(int))); //here connect(ui->doubleSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), [rechtEck](double value)->void { if(rechtEck)//Not a nullptr -> valid pointer to a Square rechtEck->set_S(value); }); connect(ui->horizontalSlider, &QSlider::valueChanged, [rechtEck](int value)->void { if(rechtEck)//Not a nullptr -> valid pointer to a Square rechtEck->setWinkel(value); }); }
and than when you add a Square you than asign the newly created squarepointer to your member variable/pointer:
void MainWindow::addSquare() { MySquar *rechtEck = new MySquar(); rechtEck->set_Index(ptrCounter); qDebug() << rechtEck; ptrInsert(rechtEck); scene->addItem(rechtEck); this->rechtEck = rechtEck; }
should work, but it's untested. Brain compiled only ;-)
Edit: fixed some obvious typos.
-
If I understood correct this would establish a connection between the new created item and the UI. Instead I would like to establish a connection only with the selected item and disconnect the other items.
So I thought about the idea of implementing a signal in the MySquare class: “squareClicked(int)"
MySquare.hvoid sqaureClicked(int);
This is emitted each time the mousePresseEvent is triggered.
MySquare.hvoid MySquar::mousePressEvent(QGraphicsSceneMouseEvent *event) { _pressed = true; update(); QGraphicsItem::mousePressEvent(event); emit squareClicked(_index); }
In the MainWindow class I implement a slot which should establish the connection between the selected item and the UI-elements
MainWindow.hpublic slots: void establishConnection(int);
MainWindow.cpp
void MainWindow::establishConnection(int index) { rechtEck = ptrVector->value(index); connect(ui->doubleSpinBox, SIGNAL(valueChanged(double)), rechtEck, SLOT(set_S(double))); }
The ptrVector contains the pointers to each created item so with the int index I can grant exes to each items pointer
Within the constructor of the MainWindow() I now connect the sqaureClicked signal with the establishConnection instruction of the MainWindow class
MainWindow.cppMainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), scene(new QGraphicsScene(this)), ptrVector(new QVector<MySquar*>) { [...] //here connect(rechtEck, SIGNAL(sqaureClicked(int)), this, SLOT(establishConnection(int))); }
For some reasons the programm crusches and i don't know why.
-
@Erdem
with this approach, you'll end up again, with multiple squares doing stuff simultaniously.let me suggest something different.
Instead of on int, let the signal carry the instance pointer://to void squareClicked(MySquar *); void MySquar::mousePressEvent(QGraphicsSceneMouseEvent *event) { _pressed = true; update(); QGraphicsItem::mousePressEvent(event); emit squareClicked(this); }
than you can simply reasign the pointer
void MainWindow::addSquare() { MySquar *rechtEck = new MySquar(); rechtEck->set_Index(ptrCounter); qDebug() << rechtEck; ptrInsert(rechtEck); scene->addItem(rechtEck); connect(rechtEck, &MySquar::squareClicked, this, [this->rechtEck](MySquar *pointer)->void{this->rechtEck = pointer;}); }
-
I have finally achieved what I was looking for. Therefor I have implemented the method determinSelectedItem() which uses the two methods of QGraphicsScene. With QGraphicsScene::Items() you got a list with all the items presented on the screen. With QGraphicsScene::selectedItems() you got the pointer to the currently selected item. Now you just have to find the index of the selected item within the returned QList provided by QGraphicsScene::Items().
MainWindow.cppvoid MainWindow::determinSelectedItem() { QGraphicsItem *currentlySelectedItem; if(!scene->selectedItems().isEmpty()) { currentlySelectedItem = scene->selectedItems().first(); indexOfSelectedItem =scene->items(Qt::AscendingOrder).indexOf(currentlySelectedItem); } else { qDebug() << "nothing Selected!"; scene->clearSelection(); } }
Furthermore I have implemented the two variables which will contain the indexes of the currently selected item and the index of the previously selected item
MainWindow.hprivate: int indexOfSelectedItem = 0; int indexOfPreviouslySelectedItem;
Finally I have implemented a the slot establishConnection(). This slot is invoked whenever the signal QGraphicsScene::selectionChanged() is emited.
void MainWindow::establishConnection() { determinSelectedItem(); switch(establishmentTriggert) { case 0: determinSelectedItem(); rechtEck = ptrVector->value(indexOfSelectedItem); connect(ui->spinBox_2, SIGNAL(valueChanged(int)), rechtEck, SLOT(set_S(int))); connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), rechtEck, SLOT(setWinkel(int))); updateUI(rechtEck); indexOfPreviouslySelectedItem = indexOfSelectedItem; establishmentTriggert++; break; default: rechtEck = ptrVector->value(indexOfPreviouslySelectedItem); disconnect(ui->spinBox_2, SIGNAL(valueChanged(int)), rechtEck, SLOT(set_S(int))); disconnect(ui->horizontalSlider, SIGNAL(valueChanged(int)), rechtEck, SLOT(setWinkel(int))); determinSelectedItem(); rechtEck = ptrVector->value(indexOfSelectedItem); connect(ui->spinBox_2, SIGNAL(valueChanged(int)), rechtEck, SLOT(set_S(int))); connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), rechtEck, SLOT(setWinkel(int))); updateUI(rechtEck); indexOfPreviouslySelectedItem = indexOfSelectedItem; break; } }
Due to the fact that my prtVector contains the pointer of MySquare in the same order like QGrpahicsScene::items() the index is of the selected MySquare is determined so that I can set the connect instruction with the selected item and disconnect the previously selected MySquare item.
establishmentTriggert is a variable to check weather the selected item is the very first one or if there has been a selection before so that there is a index to a MySquare item that needs to be disconnected.
updateUI() is a public method which sets the spinbox values the the porpertyvalues of the selected item.
MainWindow.cppvoid MainWindow::updateUI(MySquar *ptr) { ui->spinBox->setValue(ptr->get_Winkel()); ui->spinBox_2->setValue((ptr->get_S())); }