本文章从属于 Qt实验室-CSDN博客系列
拖放操作包括两个动作:拖动(drag)和放下(drop或称为放置)。
拖动允许
对于要拖出的窗口或控件,要setDragEnabled(true)
对于要拖入的窗口或控件,要setAcceptDrops(true)
下面以一个具体的用例进行说明
拖动列表控件中的项目
该用例实现从左边的列表窗口拖出,到右边的Widget窗口拖入
主界面设置
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
this->resize(1200,800);
//从ProjectListWidget拖动到MyWidget上
QSplitter* center=new QSplitter;
center->addWidget(new ProjectListWidget);
center->addWidget(new MyWidget);
center->setOrientation(Qt::Horizontal);
this->setCentralWidget(center);
}
左侧列表窗口设置
class ProjectListWidget : public QListWidget
{
Q_OBJECT
public:
ProjectListWidget();
// QAbstractItemView interface
protected:
void startDrag(Qt::DropActions supportedActions);
};
左侧窗口允许拖动其item,并且将item中的文字存入QMimeData,用以传输到右侧窗体中
ProjectListWidget::ProjectListWidget()
{
this->addItem("item1");
this->addItem("item2");
//(1)开启允许拖动,如果不开启是不会有拖动item移动的效果的
this->setDragEnabled(true);
}
//(2)开始拖动,设置了一种标记为x1的拖动数据
void ProjectListWidget::startDrag(Qt::DropActions supportedActions)
{
QString text=this->currentItem()->text();
QMimeData* mimeData=new QMimeData;
mimeData->setData("x1",text.toLocal8Bit());
QDrag* drag=new QDrag(this);
drag->setMimeData(mimeData);
drag->exec();
}
右侧窗体的实现会多些,首先必须允许拖拽进入事件dragEnterEvent,然后必须允许拖拽移动事件dragMoveEvent。最后实现dropEvent来接收数据。
MyWidget::MyWidget(QWidget *parent)
: QWidget{parent}
{
//(1)开启允许放置,如果不开启,拖动进入界面时将显示禁止符号
this->setAcceptDrops(true);
}
//(2)实现了以下两个方法后,该界面就能允许拖拽进入了
//对一种被标记为x1的拖动数据允许拖拽进入
void MyWidget::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat("x1"))
event->accept();
else
event->ignore();
}
void MyWidget::dragMoveEvent(QDragMoveEvent *event)
{
if (event->mimeData()->hasFormat("x1"))
event->accept();
else
event->ignore();
}
//(3)实现dropEvent来接收拖动携带的数据
void MyWidget::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasFormat("x1"))
{
QString text(event->mimeData()->data("x1"));
QPoint pos=event->pos();
//在这里将拖动过来的数据放入list,然后通过paintEvent()进行绘制
m_textList.append({text,pos});
event->accept();
this->update();
}
else
event->ignore();
}
void MyWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
for(int i=0;i<m_textList.size();i++)
{
QPoint pos=m_textList.at(i).second;
QString text=m_textList.at(i).first;
painter.drawText(pos,text);
}
}
最终的效果如下
事件发出顺序和传递规则
上图参考自 Qt拖放(1):拖放基本原理(QDrag类)-CSDN博客
关于 QDrag.exec()
void ProjectListWidget::startDrag(Qt::DropActions supportedActions)
{
//调用该方法的时机,点击item并移动鼠标,即进入该方法
//然后执行到drag->exec()阻塞
//exec()函数是一个阻塞函数(但不会阻塞主事件循环)
//也就是说,在松开鼠标之前,不会打印"after drag"
//但是窗口依然可以得到其他的事件响应,例如mainwindow依然可以响应QTimer触发的update()
QString text=this->currentItem()->text();
QMimeData* mimeData=new QMimeData;
mimeData->setData("x1",text.toLocal8Bit());
QDrag* drag=new QDrag(this);
drag->setMimeData(mimeData);
qDebug()<<"before drag";
drag->exec();
qDebug()<<"after drag";
}
MainWindow中构造时添加如下代码,
QTimer* timer=new QTimer(this);
timer->setInterval(1000);
connect(timer,&QTimer::timeout,[=]{
qDebug()<<"update...";
this->update();
});
timer->start();
测试在拖拽中不释放鼠标时,主窗口能否响应其他的事件(是可以的)
bool MainWindow::event(QEvent *event)
{
qDebug()<<"event::"<<event;
return QMainWindow::event(event);
}