Qt Drag Dynamic Dustbin

A few days ago, I saw the drag and drop of Qt, and I thought that it was quite flexible. However, in the similar app application scenarios on the mobile terminal, the widgets did seem a bit clumsy. But at the same time, it has powerful functions and high flexibility.

What I have implemented here is a dynamic garbage can similar to the android mobile phone when it unloads the software. Let's first look at the effect.


Support automatic scaling, adaptive.



Obviously, the QListWidget is the most convenient way to store the dragged target, which is also a typical application of drag and drop. QPropertyAnimation is used in the garbage can animation, and the other is to listen to the drag event. Please see the code below

#include "widget.h"
#include <QApplication>
#include <QListWidget>
#include <QPropertyAnimation>
#include <QVBoxLayout>
#include <QPushButton>
#include <QBitmap>
#include <QPainter>
#include <QResizeEvent>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QVBoxLayout *lay = new QVBoxLayout(this);
    listWidget = new QListWidget;
    listWidget->setViewMode(QListWidget::IconMode); //Set display mode
    listWidget->addItem(new QListWidgetItem(QIcon(":/img/test.png"),QStringLiteral("test"))); //Put an item into ListWidget
    listWidget->addItem(new QListWidgetItem(QIcon(":/img/trash.png"),QStringLiteral("trash")));//Put an item into ListWidget
    listWidget->installEventFilter(this); //Add an event listener to the ListWidget to detect whether the item item is dragged
    lay->addWidget(listWidget); //Main layout

    reRect = new QLabel(this);            //Top bin background label
    reRect->setAutoFillBackground(true);
    QPalette pal = reRect->palette();
    pal.setBrush(QPalette::Background,Qt::lightGray); //Set background color
    reRect->setPalette(pal);
    QPixmap pix(":/img/trash.png");
    reRect->resize(width(),pix.height() + 20);  //Reset label size
    reRect->setMinimumHeight(pix.height() + 20); 

    reLabel = new QLabel;                //Trash
    reLabel->resize(pix.size());        //Adjust the label size of garbage can
    reLabel->setPixmap(pix);
    reLabel->setMask(pix.mask());
    reLabel->setAcceptDrops(true);     //To be able to accept the drop of drag
    reLabel->installEventFilter(this); //Add event listener
    QHBoxLayout *hlay = new QHBoxLayout(reRect);
    hlay->addWidget(reLabel,0,Qt::AlignCenter); //Center in background label

    ani = new QPropertyAnimation(reRect, "geometry"); //Create a new animation and bind the geometry of the background label as the attribute
    ani->setEasingCurve(QEasingCurve::InCurve);       //Set animation interpolation method
    ani->setDuration(150);                            //Animation duration
    connect(ani,&QPropertyAnimation::finished,[&]{ani->setDirection(QAbstractAnimation::Direction(!ani->direction()));}); //Change direction
}

void Widget::resizeEvent(QResizeEvent *event)
{
    reRect->resize(event->size().width(),reRect->height()); //When the window changes, reset the background label size
    if(initFlag){      //When initialized for the first time, move outside the form
        initFlag = false;
        reRect->move(reRect->x(),-reRect->height());
    }
    ani->setStartValue(QRect(reRect->x(),-reRect->height(),reRect->width(),reRect->height())); //Set animation start value
    ani->setEndValue(reRect->rect());   //Set animation end value
    return QWidget::resizeEvent(event);
}

Widget::~Widget()
{

}

bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    if(reLabel = qobject_cast<QLabel*>(watched)){
        if(event->type() == QEvent::DragEnter){
            QDragEnterEvent *drageEnterE{nullptr};
            try{
                drageEnterE = dynamic_cast<QDragEnterEvent*>(event);
            }catch(std::bad_cast &e){
                qDebug() << e.what();
            }
            if(drageEnterE){
                trashPainted = false;
                drageEnterE->acceptProposedAction(); //Enable receive drag when the drag event enters
            }
            return true;
        }else if(event->type() == QEvent::Drop){
            QDropEvent *dropE{nullptr};
            try{
                dropE = dynamic_cast<QDropEvent*>(event);
            }catch(std::bad_cast &e){
                qDebug() << e.what();
            }
            if(dropE){
                listWidget->takeItem(listWidget->row(listWidget->currentItem())); //When you drop in the trash, delete the current item from the ListWidget and restore the bin color
                QPixmap *pix = const_cast<QPixmap*>(reLabel->pixmap());
                *pix = QPixmap(":/img/trash.png");
                reLabel->repaint();
            }
            return true;
        }else if(event->type() == QEvent::DragMove){
            QDragMoveEvent *mvEvent{nullptr};
            try{
                mvEvent = dynamic_cast<QDragMoveEvent*>(event);
            }catch(std::bad_cast &e){
                qDebug() << e.what();
            }
            if(mvEvent && !trashPainted){   //Change the bin color as the item floats over the bin
                QPixmap *pix = const_cast<QPixmap*>(reLabel->pixmap());
                QPainter p(pix);
                p.initFrom(pix);
                p.fillRect(pix->rect(),QColor(200,0,0,50));
                reLabel->repaint();
                trashPainted = !trashPainted;
            }
            return true;
        }else if(event->type() == QEvent::DragLeave){
            QDragLeaveEvent *leEvent{nullptr};
            try{
                leEvent = dynamic_cast<QDragLeaveEvent*>(event);
            }catch(std::bad_cast &e){
                qDebug() << e.what();
            }
            if(leEvent){
                QPixmap *pix = const_cast<QPixmap*>(reLabel->pixmap()); //Restore bin color when drag event leaves
                *pix = QPixmap(":/img/trash.png");
                reLabel->repaint();
            }
            return true;
        }
    }else if(listWidget == qobject_cast<QListWidget*>(watched)){
        QChildEvent *childEvent{nullptr};
        switch (event->type()) {
        case QEvent::ChildAdded:
            try{
                childEvent = dynamic_cast<QChildEvent*>(event);
            }catch(std::bad_cast &e){
                qDebug() << e.what();
            }
            if(childEvent){  //Start the animation when the item item is treated as a drag
                ani->start();
            }
            break;
        case QEvent::ChildRemoved:
            try{
                childEvent = dynamic_cast<QChildEvent*>(event);
            }catch(std::bad_cast &e){
                qDebug() << e.what();
            }
            if(childEvent){  //When the item drag ends, start the animation
                ani->start();
            }
            break;
        }
    }
    return QWidget::eventFilter(watched,event);
}

I made it relatively simple. If it is perfect, it should be in the last part of the code. When an item is dragged, the direction of animation execution should be judged. If the animation still leaves the form, the animation should be stopped first, then the direction should be changed, and then the animation can be started. The same is true when the drag ends. However, the duration of the animation I set here is relatively short, which is not a problem.

Need source code can Download everywhere



Tags: Qt Mobile Android Attribute

Posted on Tue, 07 Jul 2020 11:55:23 -0400 by Techissue2008