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";elsefileNames = 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();elseevent->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();elseevent->acceptProposedAction();}else{if(isSelf)event->ignore();elseevent->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设置其它格式对象。