Qt拖放文件实现(Drag,Drop, MimeData )

1 背景

本文讲述了基于Qt实现文件拖放操作。拖放操作至少需要两个窗口,一个作为拖放源窗口,另一个作为拖放目标窗口。

2 实现

这里从QTreeView派生一个类TreeView,处理鼠标开始拖放操作及拖放事件。这个类既是拖放源窗口也是拖放目标窗口。

2.1 TreeView定义

TreeView定义如下:

class TreeView : public QTreeView
{
    Q_OBJECT
public:
    TreeView(QWidget *parent = nullptr);

signals:
    void prepareDrag(QPoint const& point);
    void dragEnter(QDragEnterEvent * event);
    void dragMove(QDragMoveEvent * event);
    void drop(QDropEvent * event);

protected:
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);

    void dragEnterEvent(QDragEnterEvent * event);
    void dragMoveEvent(QDragMoveEvent * event);
    void dropEvent(QDropEvent * event);
private:
    QPoint pressPoint;
    bool mousePress;
    bool isDrag;
};

说明:

  • 信号prepareDrag是开始拖放信号,这时TreeView是拖放源窗口。
  • 信号dragEnter是TreeView做为拖放目标窗口,有拖放进入该窗口时发送的信号。
  • 信号dragMove是TreeView做为拖放目标窗口,有拖放在该窗口移动时发送的信号。
  • 信号drop是TreeView做为拖放目标窗口,有拖放在该窗口完成拖放时发送的信号。
  • 重载函数mousePressEvent,mouseMoveEvent和mouseReleaseEvent处理鼠标事件,发送开拖放信号。
  • 重载函数dragEnterEvent,dragMoveEvent和dropEvent处理拖放事件。

2.2 TreeView实现

2.2.1 构造函数

TreeView::TreeView(QWidget *parent)
    : QTreeView(parent)
    , mousePress(false)
    , isDrag(false)
{
    setMouseTracking(true);
    setAcceptDrops(true);
}

说明:

  • 通过setMouseTracking将鼠标设置为可跟踪的,以便处理鼠标移动操作。
  • 通过setAcceptDrops将对象设置为拖放目标窗口。

2.2.2 鼠标事件

void TreeView::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        pressPoint = event->pos();
        mousePress = true;
    }
    QTreeView::mousePressEvent(event);
}

void TreeView::mouseMoveEvent(QMouseEvent *event)
{
    if(mousePress)
    {
        int distance = (event->pos() - pressPoint).manhattanLength();
        if(distance >= QApplication::startDragDistance())
        {
            mousePress = false;
            emit prepareDrag(pressPoint);
        }
        isDrag = true;
    }
    if(isDrag)
        return;
    QTreeView::mouseMoveEvent(event);
}

void TreeView::mouseReleaseEvent(QMouseEvent *event)
{
    mousePress = false;
    isDrag = false;
    QTreeView::mouseReleaseEvent(event);
}

说明:

  • 在鼠标按下事件中,记下按下时鼠标位置pressPoint.
  • 在鼠标移动事件中,根据移动距离判断是否是启动拖放操作。
  • 是拖放操作设置isDrag为true。
  • 在鼠标释放事件中,恢复状态。

2.2.3 拖放事件

void TreeView::dragEnterEvent(QDragEnterEvent * event)
{
    emit dragEnter(event);
}

void TreeView::dragMoveEvent(QDragMoveEvent * event)
{
    emit dragMove(event);
}

void TreeView::dropEvent(QDropEvent * event)
{
    emit drop(event);
}

说明:拖放事件处理很简单,分别发送对应的信号。

3 使用

从QWidget派生一个类型Widget.

3.1 Widget 定义

Widget 定义如下:

class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void beginDrag(QPoint const& point);
    void dragEnter(QDragEnterEvent * event);
    void dragMove(QDragMoveEvent * event);
    void drop(QDropEvent * event);
private:
    Ui::Widget *ui;
};

3.2 Widget实现

下面分别讲述一下构造函数,beginDrag,dragEnter,dragMove和drop的实现.

3.2.1 构造函数

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    
    connect(ui->treeview, SIGNAL(prepareDrag(QPoint)),
                    this, SLOT(beginDrag(QPoint)));
    connect(ui->treeview, SIGNAL(dragEnter(QDragEnterEvent*)),
                    this, SLOT(dragEnter(QDragEnterEvent*)));
    connect(ui->treeview, SIGNAL(dragMove(QDragMoveEvent*)),
                    this, SLOT(dragMove(QDragMoveEvent*)));
    connect(ui->treeview, SIGNAL(drop(QDropEvent*)),
                    this, SLOT(drop(QDropEvent*)));
}

该函数将对象TreeView的4个信号与Widget对应的槽函数连接起来。

3.2.2 beginDrag

#define FILE_URL_HEADER "file:///"
void Widget::beginDrag(QPoint const& point)
{
    QModelIndex index = ui->treeview->indexAt(point);
    if(!index.isValid())
        return;

    QStringList fileNames;
    if(index.row() % 2)
        fileNames = QStringList() << FILE_URL_HEADER + "D:/file1.txt";
    else
        fileNames = QStringList() << FILE_URL_HEADER + "D:/file1.txt" 
            << FILE_URL_HEADER + "D:/file2.txt";

    QString text = fileNames.join("\n");

    if(fileNames.size() > 1)
        text += "\n";

    QDrag *drag = new QDrag(ui->treeview);
    QMimeData* mineData = new QMimeData();
    mineData->setData("text/uri-list", text.toUtf8());
    drag->setMimeData(mimeData);
    drag->setPixmap(QPixmap(":/image/copy.png"));
    drag->exec(Qt::LinkAction | Qt::MoveAction | Qt::CopyAction , Qt::CopyAction);
}

函数流程:

  • 根据point获取鼠标是否选择对象,如果选中对象开始拖放,否则不拖放。
  • 构造文件名数组,注意文件名以file:///为前缀。
  • 以ui->treeview为参数创建拖放对象drag。
  • 创建QMimeData对象mineData,通过setData将文件名设置给类型text/uri-list。
  • 以mineData为参数来设置drag的MimeData。
  • 设置drag的Pixmap作为拖放时图标显示。
  • 开始进入拖放状态,默认拖放动作是Copy。同时也支持Link和Move拖放动作。

3.2.3 dragEnter

void Widget::dragEnter(QDragEnterEvent* event)
{
    QMimeData const* mimeData = event->mimeData();
    if(mimeData)
        event->acceptProposedAction();
    else
    	event->ignore()
}

该函数是拖放状态下鼠标进入窗口时调用。可以有如下选择:

  • 择接受拖放请求,这时拖放图标显示为鼠标箭头+复制图标
  • 不接受拖放请求调用,这时拖放图标显示为红色禁止+复制图标。

3.2.4 dragMove

void Widget::dragMove(QDropEvent* event)
{
    QModelIndex index = ui->treeview->indexAt(event->pos());
    bool isSelf = (event->source() == ui->treeview);
    if(index.isValid())
    {
        if(isSelf && (index.row() % 2))
            event->ignore();
        else
            event->acceptProposedAction();
    }
    else
    {
        if(isSelf)
            event->ignore();
        else
            event->acceptProposedAction();
    }
}

该函数是拖放状态下鼠标在窗口中移动时调用。可以根据实际应用需要选择接受还是拒绝拖放请求。

3.2.5 drop

void Widget::drop(QDropEvent* event)
{
    QMimeData const* mimeData = event->mimeData();
    if(!mimeData)
        return;

    QString text = mimeData->text().remove(FILE_URL_HEADER);
    if(text.endsWith("\n"))
        text.remove(text.size() - 1, 1);

    QStringList fileNames = text.split("\n");

    if(event->dropAction() == Qt::MoveAction)
    {
        // move files
    }
    else if(event->dropAction() == Qt::CopyAction)
    {
        // copy files
    }
    else if(event->dropAction() == Qt::LinkAction)
    {
        // create shortcuts
    }
}

该函数是拖放状态下并且接受拖放请求,鼠标释放时调用函数。
函数流程:

  • 从mimeData中获取文件名。
  • 根据拖放动作执行对应的操作。
    说明:默认是Copy动作,在拖放状态下按Alt或Alt+Shift键可以切换支持的拖放动作。

4 总结

本文讲述了Qt下文件拖放操作,其实不只是文件操作,其它操作也是可以,只需给给mineData设置对应对象就可以。另外本文描述文件拖放操作,只是自己程序内部拖放操作。与文件资源管理器相互拖放还需要给mineData设置其它格式对象。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flysnow010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值