Unsolved Changing an image using QStyledItemDelegate in TableView
-
Separate the editor from the delagate.
Editor (imagepickbutton.h):
#include <QWidget> #include <QPushButton> #include <QIcon> #include <QFileDialog> class ImagePickButton : public QWidget{ Q_OBJECT Q_DISABLE_COPY(ImagePickButton) public: explicit ImagePickButton(QWidget* parent=Q_NULLPTR):QWidget(parent){ m_mainButton=new QPushButton(tr("Select Image"),this); QHBoxLayout* mainLay=new QHBoxLayout(this); mainLay->addWidget(m_mainButton); connect(m_mainButton,&QPushButton::clicked,this,&ImagePickButton::selectImageFile); } const QString& selectedFile() const {return m_selectedFile;} void setSelectedFile(const QString& val){ if(m_selectedFile==val) return; m_selectedFile=val; if(m_selectedFile.isEmpty()){ m_mainButton->setText(tr("Select Image")); m_mainButton->setIcon(QIcon()); } else{ m_mainButton->setText(QString()); m_mainButton->setIcon(QIcon(m_selectedFile)); } emit selectedFileChanged(m_selectedFile); } protected slots: void selectImageFile(){ const QString newFile=QFileDialog::getOpenFileName(this, tr("Open Image"), QString(), tr("Images (*.png *.xpm *.jpg)")); if(!newFile.isEmpty()) setSelectedFile(newFile); } signals: void selectedFileChanged(const QString& fileName); private: QString m_selectedFile; QPushButton* m_mainButton; };
Delegate (imagedelegate.h):
#include "imagepickbutton.h" #include <QStyledItemDelegate> #include <QModelIndex> class ImageDelegate : public QStyledItemDelegate{ Q_OBJECT Q_DISABLE_COPY(ImageDelegate) public: explicit ImageDelegate (QObject *parent = Q_NULLPTR) : QStyledItemDelegate(parent){} virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{ Q_UNUSED(option); Q_UNUSED(index); return new ImagePickButton(parent); } virtual void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE{ ImagePickButton* imgPick = qobject_cast<ImagePickButton*>(editor); Q_ASSERT(imgPick); imgPick->setSelectedFile(index.data(Qt::UserRole).toString()); } virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE{ ImagePickButton* imgPick = qobject_cast<ImagePickButton*>(editor); Q_ASSERT(imgPick); if(imgPick->selectedFile().isEmpty()){ model->setData(index,QVariant(),Qt::UserRole); model->setData(index, QVariant(),Qt::DecorationRole); }else{ model->setData(index,imgPick->selectedFile(),Qt::UserRole); model->setData(index, QIcon(imgPick->selectedFile()),Qt::DecorationRole); } } virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE{ Q_UNUSED(index); editor->setGeometry(option.rect); } };
-
@VRonin
Thank you. I'm supposed to do this in the cpp file right? -
Those are two header files but you could (and should, for efficiency) split them in .h and .cpp
-
@VRonin
I've started to implement it. I created a c++ class with .h and .cpp for the editor.
My .h file looks like this:#ifndef IMAGEPICKBUTTON_H #define IMAGEPICKBUTTON_H #include <QObject> #include <QWidget> #include <QPushButton> #include <QHBoxLayout> class ImagePickButton : public QWidget { Q_OBJECT Q_DISABLE_COPY (ImagePickButton) public: explicit ImagePickButton(QWidget *parent = Q_NULLPTR); const QString& selectedFile() const { return m_selectedFile; } void setSelectedFile(const QString& val); protected slots: void selectImageFile(); signals: void selectedFileChanged(const QString& fileName); private: QString m_selectedFile; QPushButton* m_mainButton; }; #endif // IMAGEPICKBUTTON_H
My .cpp file:
#include "imagepickbutton.h" ImagePickButton::ImagePickButton(QWidget *parent) : QWidget(parent) { m_mainButton = new QPushButton(tr("Select Image"),this); QHBoxLayout* mainLay = new QHBoxLayout(this); mainLay->addWidget(m_mainButton); connect(m_mainButton,&QPushButton::clicked,this,&ImagePickButton::selectImageFile); void setSelectedFile(const QString& val) { if(m_selectedFile == val) return; m_selectedFile = val; if(m_selectedFile.isEmpty()) { m_mainButton->setText(tr("Select Image")); m_mainButton->setIcon(QIcon()); } else { m_mainButton->setText(QString()); m_mainButton->setIcon(QIcon(m_selectedFile)); } emit selectedFileChanged(m_selectedFile); } }
It gives me an error message for void setSelectedFile(const QString& val) saying that function definition is not allowed here. Am I supposed to leave it in the .h?
-
@gabor53 You have added
setSelectedFile
definition inside the constructor. Move it outside. -
How can I link the new imagepickbutton to the existing mydelegate class?
In myDelegate.cpp I currently have:if((index.column () == 3))//image { QPushButton *button = new QPushButton(parent) ; button->setMaximumWidth (100); button->setStyleSheet ("background-color: rgba(255, 255, 255, 0);"); return button; }
-
Hi,
The same way you do for other classes, include the header file and use the class.
-
@SGaist
I'm still struggling linking the new code to the existing mydelegate.cpp.
If I connect the editor from imagepickbutton like this:ImagePickButton *myImagePickButton = new ImagePickButton; myImagePickButton->ImagePickButton::setSelectedFile(QString &val);
I get 2 error messages:
- val is not declared in this scope
- expected primary expression '&' token
How and where should I connect the new code? I should connect the whole class somewhere or I have to use each function separately? In the second case how can I access the first few lines of imagepickupbutton:
ImagePickButton::ImagePickButton(QWidget *parent) : QWidget(parent) { m_mainButton = new QPushButton(tr("Select Image"),this); QHBoxLayout* mainLay = new QHBoxLayout(this); mainLay->addWidget(m_mainButton); connect(m_mainButton, &QPushButton::clicked, this, &ImagePickButton::selectImageFile); } `` which creates the actual button? Thank you for your help.
-
I posted the complete code above that takes care of everything, just use that one.
if((index.column () == 3))
The delegate should never have this kind of code inside. The delegate should not depend on the model. use
setItemDelegateForColumn
in the view to obtain that result -
@VRonin
When I try to implement your code I end up with different error messages.
If I leave everything in the .h file like this:virtual void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE { ImagePickButton* imgPick = qobject_cast<ImagePickButton*>(editor); Q_ASSERT(imgPick); if(imgPick->selectedFile().isEmpty()) { model->setData(index,QVariant(),Qt::UserRole); model->setData(index, QVariant(),Qt::DecorationRole); } else { model->setData(index,imgPick->selectedFile(),Qt::UserRole); model->setData(index, QIcon(imgPick->selectedFile()),Qt::DecorationRole); } } virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE { ImagePickButton* imgPick = qobject_cast<ImagePickButton*>(editor); Q_ASSERT(imgPick); if(imgPick->selectedFile().isEmpty()) { model->setData(index,QVariant(),Qt::UserRole); model->setData(index, QVariant(),Qt::DecorationRole); } else { model->setData(index,imgPick->selectedFile(),Qt::UserRole); model->setData(index, QIcon(imgPick->selectedFile()),Qt::DecorationRole); } }
I get an error message saying expected token ';' got 'const'. If I move the definition to the cpp file I get an error saying unexpected token ImagePickButton. What am I doing incorrectly? Thank you
-
Looks like you forgot some
#include
s, added them to my original post -
@VRonin
I have in the .h file:virtual void setEditorData(QWidget *editor, const QModelIndex &index);
In the .cpp:
void ImageDelegate::setEditorData(QWidget *editor, const QModelIndex &index) { ImagePickButton* imgPick = qobject_cast<ImagePickButton*>(editor); Q_ASSERT(imgPick); imgPick->setSelectedFile(index.data(Qt::UserRole)); }
It gives me the following error message:
C:\Programming\Projects\Folkfriends_1_0\imagedelegate.cpp:18: error: no matching function for call to 'ImagePickButton::setSelectedFile(QVariant)'
imgPick->setSelectedFile(index.data(Qt::UserRole));^
C:\Programming\Projects\Folkfriends_1_0\imagedelegate.h:4: In file included from ..\Folkfriends_1_0\imagedelegate.h:4:0,
C:\Programming\Projects\Folkfriends_1_0\imagedelegate.cpp:1: from ..\Folkfriends_1_0\imagedelegate.cpp:1:
C:\Programming\Projects\Folkfriends_1_0\imagepickbutton.h:18: candidate: void ImagePickButton::setSelectedFile(const QString&)
void setSelectedFile(const QString& val);
^
C:\Programming\Projects\Folkfriends_1_0\imagepickbutton.h:18: note: no known conversion for argument 1 from 'QVariant' to 'const QString&'What should I change? Everything else builds correctly.
-
@gabor53
setSelectedFile
expects aQString
parameter so convert theQVariant
returned bydata
toQString
. -
easy fix
imgPick->setSelectedFile(index.data(Qt::UserRole).toString());
-
@VRonin
Thank you. This part works now. I addedImagePickButton *myImagePickButton = new ImagePickButton(this); myImagePickButton->show();
to mydeelgate.cpp createEditor.
When the user clicks on the image now it creates the button saying "Select Image". But this button is not on the view; it is separated from it and appears in the top left corner of the screen. How can I add it to the view so the button is on the actual image the user is trying to change? Thank you.
-
do not call
show()
just return that widget from that method. Again I'd advise you just to use the code I posted above even for the delegate -
@VRonin
I definitely want to use the code you posted. Should I call the method returning the widget from myDelegate.cpp or from fixdb.cpp where the model is set up? Currently I have this:ui->tableView_Fix->setItemDelegate (new myDelegate);
This declares that the delegate will be myDelegate from now on for everything. Can this work somehow ?
ui->tableView_Fix->setItemDelegateForColumn(3, new ImageDelegate); ui->tableView_Fix->setItemDelegate (new myDelegate);
Saying that if column 3 is chosen go with ImageDelegate; all other cases myDelegate.
It this is the way, what is missing? Currently it opens a text window in column 4). Thank you. -
do it the other way around and do not leak memory:
ui->tableView_Fix->setItemDelegate (new myDelegate(this)); ui->tableView_Fix->setItemDelegateForColumn(3, new ImageDelegate(this));
-
This post is deleted! -
@VRonin
So far I came across 2 problems.- If I add
ui->tableView_Fix->setItemDelegateForColumn(3, new ImageDelegate(this));
to fixdb.cpp , nothing happens, because myDelegate.cpp evaluates which cell the user clicked on. I can add
return new ImagePickButton(parent);
to myDelegate createEditor and it returns the button, but I couldn't figure out how to connect imageDelegate to the existing code. Please advise which is the best solution.
- The button is about 10px smaller on the top and on the left than the original cell. How can I make it bigger to cover the size of the original cell (originally its 100 x 100). It doesn't respond to any of the size settings.
Thank you for your help.