[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
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.