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

Stream operators for subclassed QTreeWidgetItem not invoked



  • I've subclassed QTreeWidgetItem as XItem, it works fine in QTreeWidget except that when I attempt to do drag/drop operation, inside default StartDrag implementation it crashes trying to serialize an internal struct that's set as DataRole.

    To fix that I wrote stream operators for XItem that don't write DataRole. For somereason the stream operators are not being invoked and internally the default implementation for QTreeWidgetItem is being used, and I'm still getting the same exception.

    62685617-3f01-4382-8a52-a68dfa385e22-image.png


  • Qt Champions 2017

    Please provide at least minimal code of how you do things. Also please post the stack trace as text and don't cut the leading part of it, I can't know what module what code resides in, nor should I.



  • @kshegunov The module name is exe filename (which I wanted to hide).

    Here's the subclassed item (I took out some private methods, since they're not related to this functionality)

    class XItem : public QTreeWidgetItem {
    public:
    	friend QDataStream& operator<<(QDataStream& stream, const XItem & item);
    	friend QDataStream& operator>>(QDataStream& stream, XItem & item);
    
    	XItem () { };
    
    	XItem (QTreeWidget* parent) :QTreeWidgetItem(parent) {}
    
    };
    
    Q_DECLARE_METATYPE(XItem );
    

    Overloaded Stream operators

    QDataStream& operator<<(QDataStream& stream, const XItem& item)
    {
    	stream << item.text(0);
    	return stream;
    }
    
    QDataStream& operator>>(QDataStream& stream, XItem& item)
    {
    	QString text;
    	stream >> text;
    	item.setText(0, text);
    
    	return stream;
    }
    

    This is the struct that is set as the DataRole

    struct ListEntry
    {
    	int mapID = 0;
    
    	QString name;
    	QDateTime dtModified;
    };
    

    Here's how I'm setting the struct as DataRole

    item->setData(iData, Qt::UserRole, QVariant::fromValue<ListEntry*>(&entry));
    


  • @Taytoo said in Stream operators for subclassed QTreeWidgetItem not invoked:

    inside default StartDrag implementation it crashes

    It doesn't look like it's crashing but rather asserting. What is the message that gets printed befor the crash?



  • @VRonin Yes its a assert, but it actually stops program executing since its an error of sorts

    Message printed before assert:

    QVariant::save: unable to save type 'ListEntry*' (type id: 1031).
    
    ASSERT failure in QVariant::save: "Invalid type to save", file kernel\qvariant.cpp, line 2594
    Debug Error!
    
    Program: C:\Qt\5.12.5\msvc2017\bin\Qt5Cored.dll
    Module: 5.12.5
    File: kernel\qvariant.cpp
    Line: 2594
    
    ASSERT failure in QVariant::save: "Invalid type to save", file kernel\qvariant.cpp, line 2594
    
    (Press Retry to debug the application)
    QtTestProject.exe has triggered a breakpoint.
    

    Created a bare minimum program to figure out the issue. Here's the stack trace:

     	Qt5Cored.dll!qt_message_fatal(QtMsgType __formal, const QMessageLogContext & context, const QString & message) Line 1878	C++
     	Qt5Cored.dll!QMessageLogger::fatal(const char * msg, ...) Line 888	C++
     	Qt5Cored.dll!qt_assert_x(const char * where, const char * what, const char * file, int line) Line 3220	C++
     	Qt5Cored.dll!QVariant::save(QDataStream & s) Line 2594	C++
     	Qt5Cored.dll!operator<<(QDataStream & s, const QVariant & p) Line 2619	C++
    >	Qt5Cored.dll!QtPrivate::writeAssociativeContainer<QMap<int,QVariant> >(QDataStream & s, const QMap<int,QVariant> & c) Line 319	C++
     	Qt5Cored.dll!operator<<<int,QVariant>(QDataStream & s, const QMap<int,QVariant> & map) Line 444	C++
     	Qt5Cored.dll!QAbstractItemModel::encodeData(const QList<QModelIndex> & indexes, QDataStream & stream) Line 2598	C++
     	Qt5Cored.dll!QAbstractItemModel::mimeData(const QList<QModelIndex> & indexes) Line 1963	C++
     	Qt5Widgetsd.dll!QTreeModel::internalMimeData() Line 726	C++
     	Qt5Widgetsd.dll!QTreeWidget::mimeData(const QList<QTreeWidgetItem *> items) Line 3357	C++
     	Qt5Widgetsd.dll!QTreeModel::mimeData(const QList<QModelIndex> & indexes) Line 739	C++
     	Qt5Widgetsd.dll!QAbstractItemView::startDrag(QFlags<enum Qt::DropAction> supportedActions) Line 3681	C++
     	Qt5Widgetsd.dll!QAbstractItemView::mouseMoveEvent(QMouseEvent * event) Line 1839	C++
     	Qt5Widgetsd.dll!QTreeView::mouseMoveEvent(QMouseEvent * event) Line 1983	C++
     	Qt5Widgetsd.dll!QWidget::event(QEvent * event) Line 8940	C++
     	Qt5Widgetsd.dll!QFrame::event(QEvent * e) Line 550	C++
     	Qt5Widgetsd.dll!QAbstractScrollArea::viewportEvent(QEvent * e) Line 1221	C++
     	Qt5Widgetsd.dll!QAbstractItemView::viewportEvent(QEvent * event) Line 1751	C++
    
    


  • Looks pretty clear as an error, you don't need the stream operators for XItem but for ListEntry*

    P.S.
    Stream operators for pointers usually are a bad idea but depends on how your internal data is structured



  • @VRonin Yes, I also tried creating stream operators for ListEntry but still got the assert. Do I need separate Stream operators for ListEntry and ListEntry* ?

    For now, I'm just saving ListEntry* pointer as qlonglong in QVariant, that takes care of QVariant throwing up while serializing/deserializing. Still would like to know what the proper solution is.



  • @Taytoo said in Stream operators for subclassed QTreeWidgetItem not invoked:

    Do I need separate Stream operators for ListEntry and ListEntry* ?

    No, just for ListEntry*

    Still would like to know what the proper solution is.

    Depends on what ListEntry is in the context of your data model.
    First question: why are you storing it as a pointer instead of a value in the model?


  • Qt Champions 2017

    @Taytoo said in Stream operators for subclassed QTreeWidgetItem not invoked:

    Here's the subclassed item (I took out some private methods, since they're not related to this functionality)

    That's fine, now we have at least something to work with. First, see what @VRonin wrote, and as an addition:

    QVariant::fromValue<ListEntry*>(&entry)

    Are you really wanting to save a pointer to an object? What I'd expect you to want to do is to serialize the struct itself, not a pointer to it that may or may not persist (the variant can have no way of knowing either way).

    Edit: I just saw that @VRonin already mentioned this. Leaving the reply for posterity, though.



  • @VRonin said in Stream operators for subclassed QTreeWidgetItem not invoked:

    @Taytoo said in Stream operators for subclassed QTreeWidgetItem not invoked:

    Do I need separate Stream operators for ListEntry and ListEntry* ?

    No, just for ListEntry*

    What would the signatures of the stream operators for ListEntry* look like?

    Still would like to know what the proper solution is.

    Depends on what ListEntry is in the context of your data model.
    First question: why are you storing it as a pointer instead of a value in the model?

    It points to bunch of data related to the Item, doesn't make sense to serialize and store it all in the item, since there could be hundreds of items, so much faster to just get the pointer and access the values directly. That's why when serializing item, I don't need it either.


Log in to reply