PDA

View Full Version : QListWidgetItem check/uncheck



Arthur
25th April 2006, 16:21
Hello,

I have a QListWidget with QAbstractItemView::ContiguousSelection and checkable items.

What I would like to do is to multiple select a couple of items and then check/uncheck them. Unfortunately, the selection of all items changes first and then the checkstate is updated (so I first recieve a selectionChanged and then a itemChanged).

Does anyone have a smart suggestion on how to change the behavior like this:

- If I hit the checkbox I don't want my selection state(s) to be changed
or (if not possible)
- If I do not hit the checkbox, I want my itemChanged event first and then a selectionChanged event

Regards,
Arthur

jpn
25th April 2006, 17:02
I think a common way to handle check state changes of multiple items is to provide an "external" widget for that purpose.
You could for example provide a checkbox (outside the list widget) and/or a context menu action for changing the checkstate of selected items.

Otherwise you might have to catch mouse click events (and possible some others too) to prevent the "view" to perform it's normal behaviour..

Arthur
5th May 2006, 13:52
An external widget to check/uncheck all is of course handy, but what I want is multiple uncheck/check when I have items selected. I couldn't find a way of fixing this (I can't even find a checkbox widget based on mouseclick coordinates).

Is there someone who can help me out?

jpn
8th May 2006, 21:57
Unfortunately, the selection of all items changes first and then the checkstate is updated (so I first recieve a selectionChanged and then a itemChanged).
Did you notice that item selection is changed due to mouse press events and the check state is not changed until mouse release events..?

Anyhow, as I mentioned in the earlier post, you have to catch certain mouse events in order to prevent the view from acting like it normally does.

Try these steps to solve the problem:
- Override both, mousePressEvent and mouseReleaseEvent. Basically you want to handle these events always when they occur over an item's checkbox, and otherwise pass them over to the base class to handle it's contiguous selection.
- Whenever a mouse press event (or release event, whichever you prefer) occurs over an item's checkbox, change the check state of all selected items.

The hardest part might be calculating whether a mouse press/release event's position is inside an item's checkbox. You can use QStyle for that.

This is just for reference:


#include <QApplication>
#include <QListWidget>
#include <QListWidgetItem>
#include <QMouseEvent>
#include <QStyle>
#include <QStyleOptionViewItem>
#include <QStyleOptionButton>

class ListWidget : public QListWidget
{
public:
ListWidget(QWidget* parent = 0) : QListWidget(parent)
{
// contiguous selection mode
setSelectionMode(QAbstractItemView::ContiguousSele ction);

// add some dummy checkable example items
for (int i = 0; i < 10; ++i)
{
QListWidgetItem* item = new QListWidgetItem(QString::number(i+i));
item->setCheckState(Qt::Unchecked);
addItem(item);
}
}

protected:
void mousePressEvent(QMouseEvent* event)
{
QListWidgetItem* item = selectedCheckStateItem(event->pos());
if (item)
{
// mouse pressed over a selected items checkbox,
// change the check states of all selected items
setSelectedCheckStates(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
}
else
{
// otherwise let the base class handle mouse press
QListWidget::mousePressEvent(event);
}
}

void mouseReleaseEvent(QMouseEvent* event)
{
// do nothing if mouse released over a selected items checkbox
if (!selectedCheckStateItem(event->pos()))
{
// otherwise let the base class handle mouse release
QListWidget::mouseReleaseEvent(event);
}
}

private:
// returns the selected item whose checkbox lies under 'pos'
// returns 0 if not selected, no item at pos, or does not fit inside checkbox
QListWidgetItem* selectedCheckStateItem(const QPoint& pos)
{
QListWidgetItem* item = itemAt(pos);
if (item)
{
// with the help of styles, check if checkbox rect contains 'pos'
QStyleOptionButton opt;
opt.QStyleOption::operator=(viewOptions());
opt.rect = visualItemRect(item);
QRect r = style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt);
// assure that the item is also selected
if (selectedItems().contains(item) && r.contains(pos))
{
return item;
}
}
return 0;
}

// sets the check state of all selected items to 'checkState'
void setSelectedCheckStates(Qt::CheckState checkState)
{
foreach (QListWidgetItem* item, selectedItems())
item->setCheckState(checkState);
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ListWidget w;
w.show();
a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()));
return a.exec();
}

Arthur
9th May 2006, 10:42
Thanks, I spent hours on finding out how to determine the click was in a checkbox (I hoped that I could get a 'child' widget from a certain position in the QListView...)

I'll check out your code!

wysota
9th May 2006, 10:54
Wouldn't it be easier to use an external widget as suggested and when it is activated, check the selection of the list widget and mark all selected items as checked?

It should be as easy as:


void someClass::someSlot(){
foreach(QListWidgetItem *item, listwidget->selectedItems())
item->setCheckedState(Qt::Checked);
}

Arthur
12th May 2006, 10:19
It is easier indeed.