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

Combobox in Qtableview



  • Creating a combobox in Qtableview2 column 1 and passing values from Qtableview1 column1
    so i am storing column1 table1 values in Qstring and passing to combobox add item but combobox values are not updating i something i missed code is shared or need to create any slot to update combobox values based on user input on table1 column1

       QAbstractItemModel* table1 = ui.tableView->model();
       QAbstractItemModel* table2 = ui.tableView_2->model();
          for (int i = 0, maxI = table1->rowCount();i <= maxI;++i)
          {
              QComboBox* combo = new QComboBox();
              for (int r= 0, maxI = table1->rowCount();r < maxI;++r) 
              {
                  QString list1[20] = { table1->data(table1->index(r, 1)).toString() };
                  for (int j = 0; j< 10; j++) 
                  {
                      combo->addItem(list1[j]);
                  }
              }
                      ui.tableView_2->setIndexWidget(ui.tableView_2->model()->index(i, 1), combo);
          }
    

    Thanks in advance



  • what slot i can usein case when column1 row value changes for Qtableview?





  • this is using delegate approach



  • @n-2204 and that's how it should be done.



  • but i shared the code and i am asking doubt in that
    thanks



  • Your approach is wrong

    If you still want to use it, as in your other questions, you have to connect a slot to the rowsInserted signal



  • thanks
    thats what i am asking like which slot need to need or what i m missing



  • using this rowinserted signal even not going on this slot

    connect(ui.tableView->model(), SIGNAL(rowsInserted), ui.tableView_2->model(), SLOT(changedvalue()));
    
    void tool::changedvalue()
    {
    QAbstractItemModel* table1 = ui.tableView->model();
    QAbstractItemModel* table2 = ui.tableView_2->model();
    for (int i = 0, maxI = table2->rowCount();i <= maxI;++i)//combobox in table2 col1
    {
        for (int r = 0, maxI = table1->rowCount();r < maxI;++r)//getvalue fromtable1
        {
            QComboBox* combo = new QComboBox();
            QString list1[10] = { table1->data(table1->index(r, 0)).toString() };//store value in list
            qDebug() << "combox values1" ;
            for (int j = 0; j < 10; j++)
            {
                  combo->addItem(list1[j]);/pass list to combobox
                qDebug() << "combox values" << list1[j];/
            }
            ui.tableView_2->setIndexWidget(ui.tableView_2->model()->index(i, 1), combo);
        }      
    }
    }

  • Lifetime Qt Champion

    @n-2204 said in Combobox in Qtableview:

    connect(ui.tableView->model(), SIGNAL(rowsInserted), ui.tableView_2->model(), SLOT(changedvalue()));

    This is wrong - again: use the new signal/slot syntax to catch such errors on compile time. And if you don't want then watch the output to stdout during debugging!



  • @Christian-Ehrlicher
    I, and you, have repeatedly advised @n-2204 to change from the old macros to the new style, and provided the necessary links (e.g. https://forum.qt.io/topic/126193/spinbox-input-add-rows, I think we posted 4 times there to this effect). Precisely because the user keeps getting the connections wrong. It seems the user is not interested in making the effort to change their ways, and prefers to make the same mistakes and keep posting for others to sort out....



  • connect(ui.tableView->model(), &QAbstractItemModel::rowsInserted, this,
    &tool::changedvalue);
    

    but signal/slot should work for both the ways
    and one thing is there is change in column 1 row values and @VRonin you told to use signal rowinserted() ?



  • but signal/slot should work for both the ways

    It does but in case you make a mistake the new syntax lets you know straight away when you try to compile and it's not just a warning in the console while the program runs.
    Also the new connection is faster

    there is change in column 1 row values and @VRonin you told to use signal rowinserted() ?

    In that case you would use the conveniently named dataChanged signal

    void tool::changedvalue()

    please do no rebuild all the widgets everytime something changes. setIndexWidget is already super inefficient, if you keep recreating them your program will probably stutter a lot



  • So when i am passing my values in a Qstring that time its coming in combobox but same when i try using table1 column1 rows value that time only last row value is adding in Combobox values looks like all values not appending
    Where need to make changes?

    connect(ui.tableView->model(), &QAbstractItemModel::dataChanged, this,
              &Gearcycle_model::changedvalue);
    
    void tool::changedvalue() {
    QAbstractItemModel* table1 = ui.tableView->model();
    QAbstractItemModel* table2 = ui.tableView_2->model();
    for (int i = 0, maxI = table2->rowCount();i <= maxI;++i)//combobox in table2 col1
    {
        for (int r = 0, maxI = table1->rowCount();r < maxI;++r)//getvalue fromtable1
        {
            QComboBox* combo = new QComboBox();
            //QString list1[10] = { table1->data(table1->index(r, 0)).toString() };//store value in list
            QString list1[4] = { "input 1","input 2","input3","input 4" };
            qDebug() << "table index" << r;
            for (int j = 0; j < 3; j++)
            {
                combo->addItem(list1[j]);
                qDebug() << "combox values" << list1[j];//pass list to combobox
                qDebug() << "list j=" << j;
            }
            ui.tableView_2->setIndexWidget(ui.tableView_2->model()->index(i, 1), combo);
            qDebug() << "row value i=" << i;
        }
    }
    }


  • @n-2204 said in Combobox in Qtableview:

    Where need to make changes?

    Read your code. The loops are wrong, this is basic C++



  • @n-2204 said in Combobox in Qtableview:

    So when i am passing my values in a Qstring that time its coming in combobox but same when i try using table1 column1 rows value that time only last row value is adding in Combobox values looks like all values not appending
    Where need to make changes?

    connect(ui.tableView->model(), &QAbstractItemModel::dataChanged, this,
              &Gearcycle_model::changedvalue);
    
    void tool::changedvalue() {
    QAbstractItemModel* table1 = ui.tableView->model();
    QAbstractItemModel* table2 = ui.tableView_2->model();
    for (int i = 0, maxI = table2->rowCount();i <= maxI;++i)//combobox in table2 col1
    {
        for (int r = 0, maxI = table1->rowCount();r < maxI;++r)//getvalue fromtable1
        {
            QComboBox* combo = new QComboBox();
            //QString list1[10] = { table1->data(table1->index(r, 0)).toString() };//store value in list
    

    when passing this that time only last row value is coming in combobox

            QString list1[4] = { "input 1","input 2","input3","input 4" };
    

    this is working when i am passing QString list1[4] = { "input 1","input 2","input3","input 4" }; in combobox

            for (int j = 0; j < 3; j++)
            {
                combo->addItem(list1[j]);
                qDebug() << "combox values" << list1[j];//pass list to combobox
                qDebug() << "list j=" << j;
            }
            ui.tableView_2->setIndexWidget(ui.tableView_2->model()->index(i, 1), combo);
            qDebug() << "row value i=" << i;
        }
    }
    }
    

    yes, looks like some small change will be needed during addItem in combobox



  • Look at the loops of i and r. think about what they are doing, ask yourself when you want to create and populate a combo, inside which loop?



  • ya it works thankyou.

    void tool::rowvalue1() {
    QAbstractItemModel* table1 = ui.tableView->model();
    QAbstractItemModel* table2 = ui.tableView_2->model();
    for (int i = 0, maxI = table2->rowCount();i < maxI;++i)//combobox in table2 col1
    {
         QComboBox* combo = new QComboBox();
          combo->setDuplicatesEnabled(false);
      for (int r = 0, maxI = table1->rowCount();r < maxI;++r)//getvalue fromtable1
          {
             QString list1[10] = { table1->data(table1->index(r, 0)).toString() };//store value in list
           for (int j = 0; j < 1; ++j)
            {
    

    --using uniqueSet trying to add only unique values(no duplicate values) in Combobox but its not working is this correct way ? or any other way to do this ?

                QSet<QString> uniqueSet;
                 if (!uniqueSet.contains(list1[j]))//add only unique values in combobox
                 {
                    uniqueSet.insert(list1[j]);
                    combo->addItem(list1[j]);  //add list to combobox  
                    qDebug() << " values " << list1[j];
                }
            }
            ui.tableView_2->setIndexWidget(table2->index(i, 1), combo);
        }
      }
    }


  • @n-2204 said in Combobox in Qtableview:

    for (int j = 0; j < 1; ++j)

    What?!



  • no its 10
    j<10
    using uniqueSet trying to add only unique values(no duplicate values) in Combobox but its not working is this correct way ? or any other way to do this ?



  • My feeling is that you have no idea what your code does, you are just copy-pasting stuff around and hoping that it does something.
    Once again: read your code, run it step-by-step. This has nothing to do with Qt, it's just about loops



  • thanks for your answers
    9f7df07c-9f26-4a5b-826b-00d285364dcb-image.png
    but what i shared i working code
    and now i asked -
    using uniqueSet trying to add only unique values(no duplicate values) in Combobox but this way not working is this correct way ? or any other way to do this ?

    this it might be very simple for you or you are expert but i am new C++ and Qt so i am having so many doubts may be silly.
    Thanks



  • @n-2204 said in Combobox in Qtableview:

    no its 10
    j<10

    No, the code you pasted reads:

     for (int j = 0; j < 1; ++j)
    

    That's 1, not 10. Or are you saying you want help to fix your code but you don't paste the actual code you want help with?

    using uniqueSet trying to add only unique values(no duplicate values) in Combobox but this way not working

    If your pasted code is to be believed, you seem to declare QSet<QString> uniqueSet; inside the j loop.

                QSet<QString> uniqueSet;
                 if (!uniqueSet.contains(list1[j]))//add only unique values in combobox
    

    This will always be true. uniqueSet is always empty.


  • Lifetime Qt Champion

    Hi

    • any other way ?

    Well your idea with QSet<QString> uniqueSet; was fine, but the logic/location was not right
    and i think QStringList might be easier.

    so this would do it. ( i assume you want a combo on each row in table 2 with same non-dub values in the combo)

       QAbstractItemModel *table1 = ui->tableView->model();
        QAbstractItemModel *table2 = ui->tableView_2->model();
        QStringList guesslist;
        for (int r = 0, maxI = table1->rowCount(); r < maxI; ++r) { //for all value rows
            guesslist.append( table1->data(table1->index(r, 0)).toString() );//store value in stringlist
        }
        guesslist.removeDuplicates(); // strip dubs
        for (int i = 0, maxI = table2->rowCount(); i < maxI; ++i) { //for all rows
            QComboBox *combo = new QComboBox(); // make combo
            combo->addItems(guesslist);  //add list to combobox
            ui->tableView_2->setIndexWidget(table2->index(i, 1), combo);// add combo
        }
    

    alt text

    But a word of warning. If you have many rows then setIndexWidget can become quite heavy for
    smaller devices and the app might lag. The cure is using a delegate as fully shown here ( thx VRonin :)

    https://forum.qt.io/topic/125906/qtableview-combobox-in-columns/10

    This also has the benefit that you don't need to copy anything around. its using the values directly. always updated.



  • QStringList might be easier
    guesslist.removeDuplicates(); // strip dubs

     Ok Thanks!! but still looks like empty row value is coming in Combobox
    

    if you have many rows then setIndexWidget can become quite heavy for
    smaller devices and the app might lag

    I am having 6-7 rows in my table
    

    I'm not using delegate as I need to read index, selection and apply some condition based on Combobox values in other columns ...,for some further req.

    Thanks


  • Lifetime Qt Champion

    @n-2204

    Hi

    • Ok Thanks!! but still looks like empty row value is coming in Combobox

    Yes we add empty ones too but you can just dont do that.

    for (int r = 0, maxI = table1->rowCount(); r < maxI; ++r) { //for all value rows
       QString str = table1->data(table1->index(r, 0)).toString();
        if ( ! str.IsEmpty() ) 
         guesslist.append( str );//store value in stringlist
        }
    

    https://doc.qt.io/qt-5/qstring.html#isEmpty



  • @mrjj said in Combobox in Qtableview:

    for (int i = 0, maxI = table2->rowCount(); i < maxI; ++i) { //for all rows
    QComboBox *combo = new QComboBox(); // make combo
    combo->addItems(guesslist); //add list to combobox
    ui->tableView_2->setIndexWidget(table2->index(i, 1), combo);// add combo
    }

    Slight tweak to avoid recreating the combobox every time:

    for (int i = 0, maxI = table2->rowCount(); i < maxI; ++i) { //for all rows
    	const QModelIndex idx = table2->index(i, 1);
    	QComboBox *combo = qobject_cast<QComboBox*>(ui->tableView_2->indexWidget(idx));
    	if(!combo){
    		combo = new QComboBox(); // make combo  
    		ui->tableView_2->setIndexWidget(idx , combo);// add combo
    	}
    	combo->model()->removeRows(0,combo->count(),combo->rootModelIndex());
    	combo->addItems(guesslist);  //add list to combobox
    }
    

    @n-2204 said in Combobox in Qtableview:

    I'm not using delegate as I need to read index, selection and apply some condition based on Combobox values in other columns

    You can do that with delegates too. Probably even easier.



  • @VRonin @mrjj
    1.
    Actually when I'm using both the ways to add values in Combobox
    one thing happening ,if I added some values in column0 table1 then select in combobox and again adding values in table1 Column0 that time combobox selection go away

    I am using dataChanged() signal
    connect(ui.tableView->model(), &QAbstractItemModel::dataChanged, this,
    &tool::rowvalue1);
    Is it because of this signal ? how can i prevent this ??

    1. As I'm using SLOT::rowvalue1() to add combobox in Qtableview
      now how can i take index() of combobox as there is SIGNAL::QComboBox::currentIndexChanged, , so again I need to write SLOT for index
      void tool::IndexChanged(int index)
      {
      combo->setCurrentIndex(combo->currentIndex());
      //but combo will be inaccessible here ??
      }
      connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
      &tool::IndexChanged);
      Is this correct way ?


  • @n-2204 said in Combobox in Qtableview:

    Is it because of this signal ? how can i prevent this ??

    It's because you recreate the entire list every time. You should just add/remove only the items that changed


    You can use std::bind to forward the combo to the slot

    void IndexChanged(int index, QComboBox* combo){
    /// do stuff
    }
    

    connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, std::bind(&tool::IndexChanged, this, std::placeholders::_1, combo));



  • @VRonin
    It's because you recreate the entire list every time. You should just add/remove only the items that changed
    okay,, ya I'm using dataChanged signal , so i think whenever changes in table1 col0 values so combobox selection got affect

    connect(ui.tableView->model(), &QAbstractItemModel::dataChanged, this,
    &tool::rowvalue1);
    

    So need to change in SIGNAL or anycondition to be add?
    but I need to update data in combobox so datachanged signal must be used.
    need to give condition in combobox ? or in SLOT something need to change or what else ??



  • Instead of just doing

    combo->model()->removeRows(0,combo->count(),combo->rootModelIndex()); // remove all items from the combo
    combo->addItems(guesslist);  //add list to combobox
    

    iterate over the items in the combo, remove those that don't appear in guesslist and add those that appear in guesslist but are not already in the combo



  • to remove duplicates

    guesslist.removeDuplicates(); // clear duplicates
    guesslist.removeAll(QString(""));
    combo->model()->removeRows(0,combo->count(),combo->rootModelIndex()); // remove all items from the combo
    combo->addItems(guesslist);  //add list to combobox
    

    remove those that don't appear in guesslist
    --means using removerows() all items are removed everytime so this need to change ??



  • @n-2204 said in Combobox in Qtableview:

    all items are removed everytime so this need to change ??

    Correct



  • @VRonin Is removeItem() can be used ?



  • @VRonin said in Combobox in Qtableview:
    for (int r = 0, maxI = table1->rowCount(); r < maxI; ++r) { //for all value rows
    guesslist.append( table1->data(table1->index(r, 0)).toString() );//store value in stringlist
    }

    guesslist is always updated,

    for (int i = 0, maxI = table2->rowCount(); i < maxI; ++i)//for all rows
        {
            const QModelIndex idx = table2->index(i, 1);
            QComboBox* combo = qobject_cast<QComboBox*>(ui.tableView_2->indexWidget(idx));
            if (!combo)
              {
                combo = new QComboBox(); // make combo  
                ui.tableView_2->setIndexWidget(idx, combo);// add combo
            }
            colvallist1.removeDuplicates(); // strip dubs clear dublicates
            colvallist1.removeAll(QString(""));
            combo->setPlaceholderText(QString(" "));
    
            combo->addItems(guesslist); // adding gueslist will add item value everytime so no need to pass this right ??
    combo->removeItem(); 
    

    iterate over the items in the combo, remove those that don't appear in guesslist and add those that appear in guesslist but are not already in the combo

    item should store in another Qstringlist and then check with initial list ?



  • edited
    In prev code i add these code lines, and now selection not changed when added new items in tableview column and updating in col combobox values

     for (int i = 0; i < guesslist.count(); i++) 
            {
                if (combo->findText(guesslist[i]) != -1)//-1 means "not found"
                { 
                    continue;
                }
                else {
                    combo->addItem(guesslist[i]); //Add to QCombobox
                }
    

    but not able to apply for "remove those t don't appear in guesslist (when col1 row values chnaged)" ??



  • @n-2204
    So you have to one of:

    • Have another loop/iterator through all the items which are in the combo box and remove any which are not in the guess list now; or
    • Clear out all the items in the combo box first, and then only add back in those which are now in guess list.


  • @JonB said in Combobox in Qtableview:

    Clear out all the items in the combo box first, and then only add back in those which are now in guess list.

    when i'm clearing all first then, whatever my prev selection in combobox it will be gone 
     combo->clear();
    

    Have another loop/iterator through all the items which are in the combo box and remove any which are not in the guess list now

    tried this but not working correctly where to chnge ?,when I'm changing value in guesslist then the combo selection got changed

      QStringList itemsInComboBox;
                for (int index = 0; index < guesslist.count(); index++)
                {
                    itemsInComboBox << combo->itemText(index);
                    if ( guesslist[index]!=itemsInComboBox[index] )
                        combo->removeItem(index); 
                }


  • @n-2204 said in Combobox in Qtableview:

    when i'm clearing all first then, whatever my prev selection in combobox it will be gone
    combo->clear();

    Yes, and, so? If you want to preserve what was selected, note it and reselect.

    Your loop for removing is no good. For one thing, you only check the same index in both guess list and combo box. If you do not clear the items, implement what I wrote:

    Have another loop/iterator through all the items which are in the combo box and remove any which are not in the guess list now

    That is just basic algorithmic stuff.



  • @JonB

    Have another loop/iterator through all the items which are in the combo box and remove any which are not in the guess list now

    I tired this but not working correctly.. now if item in guesslist then also it got removed ???

     QStringList itemsInComboBox;
            for (int item = 0; item < combo->count(); ++item) {  //check in combobox
                itemsInComboBox << combo->itemText(item);
                qDebug() << "before remove " << itemsInComboBox;
                for (int index = 0; index < guesslist.count(); ++index) {  //check in list
                          if (itemsInComboBox[item]!=guesslist[index])
                          combo->removeItem(item);
                    //qDebug() << "after remove " << itemsInComboBox;
                }
            }

Log in to reply