Problem while applying filter on QTableView with a button column
-
wrote on 17 Oct 2019, 08:36 last edited by Lati
Dear QT community,
I have a QTableView which's last column consists of buttons (pictures are at the end of the post).
I am applying filtering on the first column using QSortFilterProxyModel basically using the method described here (https://stackoverflow.com/questions/17044688/qtableview-real-time-filtering).
Filtering works well, however, when the filter is removed, buttons on the last column disappears . What might be the issue here and how can I refresh the last column after filter is removed to have the buttons back?
Thanks for your responses in advance!
-
Dear QT community,
I have a QTableView which's last column consists of buttons (pictures are at the end of the post).
I am applying filtering on the first column using QSortFilterProxyModel basically using the method described here (https://stackoverflow.com/questions/17044688/qtableview-real-time-filtering).
Filtering works well, however, when the filter is removed, buttons on the last column disappears . What might be the issue here and how can I refresh the last column after filter is removed to have the buttons back?
Thanks for your responses in advance!
Before filtering:
.
When filtered:
After filter is removed:
wrote on 17 Oct 2019, 08:40 last edited by@Lati
Since the example you reference does not have something like a button column, I think you should show minimal code for what you are doing. I would not expect you to have to take "special action", the buttons should either be there or not, it sounds like something is wrong in your "After filter is removed:" code? -
@Lati
Since the example you reference does not have something like a button column, I think you should show minimal code for what you are doing. I would not expect you to have to take "special action", the buttons should either be there or not, it sounds like something is wrong in your "After filter is removed:" code?wrote on 17 Oct 2019, 08:54 last edited by@JonB Thanks Jon for such a quick response! :)
First, I am using QT Creator v4.10.0 and QT v5.13.1.
I update the model of the QTableView as:
QStandardItemModel *model = new QStandardItemModel(); // filter proxy model filter_proxy_model = new QSortFilterProxyModel(); filter_proxy_model->setSourceModel(model); filter_proxy_model->setFilterKeyColumn(0); // # first column ui->_tableView->setModel( filter_proxy_model ); ui->_tableView->show();
And filter using LineEdit_textChanged signal as:
void MainWindow::on_filtersetTableViewLineEdit_textChanged(const QString &arg1) { filter_proxy_model->setFilterRegExp(arg1); }
Last column's buttons are created as following:
auto item = filter_proxy_model->index(i, 6); QPushButton* addIntervalButton = new QPushButton("Add interval"); ui->_tableView->setIndexWidget(item, addIntervalButton);
This is actually everything related to the filtering and button column.
-
@JonB Thanks Jon for such a quick response! :)
First, I am using QT Creator v4.10.0 and QT v5.13.1.
I update the model of the QTableView as:
QStandardItemModel *model = new QStandardItemModel(); // filter proxy model filter_proxy_model = new QSortFilterProxyModel(); filter_proxy_model->setSourceModel(model); filter_proxy_model->setFilterKeyColumn(0); // # first column ui->_tableView->setModel( filter_proxy_model ); ui->_tableView->show();
And filter using LineEdit_textChanged signal as:
void MainWindow::on_filtersetTableViewLineEdit_textChanged(const QString &arg1) { filter_proxy_model->setFilterRegExp(arg1); }
Last column's buttons are created as following:
auto item = filter_proxy_model->index(i, 6); QPushButton* addIntervalButton = new QPushButton("Add interval"); ui->_tableView->setIndexWidget(item, addIntervalButton);
This is actually everything related to the filtering and button column.
wrote on 17 Oct 2019, 08:59 last edited by JonB@Lati
But when/where are those "last column's buttons" created? If your "the filter is removed" is a change in the regular expression to empty, are you doing this code again at that instant, when the extra new rows appear? Put a debug in the code so you can see thei
loop creating for each row when needed. -
@Lati
But when/where are those "last column's buttons" created? If your "the filter is removed" is a change in the regular expression to empty, are you doing this code again at that instant, when the extra new rows appear? Put a debug in the code so you can see thei
loop creating for each row when needed.wrote on 17 Oct 2019, 09:09 last edited by@JonB Last column's buttons are created when all table is filled (yes, using a loop but code is long and not related). Filtering is done through "QSortFilterProxyModel", so table is not filled again while filtering or after filter is removed (or the text in the textbox is changed). Therefore I cannot debug in the loop when the table is filled where
i
exists, while or after filtering.I wonder if I need to refresh the table after filter is removed
-
@JonB Last column's buttons are created when all table is filled (yes, using a loop but code is long and not related). Filtering is done through "QSortFilterProxyModel", so table is not filled again while filtering or after filter is removed (or the text in the textbox is changed). Therefore I cannot debug in the loop when the table is filled where
i
exists, while or after filtering.I wonder if I need to refresh the table after filter is removed
wrote on 17 Oct 2019, 09:18 last edited by@Lati
I don't understand. The view is "filled again" when the filter changes, so the push buttons need to be created then.Separately, I don't know if
setIndexWidget()
is the right thing to do. https://doc.qt.io/qt-5/qabstractitemview.html#setIndexWidgetThis function should only be used to display static content within the visible area corresponding to an item of data. If you want to display custom dynamic content or implement a custom editor widget, subclass QStyledItemDelegate instead.
-
@Lati
I don't understand. The view is "filled again" when the filter changes, so the push buttons need to be created then.Separately, I don't know if
setIndexWidget()
is the right thing to do. https://doc.qt.io/qt-5/qabstractitemview.html#setIndexWidgetThis function should only be used to display static content within the visible area corresponding to an item of data. If you want to display custom dynamic content or implement a custom editor widget, subclass QStyledItemDelegate instead.
wrote on 17 Oct 2019, 09:28 last edited by@JonB It seems then the issue is related to the
setIndexWidget()
. I missed that setIndexWidget should only be used for static content and thank you for pointing this. I will update the code withQStyledItemDelegate
instead and see if it works.Thanks again.
-
@JonB It seems then the issue is related to the
setIndexWidget()
. I missed that setIndexWidget should only be used for static content and thank you for pointing this. I will update the code withQStyledItemDelegate
instead and see if it works.Thanks again.
-
-
wrote on 17 Oct 2019, 10:32 last edited by VRonin
Who summoned my whrath?!
In this case you have to fill a
QStyleOptionButton
in the delegate'spaint
method and simply react to theclicked
event of the view. It's just a bit tedious to type in the code but it's not complicated -
Who summoned my whrath?!
In this case you have to fill a
QStyleOptionButton
in the delegate'spaint
method and simply react to theclicked
event of the view. It's just a bit tedious to type in the code but it's not complicated -
wrote on 17 Oct 2019, 11:49 last edited by VRonin
Something like this
class ButtonDelegate : public QStyledItemDelegate { Q_OBJECT Q_DISABLE_COPY(ButtonDelegate) private: QStyleOptionButton prepareButtonOption(const QStyleOptionViewItem &option) const{ QStyleOptionButton buttonOption; buttonOption.text = tr("Add Interval"); buttonOption.rect = option.rect buttonOption.features = QStyleOptionButton::None; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.palette = option.palette; buttonOption.styleObject = option.styleObject; return buttonOption; } public: explicit ButtonDelegate(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(); QStyleOptionButton btnOption=prepareButtonOption(opt); style->drawControl(QStyle::CE_PushButton, &btnOption, painter, widget); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{ QStyleOptionButton btnOpt=prepareButtonOption(opt); const QSize fntSize = fontMetrics().size(Qt::TextShowMnemonic,btnOpt.text); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); return style->sizeFromContents(CT_PushButton,&btnOpt,fntSize,this); } }
-
Something like this
class ButtonDelegate : public QStyledItemDelegate { Q_OBJECT Q_DISABLE_COPY(ButtonDelegate) private: QStyleOptionButton prepareButtonOption(const QStyleOptionViewItem &option) const{ QStyleOptionButton buttonOption; buttonOption.text = tr("Add Interval"); buttonOption.rect = option.rect buttonOption.features = QStyleOptionButton::None; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.palette = option.palette; buttonOption.styleObject = option.styleObject; return buttonOption; } public: explicit ButtonDelegate(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(); QStyleOptionButton btnOption=prepareButtonOption(opt); style->drawControl(QStyle::CE_PushButton, &btnOption, painter, widget); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{ QStyleOptionButton btnOpt=prepareButtonOption(opt); const QSize fntSize = fontMetrics().size(Qt::TextShowMnemonic,btnOpt.text); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); return style->sizeFromContents(CT_PushButton,&btnOpt,fntSize,this); } }
-
@VRonin
That is indeed a lot of code compared against the originalsetIndexWidget(item, new QPushButton("..."));
! Makes me want to stick with that approach ;-)wrote on 17 Oct 2019, 13:07 last edited by VRonin@JonB said in Problem while applying filter on QTableView with a button column:
Makes me want to stick with that approach
Teasing eh?! Cheeky sod!
-
@JonB said in Problem while applying filter on QTableView with a button column:
Makes me want to stick with that approach
Teasing eh?! Cheeky sod!
wrote on 17 Oct 2019, 13:13 last edited by@VRonin
I'm not the one who is teasing! When I discovered them I was quite happy withsetIndexWidget()
and whatever other methods which let you set a widget in a table etc. Simple, easy to use. Then you came along and put the fear of God into me & anyone else who chooses to use this approach, saying we would run out of memory and be damned for eternity, be kicked out of the EU, etc. ;-) So now I wouldn't dare use them.... -
Something like this
class ButtonDelegate : public QStyledItemDelegate { Q_OBJECT Q_DISABLE_COPY(ButtonDelegate) private: QStyleOptionButton prepareButtonOption(const QStyleOptionViewItem &option) const{ QStyleOptionButton buttonOption; buttonOption.text = tr("Add Interval"); buttonOption.rect = option.rect buttonOption.features = QStyleOptionButton::None; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.palette = option.palette; buttonOption.styleObject = option.styleObject; return buttonOption; } public: explicit ButtonDelegate(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(); QStyleOptionButton btnOption=prepareButtonOption(opt); style->drawControl(QStyle::CE_PushButton, &btnOption, painter, widget); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{ QStyleOptionButton btnOpt=prepareButtonOption(opt); const QSize fntSize = fontMetrics().size(Qt::TextShowMnemonic,btnOpt.text); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); return style->sizeFromContents(CT_PushButton,&btnOpt,fntSize,this); } }
-
@VRonin
That is indeed a lot of code compared against the originalsetIndexWidget(item, new QPushButton("..."));
! Makes me want to stick with that approach ;-) -
Something like this
class ButtonDelegate : public QStyledItemDelegate { Q_OBJECT Q_DISABLE_COPY(ButtonDelegate) private: QStyleOptionButton prepareButtonOption(const QStyleOptionViewItem &option) const{ QStyleOptionButton buttonOption; buttonOption.text = tr("Add Interval"); buttonOption.rect = option.rect buttonOption.features = QStyleOptionButton::None; buttonOption.direction = option.direction; buttonOption.fontMetrics = option.fontMetrics; buttonOption.palette = option.palette; buttonOption.styleObject = option.styleObject; return buttonOption; } public: explicit ButtonDelegate(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(); QStyleOptionButton btnOption=prepareButtonOption(opt); style->drawControl(QStyle::CE_PushButton, &btnOption, painter, widget); } QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{ QStyleOptionButton btnOpt=prepareButtonOption(opt); const QSize fntSize = fontMetrics().size(Qt::TextShowMnemonic,btnOpt.text); const QWidget *widget = option.widget; QStyle *style = widget ? widget->style() : QApplication::style(); return style->sizeFromContents(CT_PushButton,&btnOpt,fntSize,this); } }
wrote on 22 Oct 2019, 08:52 last edited by Lati@VRonin It is implemented, working good. However, I have an issue. When
setItemDelegateForColumn
is used, all created buttons at the column have the same properties (the last row button's properties). How will I know the row number of the button I clicked?I create the buttons as following:
ButtonDelegateTableView * addIntervalButton = new ButtonDelegateTableView(ui->tableView, project, wellName, rowNumber);
And, when I clicked on the button at the first row, I get the properties (well name, row number etc.) of the last row buttons.
1/18