I'm working on a custom QTableView, use it to show a list of reomte files.
This is the configuration of the QTableView:
this->verticalHeader()->hide();
this->setSelectionMode(QAbstractItemView::ExtendedSelection);
this->setSelectionBehavior(QAbstractItemView::SelectRows);
this->setShowGrid(false);
this->setEditTriggers(QAbstractItemView::NoEditTriggers);
When I selected a row, it highlight the row without any issue. But when I clicked other area which don't have any items, the selected row becomes unselected, the dotted line border shows.
It there any way to hide the border in this case?
My Qt version:
Qt Creator 8.0.1
Based on Qt6.3.1 (MSVC 2019,x86_64)
CodePudding user response:
What you are seeing is the focus outline. There are several solutions to get rid of it, none of which is easy and straightforward.
Before I dive into it, notice there can be 3 states:
- An item has been selected.
- You clicked in e.g. another view after selecting an item.
- You clicked somewhere in the view where there is no item (= your case above)
There is a 4th state which, obviously, is items you have not selected at any point; they look the same no matter what view is the active widget and I am going to ignore them.
I assume you want to keep all 3 states as is, except for the outline in the first and third screenshots.
Solution 1: Stylesheet (recommended):
Assuming you have exactly the same colors as me, set the following stylesheet on your table view or directly on the instance of QApplication
.
QTableView { outline:none; }
QTableView::item:selected:focus { background:#0078D7; }
QTableView::item:!selected:focus { background:transparent; }
Line-by-line explanation:
- The view is set to display no outline, although it has no effect if not combined with the next lines.
- This corresponds to my first screenshot (
:selected
and:focus
are true).
You have to force the background for the outline to actually be removed, even if the value you force is the same as what the style originally has (do not ask me why, I do not know...).
Note: if you want the second screenshot (:selected
is true,:focus
is false) to look like the first, you need to remove the:focus
pseudo-state to merge both cases. - This corresponds to my third screenshot (
:selected
is false,:focus
is true).
Same reason as point 2.
Solution 2: Subclassing QProxyStyle:
Alternatively, you can subclass a style to completely ignore the primitive element QStyle::PE_FrameFocusRect
:
class NoFocusOutlineStyle : public QProxyStyle
{
public:
void drawPrimitive(PrimitiveElement element,
const QStyleOption* option,
QPainter* painter,
const QWidget* widget) const override
{
if (element == QStyle::PE_FrameFocusRect)
return;
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
};
To be added to you application, for example qtApp.setStyle(new NoFocusOutlineStyle);
Solution 3: Subclassing QStyledItemDelegate:
Last, you can instead create your own delegate to alter the style option before the item is painted on screen. All you need to do is override the paint
method. Skipping the header file and just looking at the implementation, this gives you:
void aQSNoFocusItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
QStyleOptionViewItem opt = option;
if (opt.state & QStyle::State_HasFocus && !(opt.state & QStyle::State_Selected))
opt.state &= ~QStyle::State_HasFocus;
QStyledItemDelegate::paint(painter, opt, index);
}
This solution requires you to call QAbstractItemView::setItemDelegate
, QAbstractItemView::setItemDelegateForColumn
or QAbstractItemView::setItemDelegateForRow
every time but allows fine-tuning.