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

Dynamic QWidget for QStackedWidget using ID



  • I have an application with a mainwindow and a stackedwidget.
    I like to create a widget that displays all the information about a formula 1 circuit, but it needs to be dynamic. I don't want to create a widget for each circuit, but I want it to retreive the data from the database using its ID.
    I promoted the first index of the stacked widget to Circuit and already tried this:

    Circuit.h:

        explicit Circuit(int ID,QWidget *parent = nullptr);
    

    Circuit.cpp:

    Circuit::Circuit(int ID, QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Circuit)
    {
        ui->setupUi(this);
    }
    

    but it gives me this error:

    no matching function for call to 'Circuit::Circuit()'
    no matching constructor for initialization of 'Circuit'
    
            page = new Circuit();
            page->setObjectName(QString::fromUtf8("page"));
            stackedWidget->addWidget(page);
    

    Is there anyone who knows what the proper way to do this is?


  • Lifetime Qt Champion

    @hobbyProgrammer said in Dynamic QWidget for QStackedWidget using ID:

    page = new Circuit();

    Hi
    You have to give it the int in the ctor
    page = new Circuit(666);
    ( ps. do you store it in the Circuit class, the code dont show that at all )



  • @mrjj Hi, I don't store it yet. I'd like to create a good setup before starting into the actual coding.

    From the MainWindow how do I call this?

        QSqlQuery query(db);
        query.prepare("SELECT DISTINCT(name) FROM circuit WHERE circuit_id = :circuitID");
        query.bindValue(":circuitID", circuitID);
        query.exec();
    
        QSqlQueryModel *model = new QSqlQueryModel();
        model->setQuery(query);
    
        for(int i = 0; i < model->rowCount(); i++)
        {
            QString strCircuitName = model->data(model->index(i,0)).toString();
            const char* charCircuitName = strCircuitName.toStdString().c_str();
            QAction *circuitAct = new QAction(tr(charCircuitName));
            ui->menuCircuits->addAction(circuitAct);
            connect(circuitAct, &QAction::triggered, this, &MainWindow::goToCircuit);
        }
    
    void MainWindow::goToCircuit()
    {
        qDebug() << "circuitID: " << circuitID;
        Circuit *circuit = new Circuit(circuitID);
        ui->stackedWidget->setCurrentWidget(circuit);
    }
    

    When I start the app it goes straight to the page with index 0 (because of the code below, that was causing the error previously).
    Whenever I press either Circuit 1 or Circuit 2, it doesn't seem to do anything. I suspect that my connect isn't the proper way to do this, but how should I do this?

    page = new Circuit(0);
    page->setObjectName(QString::fromUtf8("page"));
    stackedWidget->addWidget(page);
    


  • @hobbyProgrammer
    I'm sorry I don't have time to answer the signal/slot/connect part, you'll perhaps want to use a C++ lambda for that, there are various ways but you need your slot to gain access to which circuit ID the action is for, in some shape or form.

    But I couldn't help noticing:

            QString strCircuitName = model->data(model->index(i,0)).toString();
            const char* charCircuitName = strCircuitName.toStdString().c_str();
            QAction *circuitAct = new QAction(tr(charCircuitName));
    

    You don't want to do it this way. At minimum from docs use:

    Similarly, you can pass a QString to a function that takes a const char * argument using the qPrintable() macro which returns the given QString as a const char *. This is equivalent to calling <QString>.toLocal8Bit().constData().

    Separately

        Circuit *circuit = new Circuit(circuitID);
        ui->stackedWidget->setCurrentWidget(circuit);
    

    https://doc.qt.io/qt-5/qstackedwidget.html#setCurrentWidget

    Sets the current widget to be the specified widget. The new current widget must already be contained in this stacked widget.

    You're going to need a https://doc.qt.io/qt-5/qstackedwidget.html#addWidget.



  • @JonB Hi,
    thanks for your help.
    I really need to know how to load these Circuits in the menu dynamically tho. Is there anyone else who might know how to help me?

    I thought it could be done using QSignalMap, but I can't seem to find the solution anywhere



  • This post is deleted!


  • Allright, I got it working. It might not be the most beautiful solution so any feedback on how to improve the code is always welcome, but this is what I got so far:

    void MainWindow::fillCircuitMenu()
    {
        QSqlQuery query(db);
        query.prepare("SELECT DISTINCT(name), id FROM circuit WHERE circuit_id = :circuitID");
        query.bindValue(":circuitID", circuitID);
        query.exec();
    
        QSqlQueryModel *model = new QSqlQueryModel();
        model->setQuery(query);
    
        for(int i = 0; i < model->rowCount(); i++)
        {
            QString strCircuitName = model->data(model->index(i,0)).toString();
            const char* charCircuitName = strCircuitName.toStdString().c_str();
    
            QAction *circuitAct = new QAction(tr(charCircuitName));
            this->circuitID = model->data(model->index(i,1)).toInt();
            ui->menuCircuits->addAction(circuitAct);
    
            Circuit *circuit = new Circuit(circuitID);
            qvCircuitMenu.insert(i, circuit);
    
            QSignalMapper *signalMapper = new QSignalMapper(this);
            connect(circuitAct, SIGNAL(triggered()), signalMapper, SLOT(map()));
            signalMapper->setMapping(circuitAct, circuitID);
            connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(goToCircuit(int)));
        }
    }
    
    void MainWindow::goToCircuit(int index)
    {
        qDebug() << "goToCircuit: " << index;
        Circuit *circuit = new Circuit(index);
        ui->stackedWidget->addWidget(circuit);
        ui->stackedWidget->setCurrentWidget(circuit);
    }
    


  • @hobbyProgrammer
    I don't know what is going on here, but I note that for a given circuitID you do two new Circuit(circuitID)s, one in fillCircuitMenu() and another any time slot goToCircuit(int index) is invoked. I don't know if that is correct, no re-use of same Circuit instance?

    Separately, I don't think there is any need to use deprecated QSignalMapper any longer. You can replace with new signal/slot syntax (you should get away from SIGNAL/SLOT() macros in all new code) plus a lambda. See e.g. https://doc.qt.io/qt-5/qsignalmapper.html#details for an example of this.