Results 1 to 15 of 15

Thread: reordering QListWidgetItems of a QListWidget

  1. #1
    Join Date
    Nov 2007
    Posts
    35
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Question reordering QListWidgetItems of a QListWidget

    Qt Code:
    1. void MyListWidget::dropEvent(QDropEvent* event) {
    2. QList<QListWidgetItem *> before = getListWidgets();
    3. QListWidget::dropEvent(event);
    4. //TODO remove this one
    5. //TODO do not use the qtest lib, remove it from linker
    6. //QTest::qWait(5000);
    7. QList<QListWidgetItem *> after = getListWidgets();
    8. dbg() << "before" << before;
    9. dbg() << "after" << after;
    10. //TODO: compare before and after, if not equal, emit a signal
    11. }
    12.  
    13. QList<QListWidgetItem *> MyListWidget::getListWidgets(void) {
    14. QList<QListWidgetItem *> r(findItems("*",Qt::MatchWildcard));
    15. return r;
    16. }
    To copy to clipboard, switch view to plain text mode 

    MyListWidget is a QListWidget. What I'm trying to do is emitting a signal when the widgets are really reordered, as dropEvent sometimes occurs even when the order is the same. The problem is that the list "after" contains one more element, as QListWidget::dropEvent() duplicates the QListWidgetItem before moving it to the new position (QTest::qWait() proves it).

    My question is: how, where or when to attempt to get the state of the list widgets after the copy has been removed and everything is in place, so that a simple

    Qt Code:
    1. if(after != before) emit widgetsReordered;
    To copy to clipboard, switch view to plain text mode 

    will do the job ?

  2. The following user says thank you to OriginalCopy for this useful post:

    pbek (10th August 2017)

  3. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: reordering QListWidgetItems of a QListWidget

    Why do you reimplement dropEvent for the list widget? You should reimplement dropMimeData() instead. You'd immediately know what is being dropped, where and why and you can use that information to manipulate the list. But in general I think you should just connect to the layoutChanged() (or some other) signal of the listwidget model...

  4. #3
    Join Date
    Nov 2007
    Posts
    35
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: reordering QListWidgetItems of a QListWidget

    it behaves the same within dropMimeData(). There is no method layoutChange(), and I don't find anything similar.

  5. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: reordering QListWidgetItems of a QListWidget

    Quote Originally Posted by OriginalCopy View Post
    There is no method layoutChange(), and I don't find anything similar.
    Launch Qt Assistant and type in "layoutChanged" in the index tab.

    BTW. Of course it behaves the same within dropMimeData, because the base implementation of dropEvent calls dropMimeData to do its job. The point is to implement what you want inside dropMimeData based on arguments you are given. Testing whether the drop changes the order or not based on those arguments is trivial.

  6. The following user says thank you to wysota for this useful post:

    OriginalCopy (3rd December 2007)

  7. #5
    Join Date
    Nov 2007
    Posts
    35
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: reordering QListWidgetItems of a QListWidget

    thank you, I'll follow that path. However, it seems that Qt duplicates the list item, it drops the copy at the new position, and afterwards it removes the old one. This is not what one would normally describe as "moving object O from position A to position B", thus it messes pointer adresses (as a new instance is created at every drop). A simple dump:

    Debug: virtual bool MyListWidget::dropMimeData(int, const QMimeData*, Qt:ropAction) 41 src\MyListWidget.cpp index: 1 data QMimeData(0x4386548)
    Debug: virtual bool MyListWidget::dropMimeData(int, const QMimeData*, Qt:ropAction) 42 src\MyListWidget.cpp before (0x43e4d78, 0x4386368, 0x43fe8e0, 0x441f3c0)
    Debug: virtual bool MyListWidget::dropMimeData(int, const QMimeData*, Qt:ropAction) 43 src\MyListWidget.cpp after (0x43e4d78, 0x4386568, 0x4386368, 0x43fe8e0, 0x441f3c0)
    Debug: virtual bool MyListWidget::dropMimeData(int, const QMimeData*, Qt:ropAction) 44 src\MyListWidget.cpp return true
    Here, 0x4386568 is a duplicate of 0x441f3c0. Practically I moved the item at the address 0x441f3c0 to the index 1. But how could I decide which of the elements is duplicated ? (I know it in this given scenario because I've got the output while debugging it).

    As you can see, Qt screws with pointers and that hinders me to implement the fastest and the most elegant solution. Do you have any idea on how to avoid this problem ?


    I prefer to work with pointers, as it would be faster to compare if object A is the same as object B (ie they point to the same address) rather than doing things like "string comparison". In my particular example, string comparison is luckily possible, as there are not allowed items with the same ->text(). However, if there was no such constraint, it would be harder to identify the object which was moved.

    I've tried doing it like this:

    Qt Code:
    1. bool MyListWidget::dropMimeData(int index,const QMimeData* data,Qt::DropAction action) {
    2. QStringList before = getListWidgets();
    3. bool r = QListWidget::dropMimeData(index,data,action);
    4. QStringList after = getListWidgets();
    5. dbg() << connect(model(),SIGNAL(layoutChanged()),this,SLOT(layoutChanged()));
    6. dbg() << "index:" << index << "data" << data;
    7. dbg() << "before" << before;
    8. dbg() << "after" << after;
    9. dbg() << "return" << r;
    10. return r;
    11. }
    12.  
    13. void MyListWidget::layoutChanged(void) {
    14. dbg() << "//TODO: reordered";
    15. }
    To copy to clipboard, switch view to plain text mode 
    but it doesn't get connected

  8. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: reordering QListWidgetItems of a QListWidget

    Quote Originally Posted by OriginalCopy View Post
    However, it seems that Qt duplicates the list item, it drops the copy at the new position, and afterwards it removes the old one.
    What is wrong with that?
    This is not what one would normally describe as "moving object O from position A to position B"
    Hmm... I would
    thus it messes pointer adresses (as a new instance is created at every drop). A simple dump:


    Here, 0x4386568 is a duplicate of 0x441f3c0. Practically I moved the item at the address 0x441f3c0 to the index 1. But how could I decide which of the elements is duplicated ? (I know it in this given scenario because I've got the output while debugging it).
    What are these pointers?

    As you can see, Qt screws with pointers and that hinders me to implement the fastest and the most elegant solution. Do you have any idea on how to avoid this problem ?
    Yes, don't rely on pointers. Use the mime data to store the data of the original (you can store its position as well if you have to) and then compare it to the data obtained while dropping.

    I prefer to work with pointers, as it would be faster to compare if object A is the same as object B (ie they point to the same address) rather than doing things like "string comparison".
    Depends what you mean by "the same". You are using the simplest possible case here - dragging and dropping within the same widget. But you could be dropping on a different application than you are dragging from. Hard to compare pointers then...

    In my particular example, string comparison is luckily possible, as there are not allowed items with the same ->text(). However, if there was no such constraint, it would be harder to identify the object which was moved.
    Who said you should compare strings? Compare whatever you want. If you want to compare pointers, do it - Qt doesn't care what you store within the mime data object.

    Qt Code:
    1. bool MyListWidget::dropMimeData(int index,const QMimeData* data,Qt::DropAction action) {
    2. QStringList before = getListWidgets();
    3. bool r = QListWidget::dropMimeData(index,data,action);
    4. QStringList after = getListWidgets();
    5. dbg() << connect(model(),SIGNAL(layoutChanged()),this,SLOT(layoutChanged()));
    6. dbg() << "index:" << index << "data" << data;
    7. dbg() << "before" << before;
    8. dbg() << "after" << after;
    9. dbg() << "return" << r;
    10. return r;
    11. }
    12.  
    13. void MyListWidget::layoutChanged(void) {
    14. dbg() << "//TODO: reordered";
    15. }
    To copy to clipboard, switch view to plain text mode 
    but it doesn't get connected
    Most probably the layout doesn't change I told you the signal could be different. It's best to just check which signals are emitted in the drop within the same widget. Certainly dataChanged() might be one of them.

    BTW. It doesn't get connected (connect returns false) or the slot is not called? These are two completely different things.

    If you reimplement dropMimeData() in most cases you should also reimplement mimeData().

  9. #7
    Join Date
    Nov 2007
    Posts
    35
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: reordering QListWidgetItems of a QListWidget

    It doesn't get connected (connect returns false) or the slot is not called?
    connect returns true, yet slot not called.

    I'm getting nervous when I think how elegant would be the implementation where I'd work with only lists of pointers (here: before and after).

    Who said you should compare strings?
    I was referring to my specific problem in this project, where I'm actually interested in the ->text() values of the list items

    Depends what you mean by "the same". You are using the simplest possible case here - dragging and dropping within the same widget. But you could be dropping on a different application than you are dragging from. Hard to compare pointers then...
    same means in this context the same instance situated at the same memory address. In this program, I'm only handling drags and drops from/to the same list widget

    What are these pointers?
    they are at &before and &after, and their elements point to the addresses listed in the output:

    before (0x43e4d78, 0x4386368, 0x43fe8e0, 0x441f3c0)
    after (0x43e4d78, 0x4386568, 0x4386368, 0x43fe8e0, 0x441f3c0)
    They are the addresses of the QWidgetListItem's of the list widget

  10. #8
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: reordering QListWidgetItems of a QListWidget

    Quote Originally Posted by OriginalCopy View Post
    I was referring to my specific problem in this project, where I'm actually interested in the ->text() values of the list items
    I don't know what your project is, so it's hard to suggest anything. If you are interested only in texts why do you keep referring to pointers to items? I admit I'm having a hard time understanding what are you trying to do.

    they are at &before and &after, and their elements point to the addresses listed in the output:
    Hmm... How is GetListWidgets() implemented? I'm not really sure why you try to compare addresses here...

    Here is some code. It should work - for me it doesn't because my implementation of dropMimeData doesn't get called and I don't know why. If you manage to overcome it, it should be working just fine.

    Qt Code:
    1. #include <QApplication>
    2. #include <QListWidget>
    3. #include <QMimeData>
    4. #include <QMessageBox>
    5. #include <QtDebug>
    6.  
    7. class LW : public QListWidget {
    8. public:
    9. LW() : QListWidget(){
    10. setDragEnabled(true);
    11. setAcceptDrops(true);
    12. setDragDropMode(DragDrop);
    13. }
    14. protected:
    15. QStringList mimeTypes () const{
    16. return QStringList() << "application/x-internal";
    17. }
    18. QMimeData * mimeData ( const QList<QListWidgetItem *> items ) const{
    19. qDebug("MD: %d", items.count()); QMimeData *md = new
    20. QMimeData; int r = row(items.at(0));
    21. md->setData("application/x-internal", QString::number(r).toAscii());
    22. return md;
    23. }
    24. bool dropMimeData( int index, const QMimeData * data, Qt::DropAction action ){
    25. qDebug("DMD");
    26. QByteArray ba = data->data("application/x-internal");
    27. QString rstr(ba);
    28. int r = rstr.toInt();
    29. // move from r to index
    30. QMessageBox::information(this, "DRAG&DROP", QString("Taken from %1 and dropped to %2").arg(r).arg(index));
    31. if(r==index)
    32. return false;
    33. QListWidgetItem *item = takeItem(r);
    34. if(r<index){
    35. insertItem(index-1, item);
    36. } else {
    37. insertItem(index, item);
    38. }
    39. return true;
    40. }
    41.  
    42. };
    43. int main(int argc, char **argv){
    44. QApplication app(argc, argv);
    45. LW lw;
    46. for(int i=0; i<10; i++){
    47. new QListWidgetItem(QString("ITEM %1").arg(i+1), &lw);
    48. }
    49. lw.show();
    50. return app.exec();
    51. }
    To copy to clipboard, switch view to plain text mode 

  11. #9
    Join Date
    Nov 2007
    Posts
    35
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: reordering QListWidgetItems of a QListWidget

    getListWidgets() is in the first post: http://www.qtcentre.org/forum/p-reor...ostcount1.html

    what I want is MyListWidget to emit a signal when the order of the items changes. I keep referring to QListWidgetItem*'s because they are holding the ->text(). Another property of MyListWidget is that it doesn't accept "duplicate ->text()s", ie: it's not possible to have two items with the same text values.

    I feel that if I manage to solve this problem in an elegant way (or understand a solution proposed by you) I will learn a lot about Qt :-)

    The only thing that disturbs me is the garbage left by Qt while in dropMimeData. Yeah, it is cleaned up at a later point before returning to the calling event loop, but where ? Knowing that would light the way

    I will make a simplified version of the program, bbiaf
    Last edited by OriginalCopy; 3rd December 2007 at 20:04.

  12. #10
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: reordering QListWidgetItems of a QListWidget

    Quote Originally Posted by OriginalCopy View Post
    getListWidgets() is in the first post: http://www.qtcentre.org/forum/p-reor...ostcount1.html
    Something is wrong then because it returns a list of widget items and you assign it to a list of strings...

    what I want is MyListWidget to emit a signal when the order of the items changes.
    Have you tried QListView::indexesMoved ( const QModelIndexList & indexes )?

    And what is the purpose of knowing that the order has changed?

    The only thing that disturbs me is the garbage left by Qt while in dropMimeData.
    What garbage?

  13. #11
    Join Date
    Nov 2007
    Posts
    35
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: reordering QListWidgetItems of a QListWidget

    Something is wrong then because it returns a list of widget items and you assign it to a list of strings...
    yeah, I copy/pasted wrong. Here's a scheleton:

    //implementation:
    Qt Code:
    1. #include <QtGui>
    2. #include "mylistwidget.h"
    3.  
    4.  
    5. int main(int argc,char* argv[]) {
    6. QApplication::setStyle("plastique");
    7. QApplication app(argc,argv);
    8. MyListWidget *mainwindow = new MyListWidget;
    9. mainwindow->addItem("foo");
    10. mainwindow->addItem("bar");
    11. mainwindow->addItem("hello");
    12. mainwindow->addItem("world");
    13. Q_CHECK_PTR(mainwindow);
    14. mainwindow->show();
    15. return app.exec();
    16. }
    17.  
    18. MyListWidget::MyListWidget(QWidget* parent) : QListWidget(parent) {
    19. setFrameShape(QFrame::StyledPanel);
    20. setFrameShadow(QFrame::Sunken);
    21. setLineWidth(1);
    22. setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    23. setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    24. setTabKeyNavigation(true);
    25. setDragEnabled(true);
    26. setDragDropMode(QAbstractItemView::InternalMove);
    27. setSelectionMode(QAbstractItemView::ExtendedSelection);
    28. setTextElideMode(Qt::ElideLeft);
    29. setMovement(QListView::Free);
    30. setResizeMode(QListView::Adjust);
    31. setViewMode(QListView::ListMode);
    32. setUniformItemSizes(true);
    33. setSelectionRectVisible(false);
    34. }
    35. MyListWidget::~MyListWidget() {
    36.  
    37. }
    38.  
    39. bool MyListWidget::addItem(const QString& label) {
    40. //TODO do not add already existing items
    41. QList<QListWidgetItem *> items = findItems(label,Qt::MatchFixedString);
    42. if(items.isEmpty()) {
    43. QListWidget::addItem(label);
    44. return true;
    45. }
    46. return false;
    47. }
    48.  
    49. QList<QListWidgetItem *> MyListWidget::getListWidgets(void) {
    50. QList<QListWidgetItem *> r(findItems("*",Qt::MatchWildcard));
    51. return r;
    52. }
    53.  
    54. QStringList MyListWidget::getListLabels(void) {
    55. QList<QListWidgetItem *> t(findItems("*",Qt::MatchWildcard));
    56. QList<QListWidgetItem *>::const_iterator iter;
    57. for(iter = t.begin();iter != t.end();++iter) {
    58. r.append((*iter)->text());
    59. }
    60. return r;
    61. }
    To copy to clipboard, switch view to plain text mode 

    mylistwidget.h:

    Qt Code:
    1. #ifndef __MYLISTWIDGET_H__
    2. #define __MYLISTWIDGET_H__
    3. #include <QListWidget>
    4.  
    5. class MyListWidget : public QListWidget {
    6. Q_OBJECT
    7. public:
    8. MyListWidget(QWidget* parent=0);
    9. ~MyListWidget();
    10. public:
    11. bool addItem(const QString& label);
    12. QList<QListWidgetItem *> getListWidgets(void);
    13. QStringList getListLabels(void);
    14. };
    15.  
    16. #endif // __MYLISTWIDGET_H__
    To copy to clipboard, switch view to plain text mode 

    Have you tried QListView::indexesMoved ( const QModelIndexList & indexes )?
    yes, useless/not working

    And what is the purpose of knowing that the order has changed?
    the purpose lays deep enough in the things needed by the app that for simplicity's sake we should not even bother about "WHY?" (simple said: a kind of priority queue)

    MyListWidget should be as self-contained as possible, as I may need to use it in other places. A signal emitted when the order changes would bring the ideal modularity and reusability of the code.

  14. #12
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: reordering QListWidgetItems of a QListWidget

    I'd like to know an answer to the question I asked. Why? Because maybe there are better ways to achieve the same effect (for instance by implementing a simple model over your of widgets or whatever). There is no point in reinventing the wheel. Assuming that you have some widgets you want to reorder, a simple model wrapped around a vertical layout holding the widgets is enough to provide reordering and all other stuff you might think of without even touching the view.

  15. #13
    Join Date
    Nov 2007
    Posts
    35
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: reordering QListWidgetItems of a QListWidget

    think of mylistwidget as a priority queue. for now, I use it only for displaying settings to the user, but it is possible that I will use it for let's say, displaying trusted friends, or users of the system, or or or ...

  16. #14
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: reordering QListWidgetItems of a QListWidget

    So I suggest you implement a model for each of these and display them using QListView or other model-based widget. Right now you are really reinventing the wheel.

    Here is a simple example of a widget model (implemented just for fun). Of course it's not complete

    Qt Code:
    1. #include <QAbstractListModel>
    2. #include <QApplication>
    3. #include <QListView>
    4. #include <QPushButton>
    5. #include <QLayout>
    6.  
    7. class WidgetModel : public QAbstractListModel {
    8. public:
    9. WidgetModel(QBoxLayout *l, QObject *parent=0) : QAbstractListModel(parent){
    10. m_layout = l;
    11. }
    12.  
    13. QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const{
    14. QLayoutItem *item = m_layout->itemAt(index.row());
    15. QWidget *widget = item->widget();
    16. if(!widget) return QVariant();
    17. if(role == Qt::DisplayRole){ return widget->objectName(); }
    18. if(role == Qt::ToolTipRole){ return widget->toolTip(); }
    19. return QVariant();
    20. }
    21.  
    22. int rowCount ( const QModelIndex & parent = QModelIndex() ) const{
    23. return m_layout->count();
    24. }
    25.  
    26. QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const{
    27. return createIndex(row, column, m_layout->itemAt(row));
    28. }
    29.  
    30.  
    31. protected:
    32. QBoxLayout *m_layout;
    33. };
    34.  
    35. int main(int argc, char **argv){
    36. QApplication app(argc, argv);
    37. QVBoxLayout *l = new QVBoxLayout(&w);
    38. for(int i=0;i<10;i++){
    39. b->setObjectName(QString("PushButton_%1").arg(i+1));
    40. b->setToolTip(QString("Tool tip #%1").arg(i+1));
    41. l->addWidget(b);
    42. }
    43. WidgetModel model(l);
    44. lv.setModel(&model);
    45. lv.show();
    46. w.show();
    47. return app.exec();
    48. }
    To copy to clipboard, switch view to plain text mode 

  17. The following user says thank you to wysota for this useful post:

    OriginalCopy (4th December 2007)

  18. #15
    Join Date
    Nov 2007
    Posts
    35
    Thanks
    3
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: reordering QListWidgetItems of a QListWidget

    Right now I'm learning about MVC, interesting stuff in the manual. Thank you for your patience, help, information and everything.

Similar Threads

  1. Replies: 13
    Last Post: 15th December 2006, 11:52

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.