PDA

View Full Version : QItemDelegate with QTextEdit



caster89
24th April 2013, 14:01
Hi everyone,
I'm fairly new to Qt so i'm pretty sure i'm taking the long way around to solv a very simple problem. Anyhow, what I want to do is to obtain is a QListWidget in which under every image is a description of the image, which can be edited in a QTextBox, thus allowing multiple lines and wordwrap.
What I did was to create a new QItemDelegate subclassing from QStyledItemDelegate (ItemDelegate), and then I created a editor subclassing from QWidget (DelegateWidget).
The problem I am having right now is that when I pass the QModelIndex to the DelegateWidget, I can't get the Image from the DecorativeRole.
Can someone tell me what I'm doing wrong, or if there is an easier way, as I really think I'm just making a mess.
P.S.
I was actually thinking I might just create a ScrollArea from scratch and just add the images and text to the ScrollArea, but then I'd probably hav to reimplement the dragging etc.

QItemDelegate


ItemDelegate::ItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}

QWidget *ItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index ) const
{


return new DelegateWidget(parent, index);
}

void ItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{

static_cast<DelegateWidget*>(editor)->setText(index.data(Qt::EditRole).toString());
}

void ItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const
{

QString value=static_cast<DelegateWidget*>(editor)->GetText();

model->setData(index, value, Qt::EditRole);
}

void ItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
static_cast<DelegateWidget*>(editor)->setGeometry(option.rect);
}



DelegateWidget


DelegateWidget::DelegateWidget(QWidget *parent, const QModelIndex &index) :
QWidget(parent)
,TextEditBox(new QTextEdit)
,mText(index.data().toString())
,ImgDisplay(new QLabel)
{

QVBoxLayout *vLayout =new QVBoxLayout(this);
QPixmap px=static_cast<QPixmap>(index.data(Qt::DecorationRole));
ImgDisplay->setPixmap(px);
mText=index.data(Qt::EditRole).toString();
TextEditBox->setText(mText);
vLayout->addWidget(ImgDisplay);
vLayout->addWidget(TextEditBox);
}

QString DelegateWidget::GetText() const {
return mText;
}


void DelegateWidget::setText(const QString & text){
mText=text;
TextEditBox->setText(text);
}

void DelegateWidget::updateText(void){
mText = TextEditBox->toPlainText();
//emit textChanged(mText);
}


Main


int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QWidget window;

QLabel* title = new QLabel("Custom widgets on a QListWidget");
title->setAlignment(Qt::AlignHCenter);

QListWidget* list = new QListWidget;
list->setItemDelegate(new ItemDelegate(list));
list->setViewMode(QListView::IconMode);
list->setIconSize(QSize(200,200));
list->setWrapping(false);


QListWidgetItem *first = new QListWidgetItem();
first->setText("1");
first->setIcon(QPixmap("/Users/Nick/Desktop/chocolate.jpeg"));
first->setFlags (first->flags () | Qt::ItemIsEditable);
list->addItem(first);

QListWidgetItem *second = new QListWidgetItem();
second->setText("2");
second->setIcon(QIcon("/Users/Nick/Desktop/chocolate.jpeg"));
second->setFlags (second->flags () | Qt::ItemIsEditable);
list->addItem(second);

QVBoxLayout* layout = new QVBoxLayout(&window);
layout->addWidget(title);
layout->addWidget(list);
window.setLayout(layout);

window.show();

return a.exec();
}


I got most of this code from another post somewhere in the forum (can't find it anymore though)
Thank you

Santosh Reddy
24th April 2013, 14:56
Qt::DecorationRole returns QIcon (in the QVariant) not QPixmap, the correct way will be


...
QPixmap px = qvariant_cast<QIcon>(index.data(Qt::DecorationRole)).pixmap(200);
...

caster89
24th April 2013, 17:50
What a coincidence, you are the person who posted the code in the first place!!!
Thank you very much, I had already tried that but for some reason I had uses static_cast instead of qvariant_cast.
Is there any way to get the original size of the image in the QListWidget, so I can get the editor to cover the item while editing (kind of like when I rename a file, so the QTextEdit would just expand below).
Furthermore whenever I de-comment


...
emit textChanged(mText);
...

I get the following error:
symbol(s) not found for architecture x86_64
Even though I'm not sure why you implemented that method in the first place. I think it's needed to update the model data but i'm not sure how the two are connected. I've been reading around and if i'm not mistaken I should be the equivalent of the finishedEditing() which I should then connect to the commitAndClose() (parallel taken fromt the track editor example)
Could you expand a bit on that part.
Thank you very much

Santosh Reddy
24th April 2013, 18:57
What a coincidence, you are the person who posted the code in the first place!!!
The post you took the code from has sightly different requirement, take that into account.


Is there any way to get the original size of the image in the QListWidget, so I can get the editor to cover the item while editing (kind of like when I rename a file, so the QTextEdit would just expand below).
Use QIcon::availableSizes(), and take the first size.


Furthermore whenever I de-comment


...
emit textChanged(mText);
...

I get the following error:
symbol(s) not found for architecture x86_64
Make sure you use Q_OBJECT macro as the first line in the class definition, and make sure you have declared that function as a Qt signal.


Even though I'm not sure why you implemented that method in the first place. I think it's needed to update the model data but i'm not sure how the two are connected.
As I said earlier, the code was posted for a different requirement, the requirement was not to save the data into the model until the user clicks the save/apply pushButton explicitly. That is the reason updateText() slot and textChanged(QString) signal were suggested, and as such textChanged() was only provided to extend the functionality if required and has nothing to do with model/view, so just ignore it.

caster89
25th April 2013, 14:45
Hi,
I re-wrote the class to make sure I had understood how everything worked and to make sure there were no errors, and was able to get everything to work except for emitting the commitData fromt he QStyledItemDelegate. For some reason if I add the Q_OBJECT macro at the beginning of the definition I get a "symbol(s) not found for architecture x86_64" error, while if I do not add it it tells me "No such slot QStyledItemDelegate::FinishedEditing.
Header File


#ifndef NEWITEMDELEGATE_H
#define NEWITEMDELEGATE_H
#include <QStyledItemDelegate>
#include <QWidget>
#include <QObject>

class newItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
newItemDelegate(QObject *parent = 0);

QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const;

void setEditorData(QWidget *editor, const QModelIndex &index) const;

void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const;

void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;

private slots:
void FinishedEditing();

};

#endif // NEWITEMDELEGATE_H


CPP File


#include "newitemdelegate.h"
#include "newdelegatewidget.h"
#include <QStyledItemDelegate>
#include <QWidget>

newItemDelegate::newItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}

QWidget * newItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const
{

newDelegateWidget *editWidget = new newDelegateWidget(parent, index);
connect (editWidget, SIGNAL(lostFocus()),this, SLOT(FinishedEditing()));
return new newDelegateWidget(parent, index);
}

void newItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
static_cast<newDelegateWidget*>(editor)->SetText(index.data(Qt::EditRole).toString());
}

void newItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const
{
QString value=static_cast<newDelegateWidget*>(editor)->GetText();
model->setData(index, value, Qt::EditRole);
}

void newItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
static_cast<newDelegateWidget*>(editor)->setGeometry(option.rect);
}


void newItemDelegate::FinishedEditing()
{
newDelegateWidget *editor = qobject_cast<newDelegateWidget *>(sender());
emit commitData(editor);
emit closeEditor(editor);
}


If i'm not mistaken emitting the commitData should change the value of QModelIndex in the QListWidget

Santosh Reddy
25th April 2013, 14:54
If i'm not mistaken emitting the commitData should change the value of QModelIndex in the QListWidget
NO. Edited data has to be saved into the Model (QListWidget's Model) in setModelData(), and additional signal / slots are not required.

caster89
25th April 2013, 15:03
Ok, so if I connect the signal the widget I made emits when it looses focus, to the slot setModelData() like this:


...
QWidget * newItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const
{

newDelegateWidget *editWidget = new newDelegateWidget(parent, index);
connect (editWidget, SIGNAL(lostFocus()),this, SLOT(setModelData(editWidget,option,index)));
return new newDelegateWidget(parent, index);
}
...

whenever the widget loosed focus it should call the setModelData, and update the information. Still, when I add Q_OBJECT i get the symbols error, while if the macro is not added I get the slot (setModelData()) does not exist. What am I missing?

Santosh Reddy
25th April 2013, 17:48
Ok, so if I connect the signal the widget I made emits when it looses focus, to the slot setModelData() like this:
First thing setModelData() is not a slot, so you cannot connect it to a signal.
Second, you need not call setModelDataa(), view will call it when you editing is finished.

So I repeat, REMOVE SIGNALS AND SLOTS, YOU DON'T NEED THEM, THEY WERE ADDED FOR SOME OTHER REQUIREMENT, this I mentioned in my earlier two posts.

caster89
25th April 2013, 18:16
Ok, so I had seen the post, but even removing the signals I still couldn't get the model to update when I finished editing, that is why I thought I had to reput the signals. I then realised the problem was that getText() I was returning the QString which did not update, by returning QTextEdit::toPlainText the data is updated.
Furthermore the reason i got the error when adding Q_OBJECT was that I forgot to delete the moc file, after that I didn't get the error anymore.
Thank you for your help