I'd like to be able to deselect items in my QTreeView by clicking in a part of the QTreeView with no items in, but I can't seem to find anyway of doing this. I'd intercept a click that's not on an item, but the QTreeView doesn't have a clicked
signal, so I can't work out how to do this.

- 69,215
- 34
- 177
- 229
9 Answers
Based on @Eric's solution, and as it only deselects if the clicked item was selected, here is what I came up with. This solution also works when you click the blank area of the QTreeView
#ifndef DESELECTABLETREEVIEW_H
#define DESELECTABLETREEVIEW_H
#include "QTreeView"
#include "QMouseEvent"
#include "QDebug"
class DeselectableTreeView : public QTreeView
{
public:
DeselectableTreeView(QWidget *parent) : QTreeView(parent) {}
virtual ~DeselectableTreeView() {}
private:
virtual void mousePressEvent(QMouseEvent *event)
{
QModelIndex item = indexAt(event->pos());
bool selected = selectionModel()->isSelected(indexAt(event->pos()));
QTreeView::mousePressEvent(event);
if ((item.row() == -1 && item.column() == -1) || selected)
{
clearSelection();
const QModelIndex index;
selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select);
}
}
};
#endif // DESELECTABLETREEVIEW_H
Yassir

- 6,970
- 4
- 31
- 37
-
`indexAt` could be quite expensive, you could use `item` instead of the second call. `item.isValid()` is probably a better choice than comparing row and column to `-1`. Also, it seem to change the selection if clicking on elements? – Adrian Maire May 17 '16 at 09:17
This is actually quite simple (in PyQt):
class DeselectableTreeView(QtGui.QTreeView):
def mousePressEvent(self, event):
self.clearSelection()
QtGui.QTreeView.mousePressEvent(self, event)
Qt uses mousePressEvent
to emit clicked
. If you clear the selection before sending on the event, then if an item is clicked it will be selected, otherwise nothing will be selected. Many thanks to Patrice for helping me out with this one :)

- 69,215
- 34
- 177
- 229
-
It should be noted that this emits the relevant signals from the selection model, so if you're listening to them, this will usually lead to unwanted behaviour (i.e. if you always open/close a view for the selected item, this would lead to the page being closed and opened rapidly). You can circumnavigate this by [blocking the selection model's signals](https://doc.qt.io/qt-5/qobject.html#blockSignals) – Folling Jan 22 '20 at 09:17
-
This only works when you use a single item selection model, you would lose multiple selections when clicking on a new item. – DirkR Dec 24 '21 at 20:43
The clearSelection does not work in my case. I'm using treeviews with a singleselection mode. Here is what I've coded:
class DeselectableTreeView : public QTreeView
{
public:
DeselectableTreeView(QWidget *parent) : QTreeView(parent) {}
virtual ~DeselectableTreeView() {}
private:
virtual void mousePressEvent(QMouseEvent *event)
{
QModelIndex item = indexAt(event->pos());
bool selected = selectionModel()->isSelected(item);
QTreeView::mousePressEvent(event);
if (selected)
selectionModel()->select(item, QItemSelectionModel::Deselect);
}
};
This works really fine.
Eric

- 15,657
- 10
- 64
- 169

- 101
- 1
- 2
QTreeView
inherits from QAbstractView
(http://doc.qt.digia.com/4.6/qtreeview.html) which has a clicked
signal. The problem is that the signal is emitted only when index is valid so you can not achieve what you want with this signal.
Try to intercept the mousePressEvent
instead. In the function you can find where the user has clicked and deselect selected item if needed.

- 8,023
- 5
- 37
- 52

- 14,136
- 6
- 46
- 59
-
Thanks. I've added my own answer but +1 because you helped me get there! – Skilldrick May 04 '10 at 20:53
In the answer by @Skilldrick, we risk sending superfluous events. If an item is already selected, and we're clicking it again, we are raising deselected and selected events. Based on other listeners in your application, this might not be what you want.
The solution by @eric-maeker only deselects an item if we click it again while it is already selected. Strictly speaking, this is not the answer to the original question, which was how to deselect the selected item when clicking somewhere else.
@yassir-ennazk gets close, but as pointed out by @adrian-maire, the solution is not optimal. event->pos()
is evaluated twice. Also, the mouse event is always evaluated by calling QTreeView::mousePressEvent
.
Here's the solution I've come up with, based on the other answers mentioned above. If we're clicking at a point where another tree view item is present, the new item is selected by forwarding the event to the TreeView
. If not, we're clearing the selection.
Note that this works for QTreeWidget
s as well.
virtual void mousePressEvent(QMouseEvent* event)
{
QModelIndex item = indexAt(event->pos());
if (item.isValid())
{
QTreeView::mousePressEvent(event);
}
else
{
clearSelection();
const QModelIndex index;
selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select);
}
}

- 183
- 9
-
1The only thing wrong with the default implementation is that it doesn't clear the selection when clicking on a blank area of the view. So just check whether the index as pos is valid. If it's not, call `clearSelection()`. Then unconditionally call the base-class `mousePressEvent`. – ekhumoro Jan 15 '17 at 17:59
To add to @Skilldrick's answer, if you need to apply this to a view that has already been instantiated because you're using Qt Designer, you can do something like this:
import new
def mousePressEvent(self, event):
self.clearSelection()
QtGui.QTableView.mousePressEvent(self, event)
self.ui.tableView.mousePressEvent = new.instancemethod(mousePressEvent, self.ui.tableView, None)
This assumes that your view is self.ui.tableView
.
Thanks to this answer: https://stackoverflow.com/a/1647616/1300519

- 1
- 1

- 3,348
- 2
- 29
- 51
You could try setting a different selection mode for your widget. I don't know if any of them quite covers what it appears you want (single selection, but deselectable).

- 14,785
- 3
- 42
- 49
Since the question is specifically about PyQt, I'd like to add this based on the answer by Nick Pruehs and the comment by ekhumoro, the simplest way to achieve this:
class TreeView(QTreeView):
def __init__(self, *args, **kwds):
QTreeView.__init__(self, *args, **kwds)
def mousePressEvent(self, event):
item = self.indexAt(event.pos())
if not item.isValid():
self.clearSelection()
QTreeView.mousePressEvent(self, event)

- 2,276
- 1
- 16
- 24
I chose to use event filtering rather than subclassing. I would subclass if I had a lot of these, but for a one off, I figured it was easier to modify my window code.
Some notes:
- Event filters need to be installed in the treeviews viewport
- Its important to both clear selection and clear the selection model.
Example below shows dialog constructor and the event filter:
MyWindow::MyWindow(QWidget *theParent) :
QDialog(theParent),
ui(new Ui::MyWindow)
{
ui->setupUi(this);
ui->MyTreeView->viewport()->installEventFilter(this);
}
bool MyWindow::eventFilter(QObject* theObject, QEvent* theEvent)
{
if (theObject == ui->MyTreeView->viewport())
{
if(theEvent->type() == QEvent::MouseButtonPress)
{
QMouseEvent* aMouseEvent = static_cast<QMouseEvent*>(theEvent);
if (aMouseEvent->button() == Qt::LeftButton)
{
auto anIndex = ui->MyTreeView->indexAt(aMouseEvent->pos());
if (anIndex.isValid() == false)
{
ui->MyTreeView->clearSelection();
ui->MyTreeView->selectionModel()->clearSelection();
}
}
}
}
return QObject::eventFilter(theObject, theEvent);
}

- 1,875
- 3
- 6
- 19

- 1
- 3