Qt拖放助手类——简化Qt控件的拖拽程序编写

由于Qt程序中,拖拽功能被经常使用,网上很多例子都是直接对该类的两个虚函数进行重写:

protected:
    virtual void dragEnterEvent(QDragEnterEvent *event) override;
    virtual void dropEvent(QDropEvent *event) override;

但是,每个控件都对其这样书写使得代码简洁性降低,而且由于是重写,不得不在头文件中暴露上述两个虚函数。

所以,我一直在构思着,能不能写一个类,通过事件过滤器的方式,来简化代码呢?

答案是可以的。

话不多说,直接上代码:

dragdrophelper.h:

#ifndef DRAGDROPHELPER_H
#define DRAGDROPHELPER_H

#include 
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include 
#endif

class DragDropHelper : public QObject
{
    Q_OBJECT
public:
    enum  AllowedType { None = 0, File = 1, Directory = 2, All = File | Directory };
    Q_DECLARE_FLAGS(AllowedTypes, AllowedType)

    explicit DragDropHelper(QWidget *beneficiary, AllowedTypes types = File);
    void setAllowedTypes(AllowedTypes types);

signals:
    void resourceDropped(const QList &urls);

private:
    AllowedTypes types;
    virtual bool eventFilter(QObject *watched, QEvent *event) override;
    bool allResourceAllowed(const QList &urls);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(DragDropHelper::AllowedTypes)

#endif // DRAGDROPHELPER_H

dragdrophelper.cpp:

#include "dragdrophelper.h"

/*!
 * \brief 拖拽助手,可以简化一个类的拖拽实现
 * \details 被协助的对象的类无需重写dragEnterEvent()和dropEvent(),
 *              只需将resourceDropped()信号连接到资源处理的槽函数
 * \param beneficiary 接受协助的对象
 * \param types 允许拖入的对象的类型,不同类型可用或运算符 '|' 进行组合
 */
DragDropHelper::DragDropHelper(QWidget *beneficiary, AllowedTypes types) : QObject(beneficiary)
{
    this->types = types;
    beneficiary->setAcceptDrops(true);
    beneficiary->installEventFilter(this);
}

void DragDropHelper::setAllowedTypes(AllowedTypes types)
{
    this->types = types;
}

bool DragDropHelper::eventFilter(QObject *watched, QEvent *event)
{
    if ( event->type() == QEvent::DragEnter ) {
        QDragEnterEvent *e = dynamic_cast(event);
        if ( !e || !e->mimeData()->hasUrls() )
            return QObject::eventFilter(watched, event);
        if ( allResourceAllowed( e->mimeData()->urls() ) ) {
            e->acceptProposedAction();
            return true;
        }
    } else if ( event->type() == QEvent::Drop ) {
        QDropEvent *e = dynamic_cast(event);
        if ( e )
            emit resourceDropped( e->mimeData()->urls() );
    }
    return QObject::eventFilter(watched, event);
}

bool DragDropHelper::allResourceAllowed(const QList &urls)
{
    if ( urls.isEmpty() )
        return false;
    if ( types.testFlag(File) && types.testFlag(Directory) )
        return true;
    if ( types.testFlag(File) )
        foreach (QUrl url, urls)
            if ( !QFileInfo(url.toLocalFile()).isFile() )
                return false;
    if ( types.testFlag(Directory) )
        foreach (QUrl url, urls)
            if ( !QFileInfo(url.toLocalFile()).isDir() )
                return false;
    return true;
}

使用:(假设拖拽落下时,执行处理的对象为widget,处理的槽函数为loadFiles())

    DragDropHelper *dragDropHelper = new DragDropHelper(widget, DragDropHelper::File);
    connect(dragDropHelper, SIGNAL(resourceDropped(QList)), widget, SLOT(loadFiles(QList)));

这样一来,就免去了重写文章起始处提及的两个虚函数,只需要写一个槽函数就可以了。

另外,事件过滤器是可以动态安装/卸载的,这使得程序在运行期间可以选择是否响应某种行为。

 

你可能感兴趣的:(Qt,C++)