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

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


  • Moderators

    Hi @Erdem said in Connecting issus:

    in your addSquare function, you asign the doubleSpinBox and the slider to each and every new Square you create

    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)));
    }

    -> 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 :-)



  • @J.Hilk
    I thought the pointer rechtEck will connect each item separately. So it’s not doing so. Good to know.
    Thanks. Do you have an advice for that issue?


  • Lifetime Qt Champion

    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.


  • Moderators

    @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.h

    void sqaureClicked(int);
    

    This is emitted each time the mousePresseEvent is triggered.
    MySquare.h

    void 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.h

    public 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.cpp

    MainWindow::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.


  • Moderators

    @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.cpp

    void 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.h

        private:
        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.cpp

    void MainWindow::updateUI(MySquar *ptr)
    {
        ui->spinBox->setValue(ptr->get_Winkel());
        ui->spinBox_2->setValue((ptr->get_S()));
    }
    

Log in to reply