Custom QListWidget



  • Hi guys,

    I'm trying to create an interface like this :
    https://www.dropbox.com/s/owzgk3bq7k9ix3f/interface.png

    I previously tried with QListView and custom model, and failed hard (was complex to code and was not happy with the result), so i'm trying a new approach.

    I want to use QListWidget and QListWidgetItem. Each box would be a QListWidgetItem (a custom widget I create)
    I started up a basic example :
    https://www.dropbox.com/s/nei0gqjs889jp9y/current.png

    The "add" button call this slot, which is supposed to add my custom Widget in the QListWidget:

    @void WorkoutCreator::on_pushButton_addStep_clicked()
    {
    QListWidgetItem *newItem = new QListWidgetItem;
    newItem->setText("itemText");

    IntervalWidget *intervalWidget = new IntervalWidget();
    ui->listWidget->setItemWidget(newItem, intervalWidget);
    
    ui->listWidget->insertItem(0, newItem);
    

    }@

    However, the widget shown is a standard QListItem, anything i'm missing?
    Also i'm considering other option, maybe using a QVBoxLayout and adding Widget directly in it, but I suspect drag and drop functionality will be a pain to write..

    Thanks!



  • I think i'm going to go and try to replicate the Qt Creator designer (drag and drop widget, ..)

    I use a QVBoxLayout and add directly custom QWidget inside it
    https://www.dropbox.com/s/uzi8qrsf4esoawr/test1.png
    No need for model data, every time the Layout change, I will create my model data from the current ui
    Still much work to be done, drag and drop, etc, will post when it's done with working code

    @void WorkoutCreator::on_pushButton_addStep_clicked()
    {
    IntervalWidget *intervalWidget = new IntervalWidget();
    ui->verticalLayout_main->addWidget(intervalWidget);
    }@


  • Lifetime Qt Champion

    Hi,

    Since you where using a QListWidget why not implement a QStyledItemDelegate and a custom editor ?



  • The problem I had with the Model/QStyledItemDelegate architecture; I had to reimplement fake painting of the widget (because my widget is complex, not just a one line text edit... then detect when the widget is clicked so that the editor is drawn (actual widget) on top of the fake widget. All this needed 2-3 classes and lot of coding.

    On top of that, not every row in my QListView will be the same widget, I have 2 types of widget to insert, I think you can only have 1 delegateForRow per QListView?
    My structure is like this :
    #1- Simple Widget (orange box in picture **1)
    #2- Widget QGroupBox, containing one or multiple #1 (repeat box in picture **1)
    #3 - Parent Widget (QVBoxLayout) containing one or multiple #1, #2 (whole QVBoxLayout in **1)

    Here is a prototype of what I have so far :
    **1 - https://www.dropbox.com/s/8fi21mtodvqa2bo/interfaceNow1.png
    What I'm aiming at (similar):
    **2 - https://www.dropbox.com/s/jav6ouufa2ft2jl/workoutCreatorGarmin.png

    You think what i'm doing is possible? Adding custom widgets to a QVBoxLayout, then retrieve data widgets order from the QVBoxLayout, create data structure from what is inside every Widget. Also need drag and drop inside the QVBoxLayout.

    Thanks!



  • So far I can traverse the QVBoxLayout easily

    @ for (int i = 0; i < ui->verticalLayout_main->count(); i++)
    {
    qDebug() << "checking widget Data..";
    qDebug() << ui->verticalLayout_main->itemAt(i)->widget();

        if (ui->verticalLayout_main->itemAt(i)->widget()->objectName() == "IntervalWidget") {
            qDebug() << "intervalWidget found! parse it";
        }
        else {  /// GroupBox of Interval Widget, parse each of them, recursive
            qDebug() << "QGroupBox found! parse it";
        }
    }@

  • Lifetime Qt Champion

    The drag and drop can't be done on a QVBoxLayout but that's not a problem. QScrollArea + custom widget handling the drop event (and using a QVBoxLayout) and you're good to go.

    As for the delegate, nothing forbids you to draw different things based on the model data and provide different editors. Take for example the default delegate, you don't set anything special and it gives you the adequate representation.



  • Thanks SGaist, I want to try the homemade solution first :)

    I made custom QWidget container that has a QVBoxLayout so I can get dragDrop.
    So far I get inside the dragEnterEvent and the DropEvent fine in the container.
    Would it be possible to set a Drop Indicator in my container? I searched for such function but didn't find. I'm only using "setAcceptDrops(true);" on my container but it doesn't show the drop indicator even though it's working.

    Current Ui Strucutre :
    https://www.dropbox.com/s/p0vwzz794qefxng/currentUi1.png


  • Lifetime Qt Champion

    IIRC you have to call acceptProposedAction on the dragEnterEvent and dragMoveEvent functions to show it.



  • Hi good monday, I read "this guide":http://qt-project.org/doc/qt-5/dnd.html

    It doesn't mention how to show the drop indicator, even when it get to "event.acceptProposedAction(), nothing is shown on my custom container, I guess I probably have to subclass some other class and paint the drop indicator myself, I don't really want to get into that...I tried by subclassing "dragMoveEvent " in the container but this function doesn't get called. I'll try from sratch with a normal QListWidget and setIndexWidget. Hopefully I can find a way not to use delegate and editor I hate this stuff.. Thanks

    My code for reference if one day you want to add a showDropIndicator function would be nice..

    Custom container

    @void DroppableWidget::dragEnterEvent(QDragEnterEvent *event) {

    //// No drop indicator shown.. not good
    qDebug() << "dragEnterEvent";
    if (event->mimeData()->hasFormat("mt/interval") ) {
        qDebug() << "OK CAN ACCEPT!";
        event->acceptProposedAction();
    }
    

    }

    void DroppableWidget::dropEvent(QDropEvent *event) {

    if (event->proposedAction() == Qt::MoveAction) {
        qDebug() << "dropEvent  - Move Action";
        event->acceptProposedAction();
        /// change widget position in the QVBoxLayout...
        ///    QVBoxLayout *verticalLayout = static_cast<QVBoxLayout*>(this->layout());
    }
    

    }@

    Custom widget (inside the container)

    @void IntervalWidget::mousePressEvent(QMouseEvent *event) {

    if (event->button() == Qt::LeftButton)
        dragStartPosition = event->pos();
    

    }

    //////////////////////////////////////////////////////////////////////////////////////////
    void IntervalWidget::mouseMoveEvent(QMouseEvent *event) {

    if (!(event->buttons() & Qt::LeftButton))
        return;
    if ((event->pos() - dragStartPosition).manhattanLength()
            < QApplication::startDragDistance())
        return;
    
    QDrag *drag = new QDrag(this);
    QMimeData *mimeData = new QMimeData;
    
    QByteArray output;
    mimeData->setData("mt/interval", output);
    drag->setMimeData(mimeData);
    
    ///Set the row of the data moving, at destination, switch widget position
    
    qDebug() << "ok doing drag now.";
    //// Image when moving the widget
    QPixmap pixmap(this->rect().size());
    this->render(&pixmap, QPoint(), QRegion(this->rect()));
    
    drag->setPixmap(pixmap);
    Qt::DropAction dropAction = drag->exec(Qt::MoveAction&#41;;
    

    }@
    .



  • I think the dropIndicator not showing is related to my custom Widget missing something.

    I tried with a normal QListWidget here :
    https://www.dropbox.com/s/fofinniz9ctsidz/droPIndocator.png

    And a normal item can be drag and dropped, but not my custom widget

    @void WorkoutCreate::on_pushButton_add_clicked()
    {

    ui->listWidget->addItem("Foo");
    
    QListWidgetItem* item;
    item = new QListWidgetItem(ui->listWidget);
    ui->listWidget->addItem(item);
    
    IntervalWidget *intervalWidget = new IntervalWidget();
    item->setSizeHint(intervalWidget->minimumSizeHint());
    ui->listWidget->setItemWidget(item, intervalWidget);
    

    }
    @

    EDIT:
    Found the problem, my widget was not actually "selected" when I click it, If I click in the free space near it, it get selected and I can drag and drop it, I have to fix the selection and it should work


Log in to reply
 

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