Add widget right aligned to a QTableWidget cell
-
I want to add a widget(QPushButton) at the end of a QTableWidget cell.
I know there's QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget) but it inserts the widget at the beginning of the cell. I want to insert a widget at end. -
Tested now:
tablewidgetdelegate.h#ifndef TABLEWIDGETDELEGATE_H #define TABLEWIDGETDELEGATE_H #include <QTableWidget> #include <QEvent> #include <QModelIndex> #include <QStyleOptionViewItem> #include <QHoverEvent> class TableWidgetDelegate : public QTableWidget { Q_OBJECT #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) Q_DISABLE_COPY_MOVE(TableWidgetDelegate) #else Q_DISABLE_COPY(TableWidgetDelegate) #endif public: explicit TableWidgetDelegate(QWidget *parent = Q_NULLPTR) : QTableWidget(parent) { viewport()->setAttribute(Qt::WA_Hover,true); } protected: bool viewportEvent(QEvent *event) Q_DECL_OVERRIDE { switch (event->type()) { case QEvent::HoverMove: case QEvent::HoverEnter: case QEvent::HoverLeave: { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QModelIndex index = indexAt(static_cast<QHoverEvent*>(event)->position().toPoint()); QStyleOptionViewItem options; initViewItemOption(&options); QAbstractItemDelegate *delegate = itemDelegateForIndex(index); #else QModelIndex index = indexAt(static_cast<QHoverEvent*>(event)->pos()); QStyleOptionViewItem options = viewOptions(); QAbstractItemDelegate *delegate = itemDelegate(index); #endif if(delegate){ QModelIndex buddy = model()->buddy(index); options.rect = visualRect(buddy); options.state |= (buddy == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); delegate->editorEvent(event, model(), options, buddy); } break; } default: break; } return QAbstractItemView::viewportEvent(event); } }; #endif // TABLEWIDGETDELEGATE_H
buttondelegate.h
#ifndef BUTTONDELEGATE_H #define BUTTONDELEGATE_H #include <QApplication> #include <QStyledItemDelegate> #include <QPushButton> #include <QMouseEvent> #include <QToolTip> #include <QPainter> #include <QPalette> #include <QTableWidget> #include "mybutton.h" class ButtonDelegate : public QStyledItemDelegate { Q_OBJECT #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) Q_DISABLE_COPY_MOVE(ButtonDelegate) #else Q_DISABLE_COPY(ButtonDelegate) #endif public: explicit ButtonDelegate(QObject* parent) :QStyledItemDelegate(parent), tableWidget(qobject_cast<QTableWidget*>(parent)) { mIsChecked = false; isDetailsButton = false; isEnabled = true; isHidden = false; } void update() { tableWidget->viewport()->update(); } void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { Q_ASSERT(index.isValid()); bool shouldPaint = false; if(isDetailsButton) { if(index.column() == 0) shouldPaint = true; } else shouldPaint = true; if(shouldPaint) { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); QStyleOptionButton buttonOption = buttonOptions(opt); if(! isHidden) { style->drawControl(QStyle::CE_PushButton, &buttonOption, painter, widget); } } else QStyledItemDelegate::paint(painter, option, index); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QSize baseSize = QStyledItemDelegate::sizeHint(option,index); const QRect butRect = buttonRect(opt); return QSize(baseSize.width()+butRect.width(),qMax(butRect.height(),baseSize.height())); } QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* result = new QWidget(parent); result->setGeometry(option.rect); QWidget* baseEditor = QStyledItemDelegate::createEditor(result,option,index); result->setFocusProxy(baseEditor); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QRect butRect = buttonRect(opt); baseEditor->setObjectName("baseEditor"); baseEditor->setGeometry(0,0,opt.rect.width()-butRect.width(),opt.rect.height()); MyButton* myButton = new MyButton(result); myButton->setObjectName("myButton"); myButton->setText(m_buttonText); myButton->setIcon(m_buttonIcon); myButton->setEnabled(false); myButton->setGeometry(opt.rect.width()-butRect.width(), 0, butRect.width(),butRect.height()); connect(myButton, &MyButton::clicked, this, &ButtonDelegate::clickedHelper); connect(myButton, &MyButton::mouseIn, this, &ButtonDelegate::mouseInHelper); return result; } void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE { currentIndex = index; QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); QStyledItemDelegate::setEditorData(baseEditor,index); } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); QStyledItemDelegate::setModelData(baseEditor,model,index); } void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); editor->setGeometry(opt.rect); const QRect butRect = buttonRect(opt); QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); baseEditor->setGeometry(0,0,opt.rect.width()-butRect.width(),opt.rect.height()); QWidget* myButton = editor->findChild<QWidget*>("myButton"); Q_ASSERT(myButton); myButton->setGeometry(opt.rect.width()-butRect.width(), 0, butRect.width(),butRect.height()); } const QString text() const { return m_buttonText; } void setText(const QString &newButtonText) { m_buttonText = newButtonText; update(); } const QIcon &icon() const { return m_buttonIcon; } void setIcon(const QIcon &newButtonIcon) { m_buttonIcon = newButtonIcon; update(); } void setChecked(bool checked) { mIsChecked = checked; } bool isChecked() { return mIsChecked; } void setToolTip(QString tooltip) { tooltipText = tooltip; } void setDetailsButton(bool idb) { isDetailsButton = idb; update(); } void setEnabled(bool enabled) { isEnabled = enabled; update(); } void setHidden(bool hide) { isHidden = hide; isEnabled = ! hide; update(); } void click() { mIsChecked = ! mIsChecked; clickedHelper(); } Q_SIGNALS: void clicked(const QModelIndex &index); void mouseIn(const QModelIndex &index); protected: bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override { Q_ASSERT(event); Q_ASSERT(model); Qt::ItemFlags flags = model->flags(index); if ((option.state & QStyle::State_Enabled) && (flags & Qt::ItemIsEnabled)) { switch (event->type()){ case QEvent::MouseButtonRelease:{ QStyleOptionViewItem viewOpt(option); initStyleOption(&viewOpt, index); QMouseEvent *me = static_cast<QMouseEvent*>(event); if (me->button() == Qt::LeftButton) { mIsChecked = ! mIsChecked; currentIndex = index; clickedHelper(); } } break; case QEvent::HoverMove: case QEvent::HoverEnter: case QEvent::HoverLeave: if(index!=currentIndex){ currentIndex = index; if(index.isValid()) mouseInHelper(); } break; default: break; } } return QStyledItemDelegate::editorEvent(event,model,option,index); } virtual QStyleOptionButton buttonOptions(const QStyleOptionViewItem &option, bool skipRct=false) const { const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); int buttonIconSize = style->pixelMetric(QStyle::PM_ButtonIconSize, 0, widget); QStyleOptionButton buttonOption; buttonOption.text = m_buttonText; buttonOption.icon = m_buttonIcon; buttonOption.iconSize = (QSize(buttonIconSize,buttonIconSize)); buttonOption.rect = skipRct ? QRect() : buttonRect(option); buttonOption.features = QStyleOptionButton::None; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.palette = option.palette; buttonOption.styleObject = option.styleObject; if(isEnabled) buttonOption.state = QStyle::State_Enabled; else buttonOption.state &= ~QStyle::State_Enabled; return buttonOption; } virtual QRect buttonRect(const QStyleOptionViewItem &option) const { const QStyleOptionButton buttonOption = buttonOptions(option, true); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); QSize buttonSize = style->sizeFromContents(QStyle::CT_PushButton, &buttonOption, QSize(), widget); buttonSize.setWidth(qMin(buttonSize.width(),option.rect.width()/2)); return QRect(option.rect.left()+option.rect.width()-buttonSize.width(),option.rect.top(),buttonSize.width(),qMax(buttonSize.height(),option.rect.height())); /* QRect r = option.rect; int x = isDetailsButton ? (r.left()+ r.width() - 10) : (r.center().x() - 6); int y = isDetailsButton ? r.top() : r.top() + 10; int s = isDetailsButton ? 10 : 1; return QRect(x, y, s, s); */ } virtual bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) override { if( !event || !view ) return false; if( event->type() == QEvent::ToolTip ) { QVariant tooltip = index.data( Qt::DisplayRole ); if( QApplication::keyboardModifiers() == Qt::AltModifier ) { QToolTip::showText( event->globalPos(), tooltipText); } else { QToolTip::showText( event->globalPos(), tooltipText); } if( !QStyledItemDelegate::helpEvent( event, view, option, index ) ) QToolTip::hideText(); return true; } return QStyledItemDelegate::helpEvent( event, view, option, index ); } private: mutable QModelIndex currentIndex; QPainter* mPainter; QString m_buttonText; QIcon m_buttonIcon; bool mIsChecked; bool isDetailsButton; bool isEnabled; bool isHidden; QString tooltipText; QTableWidget* tableWidget; void clickedHelper() { clicked(currentIndex); } void mouseInHelper() { mouseIn(currentIndex); } }; #endif // BUTTONDELEGATE_H
mybutton.h
#ifndef MYBUTTON_H #define MYBUTTON_H #include <QPushButton> class MyButton : public QPushButton { Q_OBJECT #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) Q_DISABLE_COPY_MOVE(MyButton) #else Q_DISABLE_COPY(MyButton) #endif public: MyButton(QWidget* parent = 0) : QPushButton(parent) {} ~MyButton() {}; signals: void mouseIn(); protected: #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void enterEvent(QEnterEvent*) Q_DECL_OVERRIDE #else void enterEvent(QEvent*) Q_DECL_OVERRIDE #endif { emit mouseIn(); } }; #endif // MYBUTTON_H
main.cpp
#include <QApplication> #include "buttondelegate.h" #include <QModelIndex> #include "tablewidgetdelegate.h" #include <QDebug> int main(int argc, char *argv[]) { QApplication app(argc,argv); TableWidgetDelegate wid; wid.setColumnCount(2); wid.setRowCount(2); ButtonDelegate *butDelegate = new ButtonDelegate(&wid); butDelegate->setText("Test"); QPixmap bluePixmap(20,20); bluePixmap.fill(Qt::blue); QIcon blueIcon; blueIcon.addPixmap(bluePixmap); butDelegate->setIcon(blueIcon); QObject::connect(butDelegate,&ButtonDelegate::clicked,[](const QModelIndex& index){qDebug() << "Clicked " << index;}); QObject::connect(butDelegate,&ButtonDelegate::mouseIn,[](const QModelIndex& index){qDebug() << "MouseIn " << index;}); wid.setItemDelegate(butDelegate); wid.show(); return app.exec(); }
-
one way to do it.
https://www.qtcentre.org/threads/3416-Center-a-widget-in-a-cell-on-a-QTableWidget
you may try delegate as well -
-
@JoeCFD I was able to solve by doing this:
QPushButton* detailsButton = new QPushButton; detailsButton->setFixedSize(20, 15); QHBoxLayout* hLayout = new QHBoxLayout; hLayout->addStretch(); hLayout->addWidget(detailsButton); widget->setLayout(hLayout); ui->table->setItem(row, 0, new QTableWidgetItem(ttitle)); ui->table->setIndexWidget(ui->table->model()->index(row, 0), widget);
Thanks for the help.
-
May
setIndexWidget
burn in hell forever and beyond!
https://forum.qt.io/post/475871 -
@hbatalha said in Add widget right aligned to a QTableWidget cell:
the buttons are only visible when the row is selected
Just remove:
if(!(option.state & QStyle::State_Selected)) return;
and I can't click it.
You need to adjust the edit trigger of the view: https://doc.qt.io/qt-5/qabstractitemview.html#editTriggers-prop
how do I get the button and connect it outside the delegate class?
create a signal in the delegate, something like
Q_SIGNAL void clicked(const QModelIndex &index);
and replaceconnect(editButton,&QPushButton::clicked,[](){qDebug("Edit");});
withconnect(editButton,&QPushButton::clicked,this,std::bind(&TailButtonsDelegate::clicked,this,index));
-
@VRonin I getting this error when trying to create a signal in the delegate:
C:\msys64\mingw64\include\c++\10.3.0\functional:460: error: no type named 'type' in 'class std::result_of<void (TailButtonsDelegate::*&(const TailButtonsDelegate*&, QModelIndex&))(const QModelIndex&)>' C:/msys64/mingw64/include/c++/10.3.0/functional:494:9: required from 'struct QtPrivate::FunctorReturnType<std::_Bind<void (TailButtonsDelegate::*(const TailButtonsDelegate*, QModelIndex))(const QModelIndex&)>, QtPrivate::List<> >' C:/Qt/6.2.0/mingw81_64/include/QtCore/qobject.h:306:158: required from 'static typename std::enable_if<(QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1), QMetaObject::Connection>::type QObject::connect(const typename QtPrivate::FunctionPointer<Func>::Object*, Func1, const QObject*, Func2, Qt::ConnectionType) [with Func1 = void (QAbstractButton::*)(bool); Func2 = std::_Bind<void (TailButtonsDelegate::*(const TailButtonsDelegate*, QModelIndex))(const QModelIndex&)>; typename std::enable_if<(QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1), QMetaObject::Connection>::type = QMetaObject::Connection; typename QtPrivate::FunctionPointer<Func>::Object = QAbstractButton]' release\../delegate.h:55:108: required from here C:/msys64/mingw64/include/c++/10.3.0/functional:460:8: error: no type named 'type' in 'class std::result_of<void (TailButtonsDelegate::*&(const TailButtonsDelegate*&, QModelIndex&))(const QModelIndex&)>' 460 | using _Res_type_impl | ^~~~~~~~~~~~~~
Also, I have a question: what's so wrong with
setIndexWidget
, creating a delegate seems so complex so I am asking what are the advantages of it oversetIndexWidget
? -
@hbatalha said in Add widget right aligned to a QTableWidget cell:
I getting this error when trying to create a signal in the delegate:
Can you show your code?
what's so wrong with setIndexWidget, creating a delegate seems so complex so I am asking what are the advantages of it over setIndexWidget?
Try creating 1000 rows and scroll through them. You'll realise very quick what the problem is.
setIndexWidget
is a HOG on resources -
@VRonin Sorry for the really late reply, I have been working offline due to internet cut.
@VRonin said in Add widget right aligned to a QTableWidget cell:
Try creating 1000 rows and scroll through them. You'll realise very quick what the problem is. setIndexWidget is a HOG on resources
I Will try that, I am curious to see it
Can you show your code?
#ifndef TAILBUTTONSDELEGATE_H #define TAILBUTTONSDELEGATE_H #include <QApplication> #include <QTableView> #include <QStandardItemModel> #include <QStyledItemDelegate> #include <QHeaderView> #include <QPushButton> class TailButtonsDelegate : public QStyledItemDelegate { Q_OBJECT Q_DISABLE_COPY(TailButtonsDelegate) public: explicit TailButtonsDelegate(QObject* parent = Q_NULLPTR) :QStyledItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { Q_ASSERT(index.isValid()); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); QStyleOptionButton editButtonOption; editButtonOption.text = QString(); //use emoji for text, optionally you can use icon + iconSize editButtonOption.rect = QRect(option.rect.left()+option.rect.width()-(2*option.rect.height()),option.rect.top(),option.rect.height(),option.rect.height()); editButtonOption.features = QStyleOptionButton::None; editButtonOption.direction = option.direction; editButtonOption.fontMetrics = option.fontMetrics; editButtonOption.palette = option.palette; editButtonOption.styleObject = option.styleObject; QStyleOptionButton removeButtonOption(editButtonOption); removeButtonOption.text = QString(); //use emoji for text, optionally you can use icon + iconSize removeButtonOption.rect = QRect(option.rect.left()+option.rect.width()-option.rect.height(),option.rect.top(),option.rect.height(),option.rect.height()); style->drawControl(QStyle::CE_PushButton, &editButtonOption, painter, widget); style->drawControl(QStyle::CE_PushButton, &removeButtonOption, painter, widget); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { const QSize baseSize = QStyledItemDelegate::sizeHint(option,index); return QSize(baseSize.width()+(2*baseSize.height()),baseSize.height()); } QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* result = new QWidget(parent); result->setGeometry(option.rect); QWidget* baseEditor = QStyledItemDelegate::createEditor(result,option,index); baseEditor->setObjectName("baseEditor"); baseEditor->setGeometry(0,0,option.rect.width()-(2*option.rect.height()),option.rect.height()); QPushButton* editButton = new QPushButton(result); editButton->setObjectName("editButton"); editButton->setGeometry(option.rect.width()-(2*option.rect.height()), 0, option.rect.height(),option.rect.height()); connect(editButton,&QPushButton::clicked,[]() { qDebug("Edit"); }); QPushButton* removeButton = new QPushButton(result); removeButton->setObjectName("removeButton"); removeButton->setGeometry(option.rect.width()-option.rect.height(), 0, option.rect.height(),option.rect.height()); connect(editButton,&QPushButton::clicked,this,std::bind(&TailButtonsDelegate::clicked,this,index)); return result; } void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); QStyledItemDelegate::setEditorData(baseEditor,index); } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); QStyledItemDelegate::setModelData(baseEditor,model,index); } void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { Q_UNUSED(index) editor->setGeometry(option.rect); QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); baseEditor->setGeometry(0,0,option.rect.width()-(2*option.rect.height()),option.rect.height()); QWidget* editButton = editor->findChild<QWidget*>("editButton"); Q_ASSERT(editButton); editButton->setGeometry(option.rect.width()-(2*option.rect.height()), 0, option.rect.height(),option.rect.height()); QWidget* removeButton = editor->findChild<QWidget*>("removeButton"); Q_ASSERT(removeButton); removeButton->setGeometry(option.rect.width()-option.rect.height(), 0, option.rect.height(),option.rect.height()); editor->setGeometry(option.rect); } Q_SIGNAL void clicked(const QModelIndex &index); }; #endif // TAILBUTTONSDELEGATE_H
Also I want to set the button size to
setFixedSize(10, 10);
but I have been having problems controlling the size. For the time being I have being using this version usingsetIndexWidget
QWidget* widget = new QWidget; QPushButton* detailsButton = new QPushButton; detailsButton->setFixedSize(10, 10); QHBoxLayout* hLayout = new QHBoxLayout; hLayout->addStretch(); hLayout->addWidget(detailsButton); widget->setLayout(hLayout); ui->tableWidget->setIndexWidget(ui->tableWidget->model()->index(row, 0), widget);
So above is what I am trying to achieve using delegate.
-
Sorry for the delay in getting back to you, I've been offline for a few days. Try this:
#ifndef TAILBUTTONDELEGATE_H #define TAILBUTTONDELEGATE_H #include <QApplication> #include <QStyledItemDelegate> #include <QPushButton> #include <QMouseEvent> class TailButtonDelegate : public QStyledItemDelegate { Q_OBJECT #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) Q_DISABLE_COPY_MOVE(TailButtonDelegate) #else Q_DISABLE_COPY(TailButtonDelegate) #endif public: explicit TailButtonDelegate(QObject* parent = Q_NULLPTR) :QStyledItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { Q_ASSERT(index.isValid()); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); QStyleOptionButton buttonOption = buttonOptions(opt); style->drawControl(QStyle::CE_PushButton, &buttonOption, painter, widget); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QSize baseSize = QStyledItemDelegate::sizeHint(option,index); const QRect butRect = buttonRect(opt); return QSize(baseSize.width()+butRect.width(),qMax(butRect.height(),baseSize.height())); } QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* result = new QWidget(parent); result->setGeometry(option.rect); QWidget* baseEditor = QStyledItemDelegate::createEditor(result,option,index); result->setFocusProxy(baseEditor); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QRect butRect = buttonRect(opt); baseEditor->setObjectName("baseEditor"); baseEditor->setGeometry(0,0,opt.rect.width()-butRect.width(),opt.rect.height()); QPushButton* extraButton = new QPushButton(result); extraButton->setObjectName("extraButton"); extraButton->setText(m_buttonText); extraButton->setIcon(m_buttonIcon); extraButton->setGeometry(opt.rect.width()-butRect.width(), 0, butRect.width(),butRect.height()); connect(extraButton,&QPushButton::clicked,this,&TailButtonDelegate::clickedHelper); return result; } void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE { currentIndex = index; QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); QStyledItemDelegate::setEditorData(baseEditor,index); } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); QStyledItemDelegate::setModelData(baseEditor,model,index); } void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); editor->setGeometry(opt.rect); const QRect butRect = buttonRect(opt); QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); baseEditor->setGeometry(0,0,opt.rect.width()-butRect.width(),opt.rect.height()); QWidget* extraButton = editor->findChild<QWidget*>("extraButton"); Q_ASSERT(extraButton); extraButton->setGeometry(opt.rect.width()-butRect.width(), 0, butRect.width(),butRect.height()); } const QString buttonText() const { return m_buttonText; } void setButtonText(const QString &newButtonText) { m_buttonText = newButtonText; } const QIcon &buttonIcon() const { return m_buttonIcon; } void setButtonIcon(const QIcon &newButtonIcon) { m_buttonIcon = newButtonIcon; } Q_SIGNALS: void buttonClicked(const QModelIndex &index); protected: bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) Q_DECL_OVERRIDE{ Q_ASSERT(event); Q_ASSERT(model); Qt::ItemFlags flags = model->flags(index); if ((option.state & QStyle::State_Enabled) && (flags & Qt::ItemIsEnabled)){ if (event->type() == QEvent::MouseButtonRelease) { QStyleOptionViewItem viewOpt(option); initStyleOption(&viewOpt, index); const QRect butRect = buttonRect(viewOpt); QMouseEvent *me = static_cast<QMouseEvent*>(event); if (me->button() == Qt::LeftButton && butRect.contains(me->pos())){ currentIndex = index; clickedHelper(); } } } return QStyledItemDelegate::editorEvent(event,model,option,index); } virtual QStyleOptionButton buttonOptions(const QStyleOptionViewItem &option, bool skipRct=false) const{ const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); int buttonIconSize = style->pixelMetric(QStyle::PM_ButtonIconSize, 0, widget); QStyleOptionButton buttonOption; buttonOption.text = m_buttonText; buttonOption.icon = m_buttonIcon; buttonOption.iconSize = (QSize(buttonIconSize,buttonIconSize)); buttonOption.rect = skipRct ? QRect() : buttonRect(option); buttonOption.features = QStyleOptionButton::None; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.palette = option.palette; buttonOption.styleObject = option.styleObject; buttonOption.state = option.state; return buttonOption; } virtual QRect buttonRect(const QStyleOptionViewItem &option) const{ const QStyleOptionButton buttonOption = buttonOptions(option,true); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); QSize buttonSize = style->sizeFromContents(QStyle::CT_PushButton, &buttonOption, QSize(), widget); buttonSize.setWidth(qMin(buttonSize.width(),option.rect.width()/2)); return QRect(option.rect.left()+option.rect.width()-buttonSize.width(),option.rect.top(),buttonSize.width(),qMax(buttonSize.height(),option.rect.height())); } private: mutable QModelIndex currentIndex; QString m_buttonText; QIcon m_buttonIcon; void clickedHelper(){buttonClicked(currentIndex);} }; #endif // TAILBUTTONDELEGATE_H
Then you can use it with something like:
int main(int argc, char *argv[]) { QApplication app(argc,argv); QTableWidget wid(2,2); TailButtonDelegate *butDelegate = new TailButtonDelegate(&wid); butDelegate->setButtonText("Test"); QPixmap bluePixmap(20,20); bluePixmap.fill(Qt::blue); QIcon blueIcon; blueIcon.addPixmap(bluePixmap); butDelegate->setButtonIcon(blueIcon); QObject::connect(butDelegate,&TailButtonDelegate::buttonClicked,[](const QModelIndex& index){qDebug() << "Clicked " << index;}); wid.setItemDelegate(butDelegate); wid.show(); return app.exec(); }
-
@hbatalha said in Add widget right aligned to a QTableWidget cell:
Also I want to set the button size to setFixedSize(10, 10);
To do this in the example above you'd need to tweak the
buttonRect()
method to something likevirtual QRect buttonRect(const QStyleOptionViewItem &option) const{ return QRect(option.rect.left()+option.rect.width()-10,option.rect.top(),10,10); }
-
@VRonin It's working greatly, thanks, but I want to access some QPushButton methods, namely
setCheckable
,isChecked
,setChecked
,setToolTip
. Also I want to emit signal when the mouse enters the button, I tried doind that but it's not working.
This is what I have tried to get a signal when mouse enters the button#ifndef TAILBUTTONSDELEGATE_H #define TAILBUTTONSDELEGATE_H #include <QApplication> #include <QStyledItemDelegate> #include <QPushButton> #include <QMouseEvent> class MyButton : public QPushButton { Q_OBJECT public: MyButton(QWidget* parent = 0) : QPushButton(parent) {} ~MyButton() {}; signals: void mouseIn(); protected: void enterEvent(QEnterEvent*) override { emit mouseIn(); } }; class TailButtonDelegate : public QStyledItemDelegate { Q_OBJECT #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) Q_DISABLE_COPY_MOVE(TailButtonDelegate) #else Q_DISABLE_COPY(TailButtonDelegate) #endif public: explicit TailButtonDelegate(QObject* parent = Q_NULLPTR) :QStyledItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { Q_ASSERT(index.isValid()); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); QStyleOptionButton buttonOption = buttonOptions(opt); style->drawControl(QStyle::CE_PushButton, &buttonOption, painter, widget); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QSize baseSize = QStyledItemDelegate::sizeHint(option,index); const QRect butRect = buttonRect(opt); return QSize(baseSize.width()+butRect.width(),qMax(butRect.height(),baseSize.height())); } QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* result = new QWidget(parent); result->setGeometry(option.rect); QWidget* baseEditor = QStyledItemDelegate::createEditor(result,option,index); result->setFocusProxy(baseEditor); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); const QRect butRect = buttonRect(opt); baseEditor->setObjectName("baseEditor"); baseEditor->setGeometry(0,0,opt.rect.width()-butRect.width(),opt.rect.height()); MyButton* myButton = new MyButton(result); myButton->setObjectName("myButton"); myButton->setText(m_buttonText); myButton->setIcon(m_buttonIcon); myButton->setGeometry(opt.rect.width()-butRect.width(), 0, butRect.width(),butRect.height()); connect(myButton, &MyButton::clicked, this, &TailButtonDelegate::clickedHelper); connect(myButton, &MyButton::mouseIn, this, &TailButtonDelegate::mouseInHelper); return result; } void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE { currentIndex = index; QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); QStyledItemDelegate::setEditorData(baseEditor,index); } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE { QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); QStyledItemDelegate::setModelData(baseEditor,model,index); } void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); editor->setGeometry(opt.rect); const QRect butRect = buttonRect(opt); QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor"); Q_ASSERT(baseEditor); baseEditor->setGeometry(0,0,opt.rect.width()-butRect.width(),opt.rect.height()); QWidget* myButton = editor->findChild<QWidget*>("myButton"); Q_ASSERT(myButton); myButton->setGeometry(opt.rect.width()-butRect.width(), 0, butRect.width(),butRect.height()); } const QString buttonText() const { return m_buttonText; } void setButtonText(const QString &newButtonText) { m_buttonText = newButtonText; } const QIcon &buttonIcon() const { return m_buttonIcon; } void setButtonIcon(const QIcon &newButtonIcon) { m_buttonIcon = newButtonIcon; } Q_SIGNALS: void clicked(const QModelIndex &index); void mouseIn(const QModelIndex &index); protected: bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) Q_DECL_OVERRIDE { Q_ASSERT(event); Q_ASSERT(model); Qt::ItemFlags flags = model->flags(index); if ((option.state & QStyle::State_Enabled) && (flags & Qt::ItemIsEnabled)) { if (event->type() == QEvent::MouseButtonRelease) { QStyleOptionViewItem viewOpt(option); initStyleOption(&viewOpt, index); const QRect butRect = buttonRect(viewOpt); QMouseEvent *me = static_cast<QMouseEvent*>(event); if (me->button() == Qt::LeftButton && butRect.contains(me->pos())) { currentIndex = index; clickedHelper(); } } if (event->type() == QEvent::Enter) { { currentIndex = index; mouseInHelper(); } } } return QStyledItemDelegate::editorEvent(event,model,option,index); } virtual QStyleOptionButton buttonOptions(const QStyleOptionViewItem &option, bool skipRct=false) const { const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); int buttonIconSize = style->pixelMetric(QStyle::PM_ButtonIconSize, 0, widget); QStyleOptionButton buttonOption; buttonOption.text = m_buttonText; buttonOption.icon = m_buttonIcon; buttonOption.iconSize = (QSize(buttonIconSize,buttonIconSize)); buttonOption.rect = skipRct ? QRect() : buttonRect(option); buttonOption.features = QStyleOptionButton::None; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.palette = option.palette; buttonOption.styleObject = option.styleObject; buttonOption.state = option.state; return buttonOption; } virtual QRect buttonRect(const QStyleOptionViewItem &option) const { const QStyleOptionButton buttonOption = buttonOptions(option,true); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); QSize buttonSize = style->sizeFromContents(QStyle::CT_PushButton, &buttonOption, QSize(), widget); buttonSize.setWidth(qMin(buttonSize.width(),option.rect.width()/2)); return QRect(option.rect.left()+option.rect.width()-10,option.rect.top(),10,10); } private: mutable QModelIndex currentIndex; QString m_buttonText; QIcon m_buttonIcon; void clickedHelper() { clicked(currentIndex); } void mouseInHelper() { mouseIn(currentIndex); } }; #endif // TAILBUTTONSDELEGATE_H
but it is not working.
-
Sorry for the choppy response. Your delegate code is doing the right thing (just minor note it should handle
QEvent::HoverEnter
instead ofQEvent::Enter
), the problem is that hover events are not passed by the view to the delegate. You need to subclass your view and reimplementviewportEvent
to something like: (warning: untested code)class HoverDelegateView : public QTableView{ Q_OBJECT #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) Q_DISABLE_COPY_MOVE(HoverDelegateView) #else Q_DISABLE_COPY(HoverDelegateView) #endif public: explicit HoverDelegateView(QWidget *parent = Q_NULLPTR) : QTableView(parent) {} protected: bool viewportEvent(QEvent *event) Q_DECL_OVERRIDE { switch (event->type()){ case QEvent::HoverMove: case QEvent::HoverEnter: case QEvent::HoverLeave:{ #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QModelIndex index = indexAt(static_cast<QHoverEvent*>(event)->position().toPoint()); QStyleOptionViewItem options; initViewItemOption(&options); #else QModelIndex index = indexAt(static_cast<QHoverEvent*>(event)->pos()); QStyleOptionViewItem options = viewOptions(); #endif QModelIndex buddy = model()->buddy(index); options.rect = visualRect(buddy); options.state |= (buddy == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); QAbstractItemDelegate *delegate = itemDelegate(index); delegate->editorEvent(event, model(), options, buddy); break; } default: break; } return QAbstractItemView::viewportEvent(event); } };