Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. crash in QListWidget::dropEvent when I add a UserRole to my QListItemWidget :'(
Forum Updated to NodeBB v4.3 + New Features

crash in QListWidget::dropEvent when I add a UserRole to my QListItemWidget :'(

Scheduled Pinned Locked Moved Solved General and Desktop
27 Posts 4 Posters 3.9k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • mbruelM Offline
    mbruelM Offline
    mbruel
    wrote on last edited by
    #1

    Hi,
    I've been playing with drag and drop between two QListWidgets and everything was working well until I start adding a UserRole to my Items.
    I guess it is a mime issue by seeing the backtrace:

    Thread 1 (Thread 0x7ffff7fb8140 (LWP 2592)):
    #0  0x00007ffff64dbbcc in QBitArray::resize(int) () from /opt/Qt/5.10.1/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #1  0x00007ffff6657fcb in QAbstractItemModel::decodeData(int, int, QModelIndex const&, QDataStream&) () from /opt/Qt/5.10.1/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #2  0x00007ffff6658f5f in QAbstractListModel::dropMimeData(QMimeData const*, Qt::DropAction, int, int, QModelIndex const&) () from /opt/Qt/5.10.1/gcc_64/lib/libQt5Core.so.5
    No symbol table info available.
    #3  0x00007ffff7971c1c in QListWidget::dropMimeData(int, QMimeData const*, Qt::DropAction) () from /opt/Qt/5.10.1/gcc_64/lib/libQt5Widgets.so.5
    No symbol table info available.
    #4  0x00007ffff7929dbc in QAbstractItemView::dropEvent(QDropEvent*) () from /opt/Qt/5.10.1/gcc_64/lib/libQt5Widgets.so.5
    No symbol table info available.
    #5  0x00007ffff7971c70 in QListWidget::dropEvent(QDropEvent*) () from /opt/Qt/5.10.1/gcc_64/lib/libQt5Widgets.so.5
    No symbol table info available.
    #6  0x00005555555e910f in ItemOfferingListWidget::dropEvent (this=0x5555561fd4f0, event=0x7fffffffaaa0) at ../src_cosi7/hmi/GUI/widget/ItemOfferingListWidget.cpp:73
    No locals.
    

    How should I declare it? It is still an unknown territory for me...
    Here is the way I insert my Items:

    QListWidget *listWidget;
    ...
    QListWidgetItem *item = new QListWidgetItem(secondaryFacility->getName());
    item->setData(Qt::UserRole, QVariant::fromValue(secondaryFacility));
    listWidget->addItem(item);
    

    Inserting the Item in my first list is ok. The crash is when I receive it in another one in the DropEvent:

    void ItemOfferingListWidget::dropEvent(QDropEvent *event)
    {
        if (event->source() == this)
        {
            if (_allowIternalMove)
            {
                setDragDropMode(InternalMove);
                QListWidget::dropEvent(event);
                setDragDropMode(DragDrop);
            }
        }
        else
        {
            QListWidget::dropEvent(event);
            if (!_allowIternalMove)
                sortItems();
            emit draggedItemsHaveBeenTakenByOtherList();
        }
    }
    

    How can I decode it properly and clone it? (in the second ListWidget, I emit the signal draggedItemsHaveBeenTakenByOtherList so the ListWidget from where the Items are coming from can remove and delete its moved items)

    Please tell me I'll be able to do such thing without having to go with QListViews using their customs models.
    Cheers

    1 Reply Last reply
    0
    • mbruelM Offline
      mbruelM Offline
      mbruel
      wrote on last edited by mbruel
      #2

      Yeah I finally find a solution \o/ This could be a bit more documented, took me a while browsing the web to figure out what to try...
      I've just override QListWidget::startDrag creating my own QDrag with a QMimeData of type that has to be "application/x-qabstractitemmodeldatalist" but that I can fill with what I want :)
      So here is my code:

      void ItemOfferingListWidget::startDrag(Qt::DropActions supportedActions)
      {
          Q_UNUSED(supportedActions)
          QMimeData       *mimeData = new QMimeData();
          QByteArray       encodedData;
          QDataStream stream(&encodedData, QIODevice::WriteOnly);
      
          for (QListWidgetItem *item  : selectedItems())
          {
              Element *elem = item->data(Qt::UserRole).value<Element*>();
              stream << elem->getId() << elem->getElementTypeName();
          }
          mimeData->setData("application/x-qabstractitemmodeldatalist", encodedData);
          QDrag *drag = new QDrag(this);
          drag->setMimeData(mimeData);
      
          drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
      }
      

      and on the reception to decode it:

      void ItemOfferingListWidget::dropEvent(QDropEvent *event)
      {
          qDebug() << "[MB_TRACE][ItemOfferingListWidget::dropEvent]" << event->mimeData()->formats();
          if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
          {
              event->accept();
      
              if (event->source() == this)
              {
                  if (_allowIternalMove)
                  {
                      setDragDropMode(InternalMove);
                      QListWidget::dropEvent(event);
                      setDragDropMode(DragDrop);
                  }
              }
              else
              {
                  QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
                  QDataStream stream(&encoded, QIODevice::ReadOnly);
                  while (!stream.atEnd())
                  {
                      QString elemId, elemType; 
                      stream >> elemId >> elemType;
                      qDebug() << "Receiving elemId: " << elemId ;
      
                      Model *model = MyApplication::instance()->getModel();
                      Element *elem = model->getElementById(elemType, elemId);
                      QListWidgetItem *item = new QListWidgetItem(elem->getName());
                      item->setData(Qt::UserRole, QVariant::fromValue(elem));
                      addItem(item);
                  }
                  if (!_allowIternalMove)
                      sortItems();
                  emit draggedItemsHaveBeenTakenByOtherList();
              }
          }
          else
          {
              event->ignore();
          }
      }
      

      Please let me know if there is a cleaner way to do it. Like directly recovering the UserRole from the source with my pointer on the object.

      Cheers

      1 Reply Last reply
      1
      • mbruelM Offline
        mbruelM Offline
        mbruel
        wrote on last edited by
        #3

        arf no.. the internal move is not working anymore :(
        any idea how to achieve it?

        1 Reply Last reply
        0
        • mbruelM Offline
          mbruelM Offline
          mbruel
          wrote on last edited by
          #4

          well I guess I can add the row of selected items in the mimeData of the QDrag and then in the drop event do the move manually (taking the items and inserting them again in the position of the drop event...)
          I'll post the code when I'll have some time to give it a try

          1 Reply Last reply
          0
          • mbruelM Offline
            mbruelM Offline
            mbruel
            wrote on last edited by mbruel
            #5

            Alright, this seems to work fine to manually handle the internal move:
            First in the startDrag override I encode the row of the selected items I'm moving:

            void ItemOfferingListWidget::startDrag(Qt::DropActions supportedActions)
            {
                Q_UNUSED(supportedActions)
                QMimeData       *mimeData = new QMimeData();
                QByteArray       encodedData;
                QDataStream stream(&encodedData, QIODevice::WriteOnly);
                for (QListWidgetItem *item  : selectedItems())
                {
                    Element *elem = item->data(Qt::UserRole).value<Element*>();
                    stream << row(item) << elem->getId() << elem->getElementTypeName();
                }
                mimeData->setData("application/x-qabstractitemmodeldatalist", encodedData);
                QDrag *drag = new QDrag(this);
                drag->setMimeData(mimeData);
                drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
            }
            

            Then in the dropEvent, if the source is myself, I store the items in a Map indexed by the row (to have the list of items ordered by row and not the user selection) and then I take them out from the list and reinsert them in the new position where the user dropped them.
            It looks like that:

            void ItemOfferingListWidget::dropEvent(QDropEvent *event)
            {
                qDebug() << "[MB_TRACE][ItemOfferingListWidget::dropEvent]" << event->mimeData()->formats();
                if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
                {
                    event->accept();
                    if (event->source() == this)
                    {
                        if (_allowIternalMove)
                        {
                            QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
                            QDataStream stream(&encoded, QIODevice::ReadOnly);
                            QMap<int, QListWidgetItem *> movedItems;
                            while (!stream.atEnd())
                            {
                                int row;
                                QString elemId, elemType;
                                stream >> row >> elemId >> elemType;
                                qDebug() << "Receiving elemId: " << elemId  << " (row in original list: " << row << ")";
                                movedItems.insert(row, item(row));
                            }
                            if (movedItems.size())
                            {
                                int rowToInsert = row(itemAt(event->pos()));
                                int index = 0;
                                auto it = movedItems.begin();
                                short direction = 1;
                                if (it.key() < rowToInsert)
                                    direction = -1;
                                for (; it != movedItems.end() ; ++it)
                                {
                                    QListWidgetItem *item = it.value();
                                    takeItem(row(item));
                                    insertItem(rowToInsert+index*direction, item);
                                    ++index;
                                }
                            }
                        }
                    }
                    else
                    {
                        QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
                        QDataStream stream(&encoded, QIODevice::ReadOnly);
                        while (!stream.atEnd())
                        {
                            int row;
                            QString elemId, elemType; // can store the elemType
                            stream >> row >> elemId >> elemType;
                            qDebug() << "Receiving elemId: " << elemId  << " (row in original list: " << row << ")";
            
                            Model *model = MyApplication::instance()->getModel();
                            Element *elem = model->getElementById(elemType, elemId);
                            QListWidgetItem *item = new QListWidgetItem(elem->getName());
                            item->setData(Qt::UserRole, QVariant::fromValue(elem));
                            item->setIcon(IconFactory::getInstance()->getElementTypeIcon(elem->getElementType()));
                            addItem(item);
                        }
                        if (!_allowIternalMove)
                            sortItems();
                        emit draggedItemsHaveBeenTakenByOtherList();
                    }
                    emit internalListChanged();
                }
                else
                {
                    event->ignore();
                }
            }
            

            There is a little subtlety in the insertion of the taken item depending on the new position we dropped the items (cf the direction multiplicator)

            Please let me know if there is an easier solution or if something is bad practice.
            cheers

            1 Reply Last reply
            2
            • mbruelM Offline
              mbruelM Offline
              mbruel
              wrote on last edited by
              #6

              Even better : following this link on how to serialize a pointer in QDataStream, here is a way to directly transfer a pointer using its address and a reinterpret_cast. That's much more efficient and a great use of reinterpret_cast that we normally never use...
              Some might say it's dodgy but there is a huge gain in performance as we don't have to find back an object but can transmit it straight away through its address.

              So no need to pass the id, we can cast the pointer address to a qulonglong and retrieve it in the receiver.

              Here is the sending side:

              void ItemOfferingListWidget::startDrag(Qt::DropActions supportedActions)
              {
                  Q_UNUSED(supportedActions)
                  QMimeData       *mimeData = new QMimeData();
                  QByteArray       encodedData;
                  QDataStream stream(&encodedData, QIODevice::WriteOnly);
                  for (QListWidgetItem *item  : selectedItems())
                  {
                      Element *elem = item->data(Qt::UserRole).value<Element*>();
                      qulonglong ptrval(*reinterpret_cast<qulonglong *>(&elem));
                      stream << row(item) << ptrval;
                  }
                  mimeData->setData("application/x-qabstractitemmodeldatalist", encodedData);
                  QDrag *drag = new QDrag(this);
                  drag->setMimeData(mimeData);
                  drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
              }
              

              And now the receiver (dropEvent)

              void ItemOfferingListWidget::dropEvent(QDropEvent *event)
              {
                  qDebug() << "[MB_TRACE][ItemOfferingListWidget::dropEvent]" << event->mimeData()->formats();
                  if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
                  {
                      event->accept();
                      if (event->source() == this)
                      {
                          if (_allowIternalMove)
                          {
                              QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
                              QDataStream stream(&encoded, QIODevice::ReadOnly);
                              QMap<int, QListWidgetItem *> movedItems;
                              while (!stream.atEnd())
                              {
                                  int row;
                                  qulonglong ptrval;
                                  stream >> row >> ptrval;
              //                    Element *elem = *reinterpret_cast<Element **>(&ptrval);
              //                    qDebug() << "Receiving elem: " << elem->getName() << " (row in original list: " << row << ")";
                                  movedItems.insert(row, item(row));
                              }
                              if (movedItems.size())
                              {
                                  int rowToInsert = row(itemAt(event->pos()));
                                  int index = 0;
                                  auto it = movedItems.begin();
                                  short direction = 1;
                                  if (it.key() < rowToInsert)
                                      direction = -1;
                                  for (; it != movedItems.end() ; ++it)
                                  {
                                      QListWidgetItem *item = it.value();
                                      takeItem(row(item));
                                      insertItem(rowToInsert+index*direction, item);
                                      ++index;
                                  }
                              }
                          }
                      }
                      else
                      {
                          QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
                          QDataStream stream(&encoded, QIODevice::ReadOnly);
                          while (!stream.atEnd())
                          {
                              int row;
                              qulonglong ptrval;
                              stream >> row >> ptrval;
                              Element *elem = *reinterpret_cast<Element **>(&ptrval);
                              qDebug() << "Receiving elem : " << elem->getName();
              
                              QListWidgetItem *item = new QListWidgetItem(elem->getName());
                              item->setData(Qt::UserRole, QVariant::fromValue(elem));
                              item->setIcon(IconFactory::getInstance()->getElementTypeIcon(elem->getElementType()));
                              addItem(item);
                          }
                          if (!_allowIternalMove)
                              sortItems();
                          emit draggedItemsHaveBeenTakenByOtherList();
                      }
                      emit internalListChanged();
                  }
                  else
                  {
                      event->ignore();
                  }
              }
              
              1 Reply Last reply
              1
              • mbruelM Offline
                mbruelM Offline
                mbruel
                wrote on last edited by
                #7

                Last update on the manual implementation of the internal move of items.
                I realized my first implementation was wrong when we were dragging some items up as it was inverting the order when we were dropping them.
                Here is a completely working version, we've to iterate the selected items in the opposite way depending the direction we're moving the items:

                void ItemOfferingListWidget::dropEvent(QDropEvent *event)
                {
                    qDebug() << "[MB_TRACE][ItemOfferingListWidget::dropEvent]" << event->mimeData()->formats();
                    if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
                    {
                        event->accept();
                        if (event->source() == this)
                        {
                            if (_allowIternalMove)
                            {
                                QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
                                QDataStream stream(&encoded, QIODevice::ReadOnly);
                                QMap<int, QListWidgetItem *> movedItems;
                                while (!stream.atEnd())
                                {
                                    int row;
                                    qulonglong ptrval;
                                    stream >> row >> ptrval;
                 //                   Element *elem = *reinterpret_cast<Element **>(&ptrval);
                //                    qDebug() << "Receiving elem: " << elem->getName() << " (row in original list: " << row << ")";
                                    movedItems.insert(row, item(row));
                                }
                                if (movedItems.size())
                                {
                                    int rowToInsert = row(itemAt(event->pos()));
                                    int index = 0;
                                    if (movedItems.firstKey() < rowToInsert)
                                    {
                                        auto it = movedItems.begin();
                                        for (; it != movedItems.end() ; ++it)
                                        {
                                            QListWidgetItem *item = it.value();
                                            takeItem(row(item));
                                            insertItem(rowToInsert+index, item);
                                            ++index;
                                        }
                                    }
                                    else
                                    {
                                        auto it = movedItems.end();
                                        do
                                        {
                                            --it;
                                            QListWidgetItem *item = it.value();
                                            takeItem(row(item));
                                            insertItem(rowToInsert-index, item);
                                            ++index;
                                        } while (it != movedItems.begin());
                                    }
                                }
                            }
                        }
                        else
                        {
                            QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
                            QDataStream stream(&encoded, QIODevice::ReadOnly);
                            while (!stream.atEnd())
                            {
                                int row;
                                qulonglong ptrval;
                                stream >> row >> ptrval;
                                Element *elem = *reinterpret_cast<Element **>(&ptrval);
                                qDebug() << "Receiving elem : " << elem->getName();
                
                                QListWidgetItem *item = new QListWidgetItem(elem->getName());
                                item->setData(Qt::UserRole, QVariant::fromValue(elem));
                                item->setIcon(IconFactory::getInstance()->getElementTypeIcon(elem->getElementType()));
                                addItem(item);
                            }
                            if (!_allowIternalMove)
                                sortItems();
                            emit draggedItemsHaveBeenTakenByOtherList();
                        }
                        emit internalListChanged();
                    }
                    else
                    {
                        event->ignore();
                    }
                }
                
                1 Reply Last reply
                0
                • VRoninV Offline
                  VRoninV Offline
                  VRonin
                  wrote on last edited by VRonin
                  #8

                  Serialisation to mime of model data is already a feature of Qt, unfortunately it has never been documented.
                  You can check this post on how to implement the drop event (it uses QGraphicsSceneDragDropEvent but it's identical with QDropEvent)

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  mbruelM 1 Reply Last reply
                  0
                  • VRoninV VRonin

                    Serialisation to mime of model data is already a feature of Qt, unfortunately it has never been documented.
                    You can check this post on how to implement the drop event (it uses QGraphicsSceneDragDropEvent but it's identical with QDropEvent)

                    mbruelM Offline
                    mbruelM Offline
                    mbruel
                    wrote on last edited by
                    #9

                    @VRonin
                    Thanks for this information. I've found something like this on stackoverflow (reading the QMap<int,QVariant> from the DataStream of the mime data) but I didn't manage to make it work, that is why I was using my own Drag event.

                    I've just tried again and it is still not working but I've a log in the debugger saying:

                    QVariant::save: unable to save type 'Element*' (type id: 1052).
                    

                    Normally I've no problem to read/write Element* in a QVariant has in the header of the Element class I've Q_DECLARE_METATYPE( Element* )

                    So this seems to be a problem within the QListWidget? I've the same type id that when I've my first crash (except now the app is not crashing anymore)

                    Here is my new code (I removed the override of startDrag)

                    void ItemOfferingListWidget::dropEvent(QDropEvent *event)
                    {
                        qDebug() << "[MB_TRACE][ItemOfferingListWidget::dropEvent]" << event->mimeData()->formats();
                        if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
                        {
                            event->accept();
                            if (event->source() == this)
                            {
                                if (_allowIternalMove)
                                {
                                    setDragDropMode(InternalMove);
                                    QListWidget::dropEvent(event);
                                    setDragDropMode(DragDrop);
                                }
                                else
                                {
                                    const QByteArray mimeData = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
                                    QDataStream mimeReader(mimeData);
                                    int row,col;
                                    QMap<int,QVariant> modelData;
                                    for(;;){
                                        mimeReader.startTransaction();
                                        mimeReader >> row  >> modelData;
                                        if(!mimeReader.commitTransaction())
                                            break;
                                        const auto userRoleIter = modelData.constFind(Qt::UserRole);
                                        if(userRoleIter != modelData.cend())
                                        {
                                            Element *elem = userRoleIter->value<Element*>();
                                            qDebug() << "Receiving elem: " << elem->getName()  << " (row in original list: " << row << ")";
                                        }
                                        else{
                                            qDebug() << "No UserRole...";
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            event->ignore();
                        }
                    }
                    

                    I'm getting the error message 'QVariant::save: unable to save type 'Element*' (type id: 1052)' before arriving to the Drop event. In the drop event it seems I receive something empty. I've no log in the loop.

                    Any idea why? do I have to rebuild the Drag manually?

                    1 Reply Last reply
                    0
                    • VRoninV Offline
                      VRoninV Offline
                      VRonin
                      wrote on last edited by
                      #10

                      The error comes from the fact that you have to register the metatype, not just declare it.

                      The real issue here is that you are passing pointers around without a clear definition of who owns what. Be very careful not to leak memory or having double deletions

                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                      ~Napoleon Bonaparte

                      On a crusade to banish setIndexWidget() from the holy land of Qt

                      mbruelM 1 Reply Last reply
                      0
                      • VRoninV VRonin

                        The error comes from the fact that you have to register the metatype, not just declare it.

                        The real issue here is that you are passing pointers around without a clear definition of who owns what. Be very careful not to leak memory or having double deletions

                        mbruelM Offline
                        mbruelM Offline
                        mbruel
                        wrote on last edited by mbruel
                        #11

                        @VRonin
                        Well it's kind of obscure to me how/when and mainly where we need to register the metatype.
                        I've no issue anywhere else putting, reading Element* or QSet<Element*> in a QVariant, simply having declared those types.
                        Why would it be different in this case for QListWidgetItem?
                        I've added the registration in my constructor of my QListWidget but I still have the same error "QVariant::save: unable to save type 'Element*' (type id: 1052)."

                        Here is what I've done:

                        #include "Model/Element.h"
                        ItemOfferingListWidget::ItemOfferingListWidget(QWidget *parent) :
                            QListWidget(parent), _allowIternalMove(true)
                        {
                            setSelectionMode(QAbstractItemView::ExtendedSelection);
                            setDragDropMode(QAbstractItemView::DragDrop);
                            setDropIndicatorShown(true);
                            qRegisterMetaType<Element *> ();
                        //    setDragDropOverwriteMode(true);
                        }
                        

                        For the use of raw pointers, I know it could be dangerous, but the architecture of the project is clear : I've a unique manager (Singleton) for each kind of object that is in charge of construction / deletion.

                        VRoninV 1 Reply Last reply
                        0
                        • mbruelM mbruel

                          @VRonin
                          Well it's kind of obscure to me how/when and mainly where we need to register the metatype.
                          I've no issue anywhere else putting, reading Element* or QSet<Element*> in a QVariant, simply having declared those types.
                          Why would it be different in this case for QListWidgetItem?
                          I've added the registration in my constructor of my QListWidget but I still have the same error "QVariant::save: unable to save type 'Element*' (type id: 1052)."

                          Here is what I've done:

                          #include "Model/Element.h"
                          ItemOfferingListWidget::ItemOfferingListWidget(QWidget *parent) :
                              QListWidget(parent), _allowIternalMove(true)
                          {
                              setSelectionMode(QAbstractItemView::ExtendedSelection);
                              setDragDropMode(QAbstractItemView::DragDrop);
                              setDropIndicatorShown(true);
                              qRegisterMetaType<Element *> ();
                          //    setDragDropOverwriteMode(true);
                          }
                          

                          For the use of raw pointers, I know it could be dangerous, but the architecture of the project is clear : I've a unique manager (Singleton) for each kind of object that is in charge of construction / deletion.

                          VRoninV Offline
                          VRoninV Offline
                          VRonin
                          wrote on last edited by VRonin
                          #12

                          @mbruel said in crash in QListWidget::dropEvent when I add a UserRole to my QListItemWidget :'(:

                          how/when and mainly where we need to register the metatype

                          Normally you call that method inside the main() so you can forget about it for the rest of your program.

                          Why would it be different in this case for QListWidgetItem?

                          It isn't

                          I've a unique manager (Singleton)

                          http://lengthily.blogspot.com/2017/02/one-size-fits-all.html

                          Also keep in mind that Qt-parent child relationship does not know about your design so it might delete stuff you didn't intend to

                          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                          ~Napoleon Bonaparte

                          On a crusade to banish setIndexWidget() from the holy land of Qt

                          mbruelM 1 Reply Last reply
                          0
                          • M Offline
                            M Offline
                            mpergand
                            wrote on last edited by mpergand
                            #13

                            There was a topic recently about this issue here:
                            https://forum.qt.io/topic/85798/save-qset-using-qsettings/17

                            Seems you need to implement the stream operators << >>

                            This code works without error:

                            class FooClass {};
                            Q_DECLARE_METATYPE(FooClass*)
                            
                            QDataStream &operator<<(QDataStream &out, const FooClass*& foo) {
                            
                                out<< foo;
                            
                                return out;
                            }
                            
                            QDataStream &operator>>(QDataStream &in, FooClass*& foo) {
                            
                                in>>foo;
                                return in;
                            }
                            
                            
                            int main(int argc, char *argv[])
                            {
                            qRegisterMetaType<FooClass *> ("fooclasspt");
                            FooClass foo;
                            QVariant v=QVariant::fromValue(&foo);
                            FooClass* fooPt=v.value<FooClass*>();
                            qDebug()<<&foo<<fooPt; // same address
                            
                            qRegisterMetaTypeStreamOperators<FooClass *> ();
                            
                            QByteArray encoded;
                            QDataStream stream(&encoded, QIODevice::WriteOnly);
                            stream<<v;
                            
                            return 0;
                            }
                            
                            mrjjM mbruelM 2 Replies Last reply
                            1
                            • M mpergand

                              There was a topic recently about this issue here:
                              https://forum.qt.io/topic/85798/save-qset-using-qsettings/17

                              Seems you need to implement the stream operators << >>

                              This code works without error:

                              class FooClass {};
                              Q_DECLARE_METATYPE(FooClass*)
                              
                              QDataStream &operator<<(QDataStream &out, const FooClass*& foo) {
                              
                                  out<< foo;
                              
                                  return out;
                              }
                              
                              QDataStream &operator>>(QDataStream &in, FooClass*& foo) {
                              
                                  in>>foo;
                                  return in;
                              }
                              
                              
                              int main(int argc, char *argv[])
                              {
                              qRegisterMetaType<FooClass *> ("fooclasspt");
                              FooClass foo;
                              QVariant v=QVariant::fromValue(&foo);
                              FooClass* fooPt=v.value<FooClass*>();
                              qDebug()<<&foo<<fooPt; // same address
                              
                              qRegisterMetaTypeStreamOperators<FooClass *> ();
                              
                              QByteArray encoded;
                              QDataStream stream(&encoded, QIODevice::WriteOnly);
                              stream<<v;
                              
                              return 0;
                              }
                              
                              mrjjM Offline
                              mrjjM Offline
                              mrjj
                              Lifetime Qt Champion
                              wrote on last edited by
                              #14

                              @mpergand
                              hmm, trying code. it does not seem to call
                              the operators ?

                              1 Reply Last reply
                              0
                              • M Offline
                                M Offline
                                mpergand
                                wrote on last edited by
                                #15

                                You're right, but :

                                If the stream operators are not implemented you've got the QVariant::save: unable to save type error

                                more over, if you change :
                                QDataStream &operator<<(QDataStream &out, const FooClass*& foo)
                                to
                                QDataStream &operator<<(QDataStream &out, const FooClass* foo)

                                without a reference to a pointer, it crashes here.

                                mrjjM 1 Reply Last reply
                                0
                                • M mpergand

                                  You're right, but :

                                  If the stream operators are not implemented you've got the QVariant::save: unable to save type error

                                  more over, if you change :
                                  QDataStream &operator<<(QDataStream &out, const FooClass*& foo)
                                  to
                                  QDataStream &operator<<(QDataStream &out, const FooClass* foo)

                                  without a reference to a pointer, it crashes here.

                                  mrjjM Offline
                                  mrjjM Offline
                                  mrjj
                                  Lifetime Qt Champion
                                  wrote on last edited by mrjj
                                  #16

                                  @mpergand
                                  yeah, its pretty odd why one cant step into the function then.
                                  ( since it wants it )

                                  I assume at run time, it maps to others overloads then.
                                  update:
                                  in any case, steaming it out/in and using it seems to work :)

                                  1 Reply Last reply
                                  0
                                  • M Offline
                                    M Offline
                                    mpergand
                                    wrote on last edited by
                                    #17

                                    I'm unable to retreive the variant.

                                    QDataStream out(&encoded, QIODevice::ReadOnly);
                                    QVariant v2;
                                    out>>v2;
                                    
                                    QDataStream &operator>>(QDataStream &in, FooClass*& foo) {
                                        in>>foo; -> It crashes here
                                    
                                    1 Reply Last reply
                                    0
                                    • VRoninV VRonin

                                      @mbruel said in crash in QListWidget::dropEvent when I add a UserRole to my QListItemWidget :'(:

                                      how/when and mainly where we need to register the metatype

                                      Normally you call that method inside the main() so you can forget about it for the rest of your program.

                                      Why would it be different in this case for QListWidgetItem?

                                      It isn't

                                      I've a unique manager (Singleton)

                                      http://lengthily.blogspot.com/2017/02/one-size-fits-all.html

                                      Also keep in mind that Qt-parent child relationship does not know about your design so it might delete stuff you didn't intend to

                                      mbruelM Offline
                                      mbruelM Offline
                                      mbruel
                                      wrote on last edited by
                                      #18

                                      @VRonin said in crash in QListWidget::dropEvent when I add a UserRole to my QListItemWidget :'(:

                                      http://lengthily.blogspot.com/2017/02/one-size-fits-all.html
                                      Also keep in mind that Qt-parent child relationship does not know about your design so it might delete stuff you didn't intend to

                                      Well, I said manager to not enter in the details... but basically it's a subclass of QApplication that owns my Model (which own all the Elements). It also owns the HMI if there is one.

                                      class MyApplication : public QApplication
                                      {
                                      private:
                                          const AppMode _mode;
                                          AppLang       _lang;
                                      
                                          Model        *_model;
                                          MainWindow   *_hmi;
                                      
                                          static const QMap<AppLang, QString> sAppLanguages;
                                          static const QString sTranslationsFileName;
                                      
                                          static const QString sVersion;
                                          static const QString sAppName;
                                      ...
                                      };
                                      

                                      However I use singleton for some thread safe objects as a convenient way to be able to get the instance from anywhere in the code. Mainly for to get my LogService, the IconFactory, the UndoStack and the IOService.
                                      I think it has some sense and it is practical to be able to do such calls:

                                      Iconfactory::getInstance()->getElementTypeIcon(elem->getElementType);
                                      UndoStack::getInstance()->beginMacro(macroLabel);
                                      

                                      Here is what the Singleton class I use:

                                      #ifndef SINGLETON_H
                                      #define SINGLETON_H
                                      
                                      template <typename T> class Singleton
                                      {
                                      protected:
                                          // Constructor /Destructor
                                          Singleton() = default;
                                          virtual ~Singleton() = default;
                                      
                                      public:
                                          Singleton(const Singleton &other) = delete;
                                          Singleton(const Singleton &&other) = delete;
                                          Singleton & operator=(const Singleton &other) = delete;
                                          Singleton & operator=(const Singleton &&other) = delete;
                                      
                                          // Public Interface
                                          static T *getInstance(){
                                              if (!_singleton) {
                                                  _singleton = new T;
                                              }
                                              return (static_cast<T*> (_singleton));
                                          }
                                      
                                          static void kill()
                                          {
                                              if (_singleton)
                                              {
                                                  delete _singleton;
                                                  _singleton = nullptr;
                                              }
                                          }
                                      
                                      private:
                                          // Unique instance
                                          static T *_singleton;
                                      };
                                      
                                      template <typename T> T *Singleton<T>::_singleton;
                                      #endif // SINGLETON_H
                                      

                                      Rq: for Services, I generally derive from this PureStaticClass

                                      class PureStaticClass
                                      {
                                          PureStaticClass() = delete;
                                          PureStaticClass(const PureStaticClass &other) = delete;
                                          PureStaticClass(const PureStaticClass &&other) = delete;
                                      
                                          PureStaticClass & operator=(const PureStaticClass &other) = delete;
                                          PureStaticClass & operator=(const PureStaticClass &&other) = delete;
                                      };
                                      

                                      @VRonin said in crash in QListWidget::dropEvent when I add a UserRole to my QListItemWidget :'(:

                                      Also keep in mind that Qt-parent child relationship does not know about your design so it might delete stuff you didn't intend to

                                      Yes I'm aware of that, I do it only for my Data Object that aren't updated directly by any QT objects. Those objects will have a pointer on them to extract some data but never delete it.

                                      1 Reply Last reply
                                      0
                                      • M mpergand

                                        There was a topic recently about this issue here:
                                        https://forum.qt.io/topic/85798/save-qset-using-qsettings/17

                                        Seems you need to implement the stream operators << >>

                                        This code works without error:

                                        class FooClass {};
                                        Q_DECLARE_METATYPE(FooClass*)
                                        
                                        QDataStream &operator<<(QDataStream &out, const FooClass*& foo) {
                                        
                                            out<< foo;
                                        
                                            return out;
                                        }
                                        
                                        QDataStream &operator>>(QDataStream &in, FooClass*& foo) {
                                        
                                            in>>foo;
                                            return in;
                                        }
                                        
                                        
                                        int main(int argc, char *argv[])
                                        {
                                        qRegisterMetaType<FooClass *> ("fooclasspt");
                                        FooClass foo;
                                        QVariant v=QVariant::fromValue(&foo);
                                        FooClass* fooPt=v.value<FooClass*>();
                                        qDebug()<<&foo<<fooPt; // same address
                                        
                                        qRegisterMetaTypeStreamOperators<FooClass *> ();
                                        
                                        QByteArray encoded;
                                        QDataStream stream(&encoded, QIODevice::WriteOnly);
                                        stream<<v;
                                        
                                        return 0;
                                        }
                                        
                                        mbruelM Offline
                                        mbruelM Offline
                                        mbruel
                                        wrote on last edited by
                                        #19

                                        @mpergand
                                        I've tried that and it doesn't work with work on my env. I'm still getting the issue:

                                        QVariant::save: unable to save type 'Element*' (type id: 1025).
                                        

                                        Here is my code:

                                        #include <QDataStream>
                                        class Element;
                                        inline QDataStream &operator>>(QDataStream &in, Element*& elem) {
                                        
                                            in >> elem;
                                            return in;
                                        }
                                        inline QDataStream &operator<<(QDataStream &in, Element*& elem) {
                                        
                                            in << elem;
                                            return in;
                                        }
                                        

                                        if I override the startDrag event like this:

                                        void ItemOfferingListWidget::startDrag(Qt::DropActions supportedActions)
                                        {
                                        //   return QListWidget::startDrag(supportedActions);
                                            Q_UNUSED(supportedActions)
                                            QMimeData       *mimeData = new QMimeData();
                                            QByteArray       encodedData;
                                            QDataStream stream(&encodedData, QIODevice::WriteOnly);
                                            for (QListWidgetItem *item  : selectedItems())
                                            {
                                                QMap<int, QVariant> data;
                                                data[Qt::UserRole] =  item->data(Qt::UserRole);
                                        //        Element *elem = item->data(Qt::UserRole).value<Element*>();
                                        //        qulonglong ptrval(*reinterpret_cast<qulonglong *>(&elem));
                                                stream << row(item) << data;
                                        //        stream << row(item) << elem->getId() << elem->getElementTypeName();
                                            }
                                            mimeData->setData("application/x-qabstractitemmodeldatalist", encodedData);
                                            QDrag *drag = new QDrag(this);
                                            drag->setMimeData(mimeData);
                                            drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
                                        }
                                        

                                        The error message kicks in when I try to write the data in the stream.
                                        I'm jumping in

                                        template <class Key, class T>
                                        inline QDataStream &operator<<(QDataStream &s, const QMap<Key, T> &map)
                                        {
                                            return QtPrivate::writeAssociativeContainer(s, map);
                                        }
                                        

                                        but never in my overriden function :(

                                        1 Reply Last reply
                                        0
                                        • VRoninV Offline
                                          VRoninV Offline
                                          VRonin
                                          wrote on last edited by VRonin
                                          #20

                                          My fault, I totally forgot you also need qRegisterMetaTypeStreamOperators

                                          P.S.
                                          I still don't like your design but at the end of the day if you are happy with it and it works, go for it

                                          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                                          ~Napoleon Bonaparte

                                          On a crusade to banish setIndexWidget() from the holy land of Qt

                                          mbruelM 1 Reply Last reply
                                          2

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved