3

I have the exact same problem, but I will use the QTableView widget. I read this and was wondering if I can override the createEditor function to use for instance QFileDialog to get the new data.

If this is possible, can anyone provide me with an example to implement such a subclass to QItemDelegate.

And if not, can anyone provide me with an example to implement a subclass to QItemDelegate, witch can draw a button next to a QLineEdit to get the functionality here.

Edit: Maybe this question is really stupid and i dont realize, because I left the project for around half a year.

Second: Is it safe to update from Qt 5.7 to 5.8?

Community
  • 1
  • 1
Darkproduct
  • 1,062
  • 13
  • 28

2 Answers2

7

I have done my best and here is my solution. The code for the QStyledItemDelegate subclass is mostly from here.

Solution Picture

However, there is one things I am curious to fix: (maybe someone can help me and left a comment)

  • QPixmap::grabWidget is deprecated, use QWidget::grab() instead but it looks like QWidget::grab() is not the right solution for this purpose.

foo.h:

#ifndef LIBRARYITEMDELEGATE_H
#define LIBRARYITEMDELEGATE_H

#include <QStyledItemDelegate>
#include <QWidget>
#include <QPushButton>
#include <QTableView>

class LibraryItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    explicit LibraryItemDelegate(QObject *parent = 0);
    ~LibraryItemDelegate();

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
//    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void setModelData(QWidget *editor, QAbstractItemModel *modal, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;

public slots:
    void cellEntered(const QModelIndex &index);

private:
    QTableView *myView;
    QPushButton *btn;
    bool isOneCellInEditMode;
    QPersistentModelIndex currentEditedCellIndex;
};

#endif // LIBRARYITEMDELEGATE_H

foo.cpp:

#include "libraryitemdelegate.h"

#include <QPainter>
#include <QStylePainter>

LibraryItemDelegate::LibraryItemDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
    if(QTableView *tableView = qobject_cast<QTableView*>(parent))
    {
        myView = tableView;
        btn = new QPushButton("...", myView);
        btn->hide();
        myView->setMouseTracking(true);
        connect(myView, SIGNAL(entered(QModelIndex)), this, SLOT(cellEntered(QModelIndex)));
        isOneCellInEditMode = false;
    }
}

LibraryItemDelegate::~LibraryItemDelegate(){}

void LibraryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
    {
        btn->setGeometry(option.rect);
        btn->setText("...");
        if(option.state == QStyle::State_Selected)
        {
            painter->fillRect(option.rect, option.palette.highlight());
        }
        QPixmap map = QPixmap::grabWidget(btn);
        painter->drawPixmap(option.rect.x(), option.rect.y(), map);
    }
    else
    {
        QStyledItemDelegate::paint(painter, option, index);
    }
}

//QSize LibraryItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
//{
//    // return the QSize of the item in Your view
//}

QWidget *LibraryItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
    {
        QPushButton *btn = new QPushButton(parent);
//        btn->setText(index.data().toString());
        btn->setText("...");
        return btn;
    }
    else
    {
        return QStyledItemDelegate::createEditor(parent, option, index);
    }
}

void LibraryItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
    {
        QPushButton *btn = qobject_cast<QPushButton*>(editor);
//        btn->setProperty("data_value", index.data());
        btn->setProperty("data_value", "...");
        btn->setText("...");
    }
    else
    {
        QStyledItemDelegate::setEditorData(editor, index);
    }
}

void LibraryItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
    {
        QPushButton *btn = qobject_cast<QPushButton*>(editor);
        model->setData(index, btn->property("data_value"));
    }
    else
    {
        QStyledItemDelegate::setModelData(editor, model, index);
    }
}

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

void LibraryItemDelegate::cellEntered(const QModelIndex &index)
{
    if(index.model()->headerData(index.column(), Qt::Horizontal, Qt::UserRole).toInt() == 1)
    {
        if(isOneCellInEditMode)
        {
            myView->closePersistentEditor(currentEditedCellIndex);
        }
        myView->openPersistentEditor(index);
        isOneCellInEditMode = true;
        currentEditedCellIndex = index;
    }
    else
    {
        if(isOneCellInEditMode)
        {
            isOneCellInEditMode = false;
            myView->closePersistentEditor(currentEditedCellIndex);
        }
    }
}

Implementation:

QStandardItemModel *myModel; // This is in the Header file

myModel = new QStandardItemModel(0,2,this);
myModel->setHeaderData(1, Qt::Horizontal, 1, Qt::UserRole);
myModel->setHorizontalHeaderLabels(QStringList(tr("Pfad zu den bibliotheks Ordnern")));

// Set Model and delegate to the View
ui->tableView_pathes->setModel(myModel);
LibraryItemDelegate *delegate = new LibraryItemDelegate(ui->tableView_pathes);
ui->tableView_pathes->setItemDelegate(delegate);

// Stretch only the first column
ui->tableView_pathes->horizontalHeader()->setSectionResizeMode(0,QHeaderView::Stretch);
ui->tableView_pathes->horizontalHeader()->setSectionResizeMode(1,QHeaderView::Fixed);

Edit: Here is the code for the buttons in the tableView. Connect the signals in createEditor with connect(btn, SIGNAL(pressed()), this, SLOT(buttonPressed())); and set give the delegate a reference to the QStandardItemModel.

void LibraryItemDelegate::buttonPressed()
{
    QString dir = QFileDialog::getExistingDirectory(new QWidget(), tr("Wähle die bibliotheks Ordner"), "/home", QFileDialog::ShowDirsOnly);

    qDebug() << "Test: " << dir;

    if(!dir.isEmpty())
    {
        QModelIndex ind = currentEditedCellIndex.model()->index(currentEditedCellIndex.row(), 0);
        myModel->setData(ind, dir, Qt::DisplayRole);
    }
}
Darkproduct
  • 1,062
  • 13
  • 28
  • I have used Widget::grab() like this: painter->drawPixmap(r.left(),r.right(), r.width(), r.height(), widget->grab()); But it was a while back, and I do not remember how it worked. – Szpaqn Aug 28 '17 at 12:46
  • 1
    I think now you just do widget->render() ? it returns Pixmap of that widget... I might be wrong tho. – Dariusz Mar 27 '19 at 17:10
3
  1. Use QStyledItemDelegate, not QItemDelegate.
  2. Read the Qt Manual

QStyledItemDelegate class

Spinboxdelegate Example

  1. Code example of subclassed QStyledItemDelegate (trimmed down):

Header file

#ifndef MYITEMDELEGATE_H
#define MYITEMDELEGATE_H

#include <QStyledItemDelegate>

class KontaktForm;

class MyItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT

    mutable SubscriberForm *subscriberForm;

public:
    explicit MyItemDelegate(QObject *parent = 0);
    ~MyItemDelegate();


////////!Methods - You don't need all of them
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);

};

#endif // MYITEMDELEGATE_H

Source file

#include "myitemdelegate.h"

#include "mytreeview.h"
#include <QModelIndex>
#include <QSize>

MyItemDelegate::MyItemDelegate(QObject *parent) : QStyledItemDelegate(parent),
    subscriberForm(Q_NULLPTR),
{
}

QSize MyItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    //// return the QSize of the item in Your view
}

void MyItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    ////optional : implement custom painting - text, images, drawings, and such
}


QWidget *MyItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    subscriberForm = new SubscriberForm(parent);
    ////optional additional settings for Your editor
    return subscriberForm;      
}

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

void MyItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    ////setup the editor - your data are in index.data(Qt::DataRoles) - stored in a QVariant;
    QString value = index.model()->data(index,Qt::EditRole).toString();
    SubscriberForm *subscriberForm =  static_cast<SubscriberForm*>(editor);
}

void MyItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    ////optional - if needed - return changed data, from editor to the model in a custom matter

    SubscriberForm *subscriberForm =  static_cast<SubscriberForm*>(editor);
    model->setData(index,QVariant(subscriberForm->getData()),Qt::EditRole);

}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Szpaqn
  • 545
  • 5
  • 17
  • Does this actually work? For example, the control disappears after editing is finished. – Mikhail Aug 27 '17 at 20:41
  • @Mikhail This code is from my application, so it should work. But there is a chance that I forgot to paste something, so if You tried it, and it didn't work, let me know. – Szpaqn Aug 28 '17 at 12:44
  • Yeah, so the button will disappear when its cell is not activated. Or specifically, `paint()` is called, which does nothing. How to keep the button present while not activated is not trivial, you can see the other answer used a deprecated screen grab method. – Mikhail Aug 28 '17 at 15:02
  • As I wrote in my answer, this is not the whole code. I did not write my paint() method here, because its quite big. As for the editor to be active, I call openPersistantEditor() from my custom treeView class (inheriting from QTreeView). You are right, that I forgot to mention that here. I will try to remember to add the code tomorrow. – Szpaqn Aug 28 '17 at 17:14
  • Unfortunately I do not have much time right now, and the question was not about leaving the editor opened. That's why I wont change my answer. If you need more information, please ask a separate question on Stack Overflow and let me know with a private message. I will have more time in the upcoming week, so I can answer (if I will know how). – Szpaqn Aug 31 '17 at 07:21