Adding QButtons to a QTableWidget Header
-
I am trying to add QButtons to the header of a QTableWidget and trying to follow the guidelines here:
qt-support-weekly-27-widgets-on-a-header
qtablewidget-personal-widget-as-headerThe code I have works and the buttons appear in the header. The problem is that the buttons don't automatically align to the columns:
If I minimize the window and then display it again, the buttons are aligned, so it seems like some sort of paint/repaint problem. However, I've tried putting 'repaint' just about everywhere and can't find a fix for this. If the window is widened fairly quickly, the same alignment issue happens, but again, a minimize and redisplay fixes it.
Here's the code I'm using. Sorry for putting so much here - this is the smallest I could get it and still show the issue. This forum doesn't allow attachments apparently. Any ideas on how to fix the button / column alignment issue is very much appreciated!
main.cpp:
#include "mainWindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
mainWindow.h:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTableWidgetItem> #include <QPushButton> #include <QMessageBox> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE const int COL_COUNT = 8; const int ROW_COUNT = 500; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); protected: void timerEvent(QTimerEvent* event); private: Ui::MainWindow *ui; int _timerID; // ID of our timer event bool _firstTime; // List containing pointers to the buttons in the table header QList<QPushButton *> _hdrButton; private slots: void popup(); }; #endif // MAINWINDOW_H
mainWindow.cpp:
#include "mainWindow.h" #include "ui_mainWindow.h" // Interval on which our timer event fires (in MS) static const int kMonitorTimerInverval = 1000; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , _timerID(0) , _firstTime(true) { ui->setupUi(this); ui->tableWidget->setRowCount(ROW_COUNT); ui->tableWidget->setColumnCount(COL_COUNT); ui->tableWidget->verticalHeader()->setVisible(false); ui->tableWidget->horizontalHeader()->setMinimumHeight(40); _hdrButton.clear(); for (int col = 2; col < COL_COUNT; col++) // Columns 3 - 8 only { _hdrButton.append(new QPushButton); connect(_hdrButton[col - 2], SIGNAL(clicked()), this, SLOT(popup())); ui->tableWidget->SetHorizontalHeaderItemWidget(col, _hdrButton[col - 2]); } _timerID = startTimer(kMonitorTimerInverval); // Start the timer } MainWindow::~MainWindow() { delete ui; } void MainWindow::popup() { // See which header button was clicked int column; for (column = 2; column < COL_COUNT; column++) { if (QObject::sender() == _hdrButton[column - 2]) { break; } } QString msg = QString("Column\n%1").arg(column + 1); QMessageBox msgBox; msgBox.setWindowTitle("Status"); msgBox.setText(msg); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } void MainWindow::timerEvent(QTimerEvent* event) { if ((event->timerId() == _timerID) && _firstTime) { // No text for columns 3 - 8 since the text will be // in the button that's located in the header QStringList headerText; headerText << "COL 1" << "COL 2" << "" << "" << "" << "" << "" << ""; ui->tableWidget->setHorizontalHeaderLabels(headerText); // Added text to the buttons and configure column sizing QString buttonText; for (int col = 0; col < COL_COUNT; col++) { if ((col == 0) || (col == 1)) // First two columns { ui->tableWidget->resizeColumnToContents(col); } else // Columns 3 - 8 { buttonText = QString("Button %1").arg(col - 1); ui->tableWidget->SetHorizontalHeaderItemText(col, buttonText); myHeaderView::Margins buttonMargins = { 4, 4, 4, 4 }; ui->tableWidget->SetHorizontalHeaderItemMargins(col, buttonMargins); ui->tableWidget->SetHorizontalHeaderItemResizeMode(col, QHeaderView::Stretch); } } ui->tableWidget->scrollToBottom(); _firstTime = false; } }
myTableWidget.h:
#ifndef EU_TABLE_WIDGET_H #define EU_TABLE_WIDGET_H #include <QtCore/qglobal.h> #include <QTableWidget> #include "myHeaderView.h" class myTableWidget : public QTableWidget { public: myTableWidget(QWidget * parent = Q_NULLPTR); void scrollContentsBy(int dx, int dy); void SetHorizontalHeaderItemWidget(int column, QWidget * widget); void SetHorizontalHeaderItemMargins(int column, myHeaderView::Margins margins); void SetHorizontalHeaderItemStylesheet(int column, QString &style); void SetHorizontalHeaderItemText(int column, QString &text); void SetHorizontalHeaderItemResizeMode(int column, QHeaderView::ResizeMode mode); private: myHeaderView * mHorizontalHeader; }; #endif // EU_TABLE_WIDGET_H
myTableWidget.cpp:
#include "myTableWidget.h" myTableWidget::myTableWidget(QWidget * parent) : QTableWidget(parent) { mHorizontalHeader = new myHeaderView(Qt::Orientation::Horizontal, this); setHorizontalHeader(mHorizontalHeader); } void myTableWidget::scrollContentsBy(int dx, int dy) { QTableWidget::scrollContentsBy(dx, dy); if (dx != 0) mHorizontalHeader->FixWidgetPositions(); } void myTableWidget::SetHorizontalHeaderItemWidget(int column, QWidget * widget) { mHorizontalHeader->SetItemWidget(column, widget); } void myTableWidget::SetHorizontalHeaderItemMargins(int column, myHeaderView::Margins margins) { mHorizontalHeader->SetItemMargins(column, margins); } void myTableWidget::SetHorizontalHeaderItemStylesheet(int column, QString &style) { mHorizontalHeader->SetItemStylesheet(column, style); } void myTableWidget::SetHorizontalHeaderItemText(int column, QString &text) { mHorizontalHeader->SetItemText(column, text); } void myTableWidget::SetHorizontalHeaderItemResizeMode(int column, QHeaderView::ResizeMode mode) { mHorizontalHeader->setSectionResizeMode(column, mode); }
myHeaderView.h:
#ifndef MY_HEADER_VIEW_H #define MY_HEADER_VIEW_H #include <QWidget> #include <QHeaderView> #include <QPushButton> class myHeaderView : public QHeaderView { public: struct Margins { int left; int right; int top; int buttom; Margins(int left = 2, int right = 2, int top = 2, int buttom = 2); }; myHeaderView(Qt::Orientation orientation, QWidget *parent = nullptr); void FixWidgetPositions(); void SetItemWidget(int index, QWidget * widget); void SetItemMargins(int index, Margins margins); void SetItemStylesheet(int index, QString &style); void SetItemText(int index, QString &text); void SetItemResizeMode(int index, QHeaderView::ResizeMode mode); private: struct Item { QWidget * item; Margins margins; Item(); }; QMap<int, Item> mItems; void showEvent(QShowEvent * e); void HandleSectionResized(int i); void HandleSectionMoved(int logical, int oldVisualIndex, int newVisualIndex); }; #endif // MY_HEADER_VIEW_H
myHeaderView.cpp:
#include "myHeaderView.h" myHeaderView::myHeaderView(Qt::Orientation orientation, QWidget * parent) : QHeaderView(orientation, parent) { connect(this, &myHeaderView::sectionResized, this, &myHeaderView::HandleSectionResized); connect(this, &myHeaderView::sectionMoved, this, &myHeaderView::HandleSectionMoved); } void myHeaderView::showEvent(QShowEvent *e) { for (int i = 0; i < count(); i++) { if (!mItems[i].item) { mItems[i].item = new QWidget(this); } else { mItems[i].item->setParent(this); } mItems[i].item->setGeometry(sectionViewportPosition(i) + mItems[i].margins.left, mItems[i].margins.top, sectionSize(i) - mItems[i].margins.left - mItems[i].margins.right - 1, height() - mItems[i].margins.top - mItems[i].margins.buttom - 1); mItems[i].item->show(); } QHeaderView::showEvent(e); } void myHeaderView::HandleSectionResized(int i) { for (int j = visualIndex(i); j < count(); j++) { int logical = logicalIndex(j); mItems[logical].item->setGeometry(sectionViewportPosition(logical) + mItems[i].margins.left, mItems[i].margins.top, sectionSize(logical) - mItems[i].margins.left - mItems[i].margins.right - 1, height() - mItems[i].margins.top - mItems[i].margins.buttom - 1); } } void myHeaderView::HandleSectionMoved(int logical, int oldVisualIndex, int newVisualIndex) { for (int i = qMin(oldVisualIndex, newVisualIndex); i < count(); i++) { logical = logicalIndex(i); mItems[logical].item->setGeometry(sectionViewportPosition(logical) + mItems[i].margins.left, mItems[i].margins.top, sectionSize(logical) - mItems[i].margins.left - mItems[i].margins.right - 1, height() - mItems[i].margins.top - mItems[i].margins.buttom - 1); } } void myHeaderView::FixWidgetPositions() { for (int i = 0; i < count(); i++) { mItems[i].item->setGeometry(sectionViewportPosition(i) + mItems[i].margins.left, mItems[i].margins.top, sectionSize(i) - mItems[i].margins.left - mItems[i].margins.right - 1, height() - mItems[i].margins.top - mItems[i].margins.buttom - 1); } } void myHeaderView::SetItemWidget(int index, QWidget * widget) { widget->setParent(this); mItems[index].item = widget; } void myHeaderView::SetItemMargins(int index, myHeaderView::Margins margins) { mItems[index].margins = margins; HandleSectionResized(index); } void myHeaderView::SetItemStylesheet(int index, QString &style) { mItems[index].item->setStyleSheet(style); } void myHeaderView::SetItemText(int index, QString &text) { reinterpret_cast<QPushButton *>(mItems[index].item)->setText(text); } void myHeaderView::SetItemResizeMode(int index, QHeaderView::ResizeMode mode) { reinterpret_cast<QHeaderView *>(mItems[index].item)->setSectionResizeMode(mode); } myHeaderView::Margins::Margins(int left, int right, int top, int buttom) : left(left), right(right), top(top), buttom(buttom) { } myHeaderView::Item::Item() : item(nullptr) { }
-
J JonB referenced this topic on