QTableWidget setCellWidget(QWidget*) Inconsistant Behavior with Cell Selection and Focus
-
I've made a subclassed
QTableWidgetand wanted to make some cells haveQPushButtonsas cell widgets since I've made a pretty heavily styled button usingQPropertyAnimationsand 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 theQTableWidgetand it was almost perfect. Since I do some subclassedQStyledItemDelegateclass 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
QCheckBoxin aQTableWidget'scell so it's not offset on the left hand side. The answer to that question was essentially this:- Create a
QWidget - Create the
QWidgetyou want to center - Create a
QH/VLayoutand set the top levelQWidget'slayout to be this - Add the sub widget that you want to center to this layout
- On the
QTableWidgetuse thesetCellWidgetfunction and set it for the row and column with the top levelQWidgetand voila, you have a centeredQWidgetin 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
QPushButtonthat I embedded in the managingQWidgetfor that particular cell. It seems that changing the focus messes something up internally on theQTableWidgetwhen 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_HTableWidget.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_HButton.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_HWidget.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
TableWidgetyou 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 theButtonobject. The Button object will correctly print that in thefocusInfunction that it went off when the cell selected contains it. However, you would expect the same behavior to occur for theWidgettoo since its added exactly the same way with exactly the same code, just with aButtonembedded inside of itsQHBoxLayout. Once you navigate to theButtonor theWidgetthe keyboard navigation for theTableWidgetseems to break and key presses don't forward from theWidgetto its childButtoneven if I were to set thesetFocusProxyon theWidgetto itsm_buttonfield which is the exact same typeButtonthat can correctly get theKeyEvent. I'm not quite sure if this is a bug or if I've mangled some behavior. - Create a