4
votes

i have a QListWidget with checkable items and it is working fine so far. Now i want to check the items not only by clicking onto the small checkbox rect but just by clicking onto the item itself.

So i implemented the function

void TagList::on_tagList_itemClicked(QListWidgetItem *item)
{
    if( item==nullptr )
        return;

    clicked_ = true;
    if( item->data(Qt::CheckStateRole) != Qt::Checked  )
        item->setData( Qt::CheckStateRole, Qt::Checked );
    else
        item->setData( Qt::CheckStateRole, Qt::Unchecked );
    clicked_ = false;
}

(clicked_ is a class variable for my dirty hack described next.)

Which sets the checkstate on item click, BUT Now the items are no longer checkable by click onto the checkbox rect because it is toggled twice, by the ListWidget and by my slot. Unfortunately the ListWidget uses only a single column model, so i cannot distinguish the click onto the Checkbox from the click onto the item by the model index.

Then i also implemented

void TagList::onModelItemChanged(QModelIndex tl, QModelIndex br, QVector<int> roles)
{
if( (roles.empty() || roles.contains(Qt::CheckStateRole)) && !clicked_ ){
    qDebug() << "changed with no click";
    clicked_ = true;
    if( tl.data(Qt::CheckStateRole)== Qt::Unchecked )
        ui->tagList->model()->setData(tl,Qt::Checked,Qt::CheckStateRole);
    else
        ui->tagList->model()->setData(tl,Qt::Unchecked,Qt::CheckStateRole);
    clicked_ = false;
}else{
    qDebug() << "changed after click";
}
}

and connected it to the dataChanged signal of the model. This works fine BUT the list is also editable so whenever i rename an object in the list, the checkstate changes and unfortunately the List Widget also ignores the roles vector of the dataChanged signal. So the roles.empty() case always applies :( and renameing triggers a checkstate change...

Has anyone a simple solution for this case? I dont want to create my own model just for this simple case.

2
In such a case I'd probably overload the mouseReleaseEvent(QMouseEvent*) and try to find out if the click was on the text or the checkbox and insert the code from your clicked-slot if it was on the text. Of course you'd need to check if this works with different font sizes etc. Somewhat of a hack as well but my best idea from the top of my headBowdzone

2 Answers

2
votes

You just need to unset the Qt::ItemIsUserCheckable flag when adding new item:

void TagList::addNewTag(QString name) 
{
    QListWidgetItem *item= new QListWidgetItem(name, ui->tagList);
    item->setFlags(item->flags() & (~Qt::ItemIsUserCheckable));
    item->setCheckState(Qt::Unchecked);
    ui->tagList->addItem(item);
}

So that the item's check state won't change when user clicks on the checkbox.

Then implement the on_tagList_itemClicked slot as you did:

void TagList::on_tagList_itemClicked(QListWidgetItem *item)
{
    if(item == nullptr)
        return;
    if(item->data(Qt::CheckStateRole) != Qt::Checked)
        item->setData(Qt::CheckStateRole, Qt::Checked);
    else
        item->setData(Qt::CheckStateRole, Qt::Unchecked);
}
0
votes

The hacky but working solution is to start a QTimer::singleshot() for 60ms on every datachanged signal of the model and to block the itemClicked signal until the timer is finished.

If you have better solutions, please let me know.