QTableWidget setCellWidget(QWidget*) Inconsistant Behavior with Cell Selection and Focus
-
I've made a subclassed
QTableWidget
and wanted to make some cells haveQPushButtons
as cell widgets since I've made a pretty heavily styled button usingQPropertyAnimations
and what not and really wanted to embed the widget in the cell. Well I've used thesetCellWidget(QWidget* widget)
function that is a part of theQTableWidget
and it was almost perfect. Since I do some subclassedQStyledItemDelegate
class drawing on my table's cell items, I draw some border lines that seemed to be conflicting a bit with the size of the cell widget.Previously someone asked how to center a
QCheckBox
in aQTableWidget's
cell so it's not offset on the left hand side. The answer to that question was essentially this:- Create a
QWidget
- Create the
QWidget
you want to center - Create a
QH/VLayout
and set the top levelQWidget's
layout to be this - Add the sub widget that you want to center to this layout
- On the
QTableWidget
use thesetCellWidget
function and set it for the row and column with the top levelQWidget
and voila, you have a centeredQWidget
in the cell that you can manipulate and align however you want
Great, that visually worked... However, I noticed some nasty side effects of this. The arrow key navigation seems to break and I can no longer press the Enter key or Space key to "click/press" the
QPushButton
that I embedded in the managingQWidget
for that particular cell. It seems that changing the focus messes something up internally on theQTableWidget
when trying to move away from the cell with the arrows keys on the keyboard.I read online that some people said to disable the
setTabKeyNavigation(bool)
on the table to fix some similar navigation issues after setting focus... This did not do anything in my case. I've made a minimal compilable example that show cases the behaviorTableWidget.h:
#ifndef TABLEWIDGET_H #define TABLEWIDGET_H #include "button.h" #include "widget.h" #include <QTableWidget> #include <QDebug> class TableWidget : public QTableWidget{ Q_OBJECT public: TableWidget(QWidget* parent = nullptr); Widget *createWidget(); Button *createButton(); void setTableCell(QWidget *selecteditem); }; #endif // TABLEWIDGET_H
TableWidget.cpp:
#include "tablewidget.h" TableWidget::TableWidget(QWidget* parent) : QTableWidget(parent){ setFixedSize(750, 500); setColumnCount(5); for(int i = 0; i < 5; ++i){ insertRow(rowCount()); for(int j = 0; j < columnCount(); ++j){ QTableWidgetItem* item = new QTableWidgetItem; item->setFlags(item->flags() ^ Qt::ItemIsEditable); setItem(j, i, item); } } setCellWidget(0, 0, createButton()); setCellWidget(2, 0, createWidget()); } Button* TableWidget::createButton(){ Button* button = new Button; connect(button, &Button::focusReceived, this, [this, button](){ setTableCell(button); }, Qt::DirectConnection); return button; } Widget* TableWidget::createWidget(){ Button* button = new Button; connect(button, &Button::focusReceived, this, [this, button](){ setTableCell(button); }, Qt::DirectConnection); return new Widget(button); } //Helper to make keyboard focus more intuitive for cell widgets versus regular items void TableWidget::setTableCell(QWidget* selecteditem){ //Find the sender in the table for(int row = 0; row < rowCount(); ++row){ for(int col = 0; col < columnCount(); ++col){ if(cellWidget(row, col) == selecteditem){ qDebug() << "TableWidget::setTableCell"; setCurrentCell(row, col); setCurrentItem(this->item(row, col)); setCurrentIndex(this->indexFromItem(this->item(row, col))); return; } } } }
Button.h:
#define BUTTON_H #include <QPushButton> #include <QFocusEvent> #include <QDebug> class Button : public QPushButton{ Q_OBJECT public: Button(QWidget *parent = nullptr); void focusIn(QFocusEvent *event); signals: void focusReceived(); public slots: bool event(QEvent* e); }; #endif // BUTTON_H
Button.cpp:
#include "button.h" Button::Button(QWidget* parent) : QPushButton(parent){ setStyleSheet(QString("background-color: solid rgba(255, 0, 0, 75);")); } bool Button::event(QEvent* event){ switch(event->type()){ case QEvent::FocusIn: focusIn(static_cast<QFocusEvent*>(event)); return true; break; default: break; } return QWidget::event(event); } void Button::focusIn(QFocusEvent* event){ qDebug() << "Button::focusIn"; emit focusReceived(); QPushButton::focusInEvent(event); }
Widget.h:
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QFocusEvent> #include <QHBoxLayout> #include <QPointer> #include "button.h" class Widget : public QWidget{ Q_OBJECT public: Widget(Button* button, QWidget *parent = nullptr); public slots: bool event(QEvent* event); void focusIn(QFocusEvent *event); signals: void focusReceived(); protected: QPointer<QHBoxLayout> m_hLayout; QPointer<Button> m_button; }; #endif // WIDGET_H
Widget.cpp:
#include "widget.h" Widget::Widget(Button* button, QWidget* parent) : QWidget(parent){ m_button = button; setStyleSheet(QString("background-color: solid rgba(0, 0, 0, 0);")); m_hLayout = new QHBoxLayout; setLayout(m_hLayout); m_hLayout->addWidget(m_button); } bool Widget::event(QEvent* event){ switch(event->type()){ case QEvent::FocusIn: focusIn(static_cast<QFocusEvent*>(event)); return true; break; default: break; } return QWidget::event(event); } void Widget::focusIn(QFocusEvent* event){ qDebug() << "Widget::focusIn"; emit focusReceived(); QWidget::focusInEvent(event); }
So, when navigating the
TableWidget
you would expect to be able to see the highlighted cells to move with the cursor and "temporarily" give soft Focus to the widget for keyboard events. This happens ONLY on theButton
object. The Button object will correctly print that in thefocusIn
function that it went off when the cell selected contains it. However, you would expect the same behavior to occur for theWidget
too since its added exactly the same way with exactly the same code, just with aButton
embedded inside of itsQHBoxLayout
. Once you navigate to theButton
or theWidget
the keyboard navigation for theTableWidget
seems to break and key presses don't forward from theWidget
to its childButton
even if I were to set thesetFocusProxy
on theWidget
to itsm_button
field which is the exact same typeButton
that can correctly get theKeyEvent
. I'm not quite sure if this is a bug or if I've mangled some behavior. - Create a
1/1