How to put a QComboBox in a QTableWidget table header?
1 answer
The task is not trivial, despite the seeming simplicity at first glance. We need to start with the fact that it’s not at all easy to insert something different from what is usually present in the QTableWidget header (and any other similar widget). This is due to the fact that the title is not just an inscription, but a set of elements connected together, each of which has its own functional purpose, be it a sorting arrow or a border to fit the column size. Not to mention the difference in drawing styles. All this leads to the fact that if the developer wants serious changes instead of the standard behavior, then he will have to “draw” the title from scratch, including the above-mentioned components and, of course, based on the style used in the application.
In order to at least start, you can access the QTableView , since QTableWidget inherits from it. In this class, you can set your own horizontal header by calling the setHorizontalHeader() method. Its argument is a pointer to a QHeaderView object, which in turn must inherit and override the QHeaderView::paintSection() method. For example:
class MyHeaderView : public QHeaderView { Q_OBJECT public: explicit MyHeaderView(Qt::Orientation orientation , QWidget *parent = Q_NULLPTR) : QHeaderView(orientation, parent) {} protected: virtual void paintSection(QPainter *painter , const QRect &rect, int logicalIndex) const; }; void MyHeaderView::paintSection(QPainter *painter , const QRect &rect, int logicalIndex) const { QStyleOptionComboBox cbox_opt; cbox_opt.rect = rect; cbox_opt.state = QStyle::State_Active | QStyle::State_Enabled; cbox_opt.currentText = "tra-ta-ta"; QApplication::style() ->drawComplexControl(QStyle::CC_ComboBox, &cbox_opt, painter); QApplication::style() ->drawControl(QStyle::CE_ComboBoxLabel, &cbox_opt, painter); } QTableWidget tbl_wdg; tbl_wdg.setHorizontalHeader(new MyHeaderView(Qt::Horizontal,&tbl_wdg)); tbl_wdg.setWindowTitle("QTableWidget"); tbl_wdg.setEditTriggers(QAbstractItemView::AllEditTriggers); tbl_wdg.insertColumn(0); tbl_wdg.insertRow(0); tbl_wdg.show(); It turned out like this:
Everything is good, but no matter how we click on the title, the list will not be revealed - this is just a static picture. To revive it, you will have to refer to the source code of the QComboxBox , where the code is quite impressive in its size and it’s responsible for disclosing the list of elements in one method ( showPopup() ) and hiding it in another ( hidePopup() ). There is no point in citing all this code, since it is easily accessible and, unfortunately, we need all of it, we can’t do it in parts.
If you do not want to engage in drawing and porting code from Qt sources, then you can compromise and instead of redefining the title, do the substitution of the first row of the table or tree widget. In the end, this is a big saving of time and effort compared to how much you have to mess around with the substitution of the title, and then, as a completely natural consequence, also try to at least somehow combine part of the standard functionality with the new-found implementation. At least the same sort by clicking on the title.
Why not create a second heading from the first row of the table. This is relatively easy with a delegate:
#include <QtWidgets/QStyledItemDelegate> class Delegate : public QStyledItemDelegate { Q_OBJECT public: //! Constructor. explicit Delegate(QObject *parent = Q_NULLPTR) : QStyledItemDelegate(parent) {} virtual void paint(QPainter *painter , const QStyleOptionViewItem &option , const QModelIndex &index) const; virtual QWidget *createEditor(QWidget *parent , const QStyleOptionViewItem &option , const QModelIndex &index) const; }; void Delegate::paint(QPainter *painter , const QStyleOptionViewItem &option , const QModelIndex &index) const { QStyleOptionComboBox cbox_opt; cbox_opt.rect = option.rect; cbox_opt.state = QStyle::State_Active | QStyle::State_Enabled; cbox_opt.currentText = index.model()->data(index).toString(); QApplication::style() ->drawComplexControl(QStyle::CC_ComboBox, &cbox_opt, painter); QApplication::style() ->drawControl(QStyle::CE_ComboBoxLabel, &cbox_opt, painter); } QWidget *Delegate::createEditor(QWidget *parent , const QStyleOptionViewItem &option , const QModelIndex &index) const { QComboBox *cbox = new QComboBox(parent); cbox->setGeometry(option.rect); cbox->addItem("my text"); cbox->addItem("1"); cbox->addItem("2"); cbox->addItem("3"); return cbox; } Connecting is simple:
QTableWidget tbl_wdg; tbl_wdg.setWindowTitle("QTableWidget"); tbl_wdg.setEditTriggers(QAbstractItemView::AllEditTriggers); tbl_wdg.insertColumn(0); tbl_wdg.insertRow(0); tbl_wdg.setItem(0,0,new QTableWidgetItem("my text")); tbl_wdg.setItemDelegateForRow(0, new ADelegate(&tbl_wdg)); tbl_wdg.show(); It turns out quite a workable widget:


