How can I add custom widget to QListView?
-
Hello. I have added a QListWidget in the main window of my program. I then created a class that inherits from QWidget. Using a separate dialog box, I created new objects and added them to QListWidget. Everything was fine until I realised that when I tried to change the order of the elements in QListWidget, the contents of the individual elements were being deleted. I did some research on the internet and found that in order to create a list of widgets that I would want to edit (reorder) to some extent, it was best to use QListView + Model/View + Delegate.
I checked out this example: https://doc.qt.io/qt-6/qtwidgets-itemviews-stardelegate-example.html
Unfortunately I didn't understand too much from it. I created my own (working) model and delegate,but I still don't know how to draw my widget. Below the model and delegate:
model.h
#ifndef OPERATIONWIDGETMODEL_H #define OPERATIONWIDGETMODEL_H #include <QAbstractItemModel> class OperationWidgetModel : public QAbstractItemModel { Q_OBJECT public: explicit OperationWidgetModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; }; #endif // OPERATIONWIDGETMODEL_H
model.cpp
#include "operationwidgetmodel.h" OperationWidgetModel::OperationWidgetModel(QObject *parent) : QAbstractItemModel(parent) { // } QVariant OperationWidgetModel::data(const QModelIndex &index, int role) const { return QVariant(); } QModelIndex OperationWidgetModel::index(int row, int column, const QModelIndex &parent) const { QModelIndex index; index = createIndex(row,column,nullptr); return index; } QModelIndex OperationWidgetModel::parent(const QModelIndex &index) const { return QModelIndex(); } int OperationWidgetModel::rowCount(const QModelIndex &parent) const { if(parent.isValid())return 0; return 1; } int OperationWidgetModel::columnCount(const QModelIndex &parent) const { if(parent.isValid()) return 0; return 1; }
delegate.h
#ifndef OPERATIONWIDGETDELEGATE_H #define OPERATIONWIDGETDELEGATE_H #include <QStyledItemDelegate> #include "operationwidget.h" class OperationWidgetDelegate : public QStyledItemDelegate { Q_OBJECT public: OperationWidgetDelegate(QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; }; #endif // OPERATIONWIDGETDELEGATE_H
delegate.cpp
#include "operationwidgetdelegate.h" OperationWidgetDelegate::OperationWidgetDelegate(QObject *parent) { } void OperationWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyledItemDelegate::paint(painter,option,index); } QSize OperationWidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { OperationWidget widget; return QSize(widget.geometry().width(),widget.geometry().height()); } QWidget *OperationWidgetDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { OperationWidget *widget = new OperationWidget(parent); return widget; } void OperationWidgetDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { OperationWidget *widget = qobject_cast<OperationWidget*>(editor); if(widget) { widget->SetOperationName("Cięcie"); } } void OperationWidgetDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { OperationWidget *widget = qobject_cast<OperationWidget*>(editor); if(widget) { model->setData(index,widget->GetOperationName(),Qt::EditRole); } }
mainwindow.cpp
OperationWidgetModel *model = new OperationWidgetModel(this); ui->listView->setModel(model); OperationWidgetDelegate *delegate= new OperationWidgetDelegate(this); ui->listView->setItemDelegate(delegate); ui->listView->show();
Below is my widget that I want to add to the QListView:
And QListWidget before I started working with QListView:
For any guidance, I would be very grateful.
Have a good day
-
@BushyAxis793 said in How can I add custom widget to QListView?:
QRect boundRect = QRect(0,00,275,70);
You must set the origin with option.rect.
Look at the Y position : qDebug()<<option.rect.y();see an example Here
-
-
Since it's only text that you are showing, you could simply paint them at the correct position. It would make your code simpler and lighter.
-
Thanks for the answer. I was able to add the widget to the QListView. But another problem arose. When adding a new item it gets "drawn" in place of the previous one. How do I fix this?
-
It's not really clear. Can you show some picture of what is happening ?
-
Below I upload the QListView before adding the new item:
After adding the first element, the QListView looks like this:
After adding the second:
Below is my AddItem function:
void OperationWidgetModel::AddItem(OperationWidget *item) { beginInsertRows(QModelIndex(),widgetList.count(),widgetList.count()); widgetList.append(item); endInsertRows(); }
I don't know why, but the next element overlaps the previous one.
-
@BushyAxis793
Show your paint() code,
you probably don't use option.rect to get the right position. -
Here is my paint():
void OperationWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if(!index.isValid()) { return; } painter->save(); if(option.state & QStyle::State_Selected) { painter->fillRect(option.rect,option.palette.highlight()); } OperationWidget* widget = index.data().value<OperationWidget*>(); QRect boundRect = QRect(0,00,275,70); painter->setPen(QPen(Qt::black,0.5,Qt::SolidLine)); painter->drawRect(boundRect); QRect operationNameRect = QRect(0,10,275,70); QRect operationToolsRect = QRect(10,40,275,70); QRect operationTimeRect = QRect(-10,40,275,70); QFont bold("Roboto",10); bold.setBold(true); painter->setFont(bold); painter->setPen(Qt::black); painter->drawText(operationNameRect,Qt::AlignTop | Qt::AlignHCenter,widget->GetOperationName()); painter->drawText(operationToolsRect,Qt::AlignLeft,QString::number(widget->GetToolsQuantity())+ " [szt.]"); painter->drawText(operationTimeRect,Qt::AlignRight,QString::number(widget->GetOperationTime())+ " [min.]"); }
-
@BushyAxis793 said in How can I add custom widget to QListView?:
QRect boundRect = QRect(0,00,275,70);
You must set the origin with option.rect.
Look at the Y position : qDebug()<<option.rect.y();see an example Here
-
I think you need to implement sizeHint() as well.
As @SGaist noted, your code is confusing :)
Can you post a skech of what you want to do, is it a single list of text cells ?
-
Let me describe as simply as I can. I have added a QListWidget in the main window of my program. I created a new class called OperationWidget, which inherits from QWidget. It has all the methods and variables it needs. In an additional QDialog window, I set the parameters of the OperationWidget object and then add it to QListWidget. I can add and remove items from QListWidget, but I cannot change the position of the items. So I have to use QListView + OperationWidgetModel + OperationWidgetDelegate. I have never worked with models and delegates, but I know there is no other way. I want the whole thing to look like the QListWidget.
OperationWidget.h
#ifndef OPERATIONWIDGET_H #define OPERATIONWIDGET_H #include <QWidget> namespace Ui { class OperationWidget; } class OperationWidget : public QWidget { Q_OBJECT public: explicit OperationWidget(QWidget *parent = nullptr); ~OperationWidget(); void SetOperationName(const QString&); QString GetOperationName()const; void SetWorkplaceName(const QString&); QString GetWorkplaceName()const; void SetToolQuantity(const int&); int GetToolsQuantity()const; void SetOperationTime(const double&); double GetOperationTime() const; void SetOperationDescription(const QString&); QString GetOperationDescription()const; void SetPreparationAndCompletionTime(const int&); int GetPreparationAndCompletionTime()const; void SetWorkplaceNumber(const QString&); QString GetWorkplaceNumber()const; void SetWorkplaceCost(const int&); int GetWorkplaceCost()const; void SetUnitTime(const int&); int GetUnitTime()const; void LoadToolTip(); private: Ui::OperationWidget *ui; QString operationName; QString workplaceName; int toolsQuantity; double operationTime; QString operationDescription; int preparationAndCompletionTime; QString workplaceNumber; int workplaceCost; int unitTime; }; #endif // OPERATIONWIDGET_H
OperationWidget.cpp
#include "operationwidget.h" #include "ui_operationwidget.h" #include <QFrame> OperationWidget::OperationWidget(QWidget *parent) : QWidget(parent), ui(new Ui::OperationWidget) { ui->setupUi(this); } OperationWidget::~OperationWidget() { delete ui; } void OperationWidget::SetOperationName(const QString &name) { operationName = name; ui->operationNameLabel->setText(name); } QString OperationWidget::GetOperationName() const { return operationName; } void OperationWidget::SetWorkplaceName(const QString &name) { workplaceName = name; } QString OperationWidget::GetWorkplaceName() const { return workplaceName; } void OperationWidget::SetToolQuantity(const int &tools) { toolsQuantity = tools; ui->toolsQuantityLabel->setText(QString::number(tools)+ " "+"[szt.]"); } int OperationWidget::GetToolsQuantity() const { return toolsQuantity; } void OperationWidget::SetOperationTime(const double &time) { operationTime = time; ui->timeOperationLabel->setText(QString::number(GetOperationTime())+ " "+ "[min.]"); } double OperationWidget::GetOperationTime() const { return operationTime; } void OperationWidget::SetOperationDescription(const QString &description) { operationDescription = description; } QString OperationWidget::GetOperationDescription() const { return operationDescription; } void OperationWidget::SetPreparationAndCompletionTime(const int &tpz) { preparationAndCompletionTime = tpz; } int OperationWidget::GetPreparationAndCompletionTime() const { return preparationAndCompletionTime; } void OperationWidget::SetWorkplaceNumber(const QString &number) { workplaceNumber = number; } QString OperationWidget::GetWorkplaceNumber() const { return workplaceNumber; } void OperationWidget::SetWorkplaceCost(const int &cost) { workplaceCost = cost; } int OperationWidget::GetWorkplaceCost() const { return workplaceCost; } void OperationWidget::SetUnitTime(const int &unitT) { unitTime = unitT; } int OperationWidget::GetUnitTime() const { return unitTime; } void OperationWidget::LoadToolTip() { this->setToolTip("Nazwa stanowiska: "+GetWorkplaceName()+"\n"+ "Nazwa operacji: "+GetOperationName()+ "\n"+ "Numer stanowiska: "+GetWorkplaceNumber()+"\n"+ "Koszt stanowiska: "+QString::number(GetWorkplaceCost())+ " [zł]"+"\n"+ "Czas przygotowawczy: "+QString::number(GetPreparationAndCompletionTime())+" [min.]"+"\n"+ "Czas jednostkowy: "+QString::number(GetUnitTime())+" [min.]"+"\n"+ "Ilość narzędzi: "+QString::number(GetToolsQuantity())+"\n"+ "Czas operacji: "+QString::number(GetOperationTime())+" [min.]"+"\n"+ "Opis operacji: "+GetOperationDescription()); }
QListWidget in mainwindow:
Put simply. I want to add my own widget to the QListView.
-
As written in the documentation and here as well: setCellWidget should only be used to show some static content not as a replacement of a properly implemented delegate.
There's no need for that widget of yours unless it's an editor.
You really should take the time to build a proper model to store your data. It will be a table of some sort. Which you can use with a QListView.
Basically, your delegate will have to pull the data from the model when drawing. You will essentially have to position the text properly. There really isn't more to it than that.
-
-
I found the solution. The problem was in paint(). Now it looks much better
Thanks everyone for help!
Unfortunatelly, I found another issue. In slot when I add my widget to list:
void QueriesCreator::ReceiveOperationData(QString workplaceName, QString operationName, int tools, double time, QString description,int tpz, QString number, int cost, int unitT) { OperationWidget* operationWidget = new OperationWidget(new QWidget); operationWidget->SetWorkplaceName(workplaceName); operationWidget->SetOperationName(operationName); operationWidget->SetToolQuantity(tools); operationWidget->SetOperationTime(time); operationWidget->SetOperationDescription(description); operationWidget->SetPreparationAndCompletionTime(tpz); operationWidget->SetWorkplaceNumber(number); operationWidget->SetWorkplaceCost(cost); operationWidget->SetUnitTime(unitT); operationWidget->LoadToolTip(); model.AddItem(operationWidget); }
As you can see I call LoadToolTip() method:
void OperationWidget::LoadToolTip() { this->setToolTip("Nazwa stanowiska: "+GetWorkplaceName()+"\n"+ "Nazwa operacji: "+GetOperationName()+ "\n"+ "Numer stanowiska: "+GetWorkplaceNumber()+"\n"+ "Koszt stanowiska: "+QString::number(GetWorkplaceCost())+ " [zł]"+"\n"+ "Czas przygotowawczy: "+QString::number(GetPreparationAndCompletionTime())+" [min.]"+"\n"+ "Czas jednostkowy: "+QString::number(GetUnitTime())+" [min.]"+"\n"+ "Ilość narzędzi: "+QString::number(GetToolsQuantity())+"\n"+ "Czas operacji: "+QString::number(GetOperationTime())+" [min.]"+"\n"+ "Opis operacji: "+GetOperationDescription()); }
But I can see any tooltip when I move cursor over widget. How can I enable it? Any ideas?
-
-
A delegate does not handle tooltips, you should return them through the model for the
Qt::ToolTipRole
role.