Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

[SOLVED, except the paint problem..] - DragDrop, QMimeData problem



  • Hi,

    I am using QMimeData .userData() function to set a pointer to a custom object directly in a QMimeData object.
    This is working fine so far, but since I use that method, when I close my application, I got an error showing "Application stopped working", and I can't seem to find where this error come from. Is there a way to debug and know where this error come from? In the error log it says fault come from "Qt5Core.dll"

    I made a small video showing the error in action - "30sec video here":https://www.youtube.com/watch?v=SpFRHoehT3U&feature=youtu.be
    There are small bug in the interface that still needs fixing (problem with paint delegate..)
    Thanks if you can help me, If you need financial compensation I would gladly need an helper to finish this QListView model faster



  • Here is my working delegate code: (except a paint problem that still need fixing)

    @#include "listintervaldelegate.h"
    #include <QDebug>
    #include "interval.h"
    #include "util.h"
    #include "intervalwidget.h"
    #include "edittargetpowerwidget.h"

    ListIntervalDelegate::ListIntervalDelegate(QObject parent) : QStyledItemDelegate(parent)
    {
    ptrParent = (QWidget
    )parent;
    }

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

    IntervalWidget *widgetInterval = new IntervalWidget(parent);
    return widgetInterval;
    

    }

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

    Interval interval = qvariant_cast<Interval>(index.model()->data(index, Qt::DisplayRole));
    IntervalWidget *widget = static_cast<IntervalWidget*>(editor);
    
    
    int stepType = interval.getType();
    QTime duration = interval.getDurationQTime();
    QString displayMsg = interval.getDisplayMessage();
    ///POWER
    int targetStepPower = interval.getPowerStepType();
    double startFTP = interval.getFTP_start() * 100;
    double endFTP = interval.getFTP_end() * 100;
    int range = interval.getFTP_range();
    
    
    widget->intervalComboBoxType->setCurrentIndex(stepType);
    widget->timeEdit->setTime(duration);
    widget->msgLineEdit->setText(displayMsg);
    ///POWER
    widget->stepTypeComboBoxPower->setCurrentIndex(targetStepPower);
    widget->targetStartValue->setValue(startFTP);
    widget->targetEndValue->setValue(endFTP);
    widget->targetRangeValue->setValue(range);
    

    }

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

    IntervalWidget *widget = static_cast<IntervalWidget*>(editor);
    
    int stepTypeInt = widget->intervalComboBoxType->currentIndex();
    Interval::Type stepType = static_cast<Interval::Type>( stepTypeInt );
    QTime duration = widget->timeEdit->time();
    QString displayMsg = widget->msgLineEdit->text();
    ///POWER
    int targetStepPower = widget->stepTypeComboBoxPower->currentIndex();
    Interval::StepType stepTypePower = static_cast<Interval::StepType>( targetStepPower );
    double startFTP = widget->targetStartValue->value() / 100.0;
    double endFTP = widget->targetEndValue->value() / 100.0;
    int range = widget->targetRangeValue->value();
    
    
    
    Interval intervalTmp(duration, stepType, displayMsg,
                         stepTypePower, startFTP, endFTP, range,
                         stepTypePower, startFTP, endFTP, range,
                         stepTypePower, startFTP, endFTP, range);
    
    
    
    QVariant v = QVariant::fromValue(intervalTmp);
    model->setData(index, v, Qt::EditRole);
    

    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void ListIntervalDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {

    if(!index.isValid())
        return;
    
    
    bool paint = true;
    if (option.state & QStyle::State_Selected) {
        painter->fillRect(option.rect, option.palette.highlight());
    }
    
    
    if (paint) {
    
        painter->save();
        IntervalWidget *widget = new IntervalWidget(ptrParent);
        Interval interval = qvariant_cast<Interval>(index.model()->data(index, Qt::DisplayRole));
    
    
        int stepType = interval.getType();
        QTime duration = interval.getDurationQTime();
        QString displayMsg = interval.getDisplayMessage();
        ///POWER
        int targetStepPower = interval.getPowerStepType();
        double startFTP = interval.getFTP_start() * 100;
        double endFTP = interval.getFTP_end() * 100;
        int range = interval.getFTP_range();
    
        widget->intervalComboBoxType->setCurrentIndex(stepType);
        widget->timeEdit->setTime(duration);
        widget->msgLineEdit->setText(displayMsg);
        ///POWER
        widget->stepTypeComboBoxPower->setCurrentIndex(targetStepPower);
        widget->targetStartValue->setValue(startFTP);
        widget->targetEndValue->setValue(endFTP);
        widget->targetRangeValue->setValue(range);
    
    
        widget->resize( option.rect.size() );
        painter->translate(option.rect.topLeft());
        widget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
    
        painter->restore();
        delete widget;
    }
    

    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    void ListIntervalDelegate::updateEditorGeometry(QWidget editor, const QStyleOptionViewItem &option, const QModelIndex &/ index */) const {

    editor->setGeometry(option.rect);
    

    }

    QSize ListIntervalDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const {

    return QSize(500, 125); //enter your values here
    

    }
    @



  • Here is my working ListModel code

    @#include "intervallistmodel.h"
    #include <QDebug>
    #include "streammyobject.h"

    IntervalListModel::IntervalListModel(QList<Interval> lstInterval, QObject *parent) : QAbstractListModel(parent) {
    this->lstInterval = lstInterval;
    }

    int IntervalListModel::rowCount(const QModelIndex &parent) const {

    Q_UNUSED(parent);
    return lstInterval.size();
    

    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////
    int IntervalListModel::columnCount(const QModelIndex &parent) const {

    Q_UNUSED(parent);
    return 1;
    

    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////
    QVariant IntervalListModel::headerData(int section, Qt::Orientation orientation, int role) const {

    if (role != Qt::DisplayRole)
        return QVariant();
    
    if (orientation == Qt::Horizontal)
        return QString("Column %1").arg(section);
    else
        return QString("Row %1").arg(section);
    

    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////
    QVariant IntervalListModel::data(const QModelIndex &index, int role) const {

    if (!index.isValid())
        return QVariant();
    
    if (index.row() >= lstInterval.size() || index.row() < 0)
        return QVariant();
    
    
    if (role == Qt::DisplayRole || role == Qt::EditRole) {
    
        Interval interval = lstInterval.at(index.row());
        QVariant v = QVariant::fromValue(interval);
        return v;
    
    }
    else {
        return QVariant();
    }
    

    }

    bool IntervalListModel::insertRows(int position, int rows, const QModelIndex &index) {

    Q_UNUSED(index);
    beginInsertRows(QModelIndex(), position, position + rows - 1);
    
    qDebug() << "INSERT ROW";
    
    for (int row = 0; row < rows; ++row) {
    
        QTime time1(0, 5, 0);
        Interval interval(time1, Interval::INTERVAL, "display_msg",
                          Interval::FLAT, .50, .50, 20,
                          Interval::FLAT, 100, 100, 15,
                          Interval::FLAT, .60, .60, 50);
        lstInterval.insert(position, interval);
    }
    
    endInsertRows();
    return true;
    

    }

    bool IntervalListModel::removeRows(int position, int rows, const QModelIndex &index)
    {
    Q_UNUSED(index);
    beginRemoveRows(QModelIndex(), position, position + rows - 1);

    qDebug() << "REMOVE ROW";
    
    for (int row = 0; row < rows; ++row) {
        lstInterval.removeAt(position);
    }
    
    endRemoveRows();
    return true;
    

    }

    bool IntervalListModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {

    qDebug() << "SET DATA";
    
    if (index.isValid() && role == Qt::EditRole) {
    
        Interval interval = qvariant_cast<Interval>(value);
        lstInterval.replace(index.row(), interval);
        emit dataChanged(index, index);
        return true;
    }
    
    return false;
    

    }

    Qt::ItemFlags IntervalListModel::flags(const QModelIndex &index) const
    {

    Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
    
    if (index.isValid())
        return Qt::ItemIsDragEnabled | Qt::ItemIsEditable |defaultFlags;
    else
        return Qt::ItemIsDropEnabled | defaultFlags;
    

    }

    QStringList IntervalListModel::mimeTypes() const {

    QStringList types;
    types << "maxtrainer/interval";
    return types;
    

    }

    bool IntervalListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {

    qDebug() << "DROPMINEDATA";
    
    if (action == Qt::IgnoreAction)
        return true;
    
    if (!data->hasFormat("amaxtrainer/interval"))
        return false;
    
    if (column > 0)
        return false;
    int beginRow;
    
    if (row != -1)
        beginRow = row;
    else if (parent.isValid())
        beginRow = parent.row();
    else
        beginRow = rowCount(QModelIndex());
    
    QByteArray encodedData = data->data("maxtrainer/interval");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    StreamMyObject myStreamer;
    Interval interval;
    
    while (!stream.atEnd()) {
        /// Deserialize stream into Object
        Util::operator >>(stream, interval);
        myStreamer.operator >>(stream, interval);
    }
    
    insertRow(beginRow, QModelIndex());
    
    QModelIndex idx = index(beginRow, 0, QModelIndex());
    QVariant v = QVariant::fromValue(interval);
    setData(idx, v);
    
    
    return true;
    

    }

    QMimeData *IntervalListModel::mimeData(const QModelIndexList &indexes) const {

    qDebug() << "MIME_DATA";
    
    QMimeData *mimeData = new QMimeData();
    QByteArray encodedData;
    
    StreamMyObject myStreamer;
    QDataStream stream(&encodedData, QIODevice::WriteOnly);
    
    foreach (QModelIndex index, indexes) {
        if (index.isValid()) {
            Interval interval = qvariant_cast<Interval>(data(index, Qt::DisplayRole) );
            ///Write object to the stream (serialize)
            myStreamer.operator <<(stream, interval);
        }
    }
    
    mimeData->setData("maxtrainer/interval", encodedData);
    return mimeData;
    

    }

    Qt::DropActions IntervalListModel::supportedDropActions() const {

    return Qt::MoveAction;
    

    }@


  • Lifetime Qt Champion

    Hi,

    Do you really need to have your Interval has pointers ? How do you store them in the first place ?

    @
    if (option.state & QStyle::State_Selected) {
    painter->fillRect(option.rect, option.palette.highlight());
    paint = false; <--- here is the cause
    }

    if (paint) { <--- there

        painter->save();
        IntervalWidget *widget = new IntervalWidget(ptrParent);
    

    @

    You are setting paint to false when selected thus the code doing the rendering will not be called



  • Thanks for the reply SGaist,
    My interval are stored in the IntervalListModel has "QList<Interval*> lstInterval;"

    If I remove the check for painting with State_Selected in paint(), the widget will be rendered 2 times, in createEditor() and paint(), resulting in a weird double widget interface when i'm in edit mode (double clicked on a row)

    [Edit: I changed QList<Interval*> for QList<Interval> and all my memory management problem are gone, QList does the job for me]



  • edited


  • Lifetime Qt Champion

    Then there's no need to play with pointers with that class.

    For the painting problem I might have misunderstood. What is happening now ?



  • Forgive me but I haven't figured how to edit the model internal data without having a pointer to Interval* in each QVariant QModelIndex.
    Since my editor Widget edit all the fields of the Interval object at the same time, I cannot use the standard approach with (1 widget = 1 object attribute field edit, like spinboxDelegate example)

    The painting problem now :
    https://www.youtube.com/watch?v=-XLK1UW6uM8&feature=youtu.be&hd=1

    Thanks for your time

    Max
    http://maximumtrainer.com/


  • Lifetime Qt Champion

    Exactly the same as currently (well, without pointers), and in the model you replace the Interval in the list with the one you just created



  • Thanks, you made me realize that a QList of pointer is not the way to go.
    I replaced my model data QList<*Interval> with QList<Interval> and let QList manage memory much easier this way!

    Still got the paint problem in double though (as you can see in video from previous post)

    Merci!



  • I created a new thread "here":http://qt-project.org/forums/viewthread/37501/ for serialization of a customObject in QMimeData. Also edited the code up there with the latest working code
    Thank you


  • Lifetime Qt Champion

    You could try to detect QStyle::State_Editing and not paint in that case.

    I would also recommend keeping an IntervalWidget as member variable in your delegate only for the painting purpose, that would avoid recreating the widget each time you have to render it.



  • I used your suggestion, one widget created in delegate constructor and deleted in destructor, thanks

    As for QStyle::State_Editing, the state never get triggered it seems, can I force the state myself in the createEditor() function or something? I could just have a simple flag when I know item is being edited, and don't paint in that case.

    I created another tread for the paint problem, as well as a rant on the QDelegate -_-
    http://qt-project.org/forums/viewthread/37513/



  • Hey SGaist it's me again.

    Do you think a QListWidget would work in my use-case
    Is drag and drop possible in a QListWidget?
    I still haven't figured this paint problem with the delegate..
    Thank you,..


  • Lifetime Qt Champion

    Drag and drop is possible with all model/view classes whether it's a QTableView or QListView. The setup will differ between the widget and view version that's all.


Log in to reply