[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;
}@
-
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]
-
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=1Thanks for your time
-
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 -
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/ -
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.