Add widget right aligned to a QTableWidget cell
-
Sorry for the choppy response. Your delegate code is doing the right thing (just minor note it should handle
QEvent::HoverEnterinstead ofQEvent::Enter), the problem is that hover events are not passed by the view to the delegate. You need to subclass your view and reimplementviewportEventto 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); } }; -
Sorry for the choppy response. Your delegate code is doing the right thing (just minor note it should handle
QEvent::HoverEnterinstead ofQEvent::Enter), the problem is that hover events are not passed by the view to the delegate. You need to subclass your view and reimplementviewportEventto 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); } }; -
@VRonin I understood what you said, the concept behind it. In the code there are two undeclared identifiers:
viewOptionsandindexthough. -
Also I would like have the button in the middle of the cell(top - bottom), this when set the size to (10,10)
@hbatalha said in Add widget right aligned to a QTableWidget cell:
Also I would like have the button in the middle of the cell(top - bottom), this when set the size to (10,10)
Again this is managed by
buttonRect, the 3rd argument to be precise:virtual QRect buttonRect(const QStyleOptionViewItem &option) const{ return QRect(option.rect.left()+option.rect.width()-10,option.rect.top()+qMax(0,(option.rect.height()-10)/2),10,10); } -
@hbatalha said in Add widget right aligned to a QTableWidget cell:
@VRonin I understood what you said, the concept behind it. In the code there are two undeclared identifiers:
viewOptionsandindexthough.Thanks, fixed those
-
@hbatalha said in Add widget right aligned to a QTableWidget cell:
Also I would like have the button in the middle of the cell(top - bottom), this when set the size to (10,10)
Again this is managed by
buttonRect, the 3rd argument to be precise:virtual QRect buttonRect(const QStyleOptionViewItem &option) const{ return QRect(option.rect.left()+option.rect.width()-10,option.rect.top()+qMax(0,(option.rect.height()-10)/2),10,10); } -
@hbatalha said in Add widget right aligned to a QTableWidget cell:
@VRonin I understood what you said, the concept behind it. In the code there are two undeclared identifiers:
viewOptionsandindexthough.Thanks, fixed those
-
@VRonin I thought you edited the reply to fix the undeclared identifiers errors in the code but they are still there.
-
-
-
@VRonin yeah it is compiling now but it's not emitting the
HoverEntersignal unfortunately:
buttondelegate.h#ifndef BUTTONDELEGATE_H #define BUTTONDELEGATE_H #include <QApplication> #include <QStyledItemDelegate> #include <QPushButton> #include <QMouseEvent> #include <QToolTip> #include <QPainter> #include <QPalette> #include <QTableWidget> 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 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 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 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 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 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 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 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)) { if (event->type() == QEvent::MouseButtonRelease) { QStyleOptionViewItem viewOpt(option); initStyleOption(&viewOpt, index); QMouseEvent *me = static_cast<QMouseEvent*>(event); if (me->button() == Qt::LeftButton) { mIsChecked = ! mIsChecked; currentIndex = index; clickedHelper(); } } } if (event->type() == QEvent::HoverEnter) { 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; 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)); 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_HtableWidgetDelegate.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) {} protected: bool viewportEvent(QEvent *event) override { switch (event->type()) { case QEvent::HoverMove: case QEvent::HoverEnter: case QEvent::HoverLeave: case QEvent::MouseMove: { #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 qDebug() << "QTableWidget -- viewport"; QModelIndex buddy = model()->buddy(index); options.rect = visualRect(buddy); options.state |= (buddy == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); QAbstractItemDelegate *delegate = itemDelegateForIndex(index); delegate->editorEvent(event, model(), options, buddy); break; } default: break; } return QAbstractItemView::viewportEvent(event); } }; #endif // TABLEWIDGETDELEGATE_H -
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_Hbuttondelegate.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_Hmybutton.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_Hmain.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(); } -
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_Hbuttondelegate.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_Hmybutton.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_Hmain.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(); }@VRonin Working perfectly, can't thank you enough. I made some changes in the
buttondelegate.h'seditorEventfunction to better suit my needs: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: { QStyleOptionViewItem viewOpt(option); const QRect butRect = buttonRect(viewOpt); QMouseEvent *me = static_cast<QMouseEvent*>(event); currentIndex = index; if(index.isValid() && butRect.contains(me->pos())) mouseInHelper(); } break; default: break; } } return QStyledItemDelegate::editorEvent(event,model,option,index); } -
@VRonin Working perfectly, can't thank you enough. I made some changes in the
buttondelegate.h'seditorEventfunction to better suit my needs: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: { QStyleOptionViewItem viewOpt(option); const QRect butRect = buttonRect(viewOpt); QMouseEvent *me = static_cast<QMouseEvent*>(event); currentIndex = index; if(index.isValid() && butRect.contains(me->pos())) mouseInHelper(); } break; default: break; } } return QStyledItemDelegate::editorEvent(event,model,option,index); }I'm surprised
QMouseEvent *me = static_cast<QMouseEvent*>(event);undercase QEvent::HoverMove: case QEvent::HoverEnter:works. if it does is just through some compiler magic. you should cast it to the correct type:QHoverEvent. Alsome->pos()works in Qt5 but you needme->position()for Qt6 -
Ok
@VRonin said in Add widget right aligned to a QTableWidget cell:
you should cast it to the correct type: QHoverEvent. Also me->pos() works in Qt5 but you need me->position() for Qt6
Will do it.
I have a question: in
createEditor, I just deleted the twoconnectlines and signals are still being emitted. So what is the purpose of creating theMyButtonobject there? -
I'm surprised
QMouseEvent *me = static_cast<QMouseEvent*>(event);undercase QEvent::HoverMove: case QEvent::HoverEnter:works. if it does is just through some compiler magic. you should cast it to the correct type:QHoverEvent. Alsome->pos()works in Qt5 but you needme->position()for Qt6@VRonin said in Add widget right aligned to a QTableWidget cell:
Also me->pos() works in Qt5 but you need me->position() for Qt6
here
QRect::containstakes aQPointbutme->positionis aQPointF, I will have to do some casting.Edit: I will just use
QPointF::toPoint -
@VRonin said in Add widget right aligned to a QTableWidget cell:
Also me->pos() works in Qt5 but you need me->position() for Qt6
here
QRect::containstakes aQPointbutme->positionis aQPointF, I will have to do some casting.Edit: I will just use
QPointF::toPoint@hbatalha said in Add widget right aligned to a QTableWidget cell:
here QRect::contains takes a QPoint but me->position is a QPointF, I will have to do some casting.
Just call
.toPoint()seeTableWidgetDelegate::viewportEventI have a question: in createEditor, I just deleted the two connect lines and signals are still being emitted. So what is the purpose of creating the MyButton object there?
Those 2 lines deal with when the user is actually editing. In my original example the delegate was the default editor (usually a lineedit) + a button on the side. The default editor is used to change the contents of the cell. If the user starts editing the cell (doubleclick by default) the editor will pop up and you'll see that clicking on the buttons will not do anything unless you have those 2
connects.
In a nutshell we have to handle 2 cases: when the delegate is just painting and when the editor is presented to the user -
@hbatalha said in Add widget right aligned to a QTableWidget cell:
here QRect::contains takes a QPoint but me->position is a QPointF, I will have to do some casting.
Just call
.toPoint()seeTableWidgetDelegate::viewportEventI have a question: in createEditor, I just deleted the two connect lines and signals are still being emitted. So what is the purpose of creating the MyButton object there?
Those 2 lines deal with when the user is actually editing. In my original example the delegate was the default editor (usually a lineedit) + a button on the side. The default editor is used to change the contents of the cell. If the user starts editing the cell (doubleclick by default) the editor will pop up and you'll see that clicking on the buttons will not do anything unless you have those 2
connects.
In a nutshell we have to handle 2 cases: when the delegate is just painting and when the editor is presented to the user@VRonin said in Add widget right aligned to a QTableWidget cell:
Those 2 lines deal with when the user is actually editing. In my original example the delegate was the default editor (usually a lineedit) + a button on the side. The default editor is used to change the contents of the cell. If the user starts editing the cell (doubleclick by default) the editor will pop up and you'll see that clicking on the buttons will not do anything unless you have those 2 connects.
In a nutshell we have to handle 2 cases: when the delegate is just painting and when the editor is presented to the userGot it, thanks.
Could you please answer me here?
-
@VRonin
I hope I can hijack this thread now that the OP has had his questions answered?I know the crusade you are on. And I have to respect that the OP may have a lot of items, I don't know. But would you admit that --- without starting a flame-war --- this is one hell of a lot of code to add for a delegate approach if
setCellWidget()would have done the job?
