通过一个例子学习 QTreeWidget 节点的拖拽功能。本示例仅实现树形结构的自我拖拽,用来更改上下级关系或者调整节点顺序。
在这里插入图片描述

子类化 QTreeWidget

  1. 概述
    通过子类化 QTreeWidget 重新实现相关事件,以实现自定义功能。
  2. 头文件
#ifndef MYTREEWIDGET_H
#define MYTREEWIDGET_H
#include <QTreeWidget>
class MyTreeWidget : public QTreeWidget
    Q_OBJECT
public:
    explicit MyTreeWidget( QWidget * parent = nullptr);
protected:
    //需要重新实现的事件
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void dragEnterEvent(QDragEnterEvent * event);
    void dropEvent(QDropEvent *event);
    void dragMoveEvent(QDragMoveEvent *event);
private:
    QPoint startDragPoint;
#endif // MYTREEWIDGET_H
#include "mytreewidget.h"
#include <QMouseEvent>
#include <QApplication>
#include "dragimage.h"
#include "treeitemmimedata.h"
#include <QDrag>
#include "dragmodedialog.h"
MyTreeWidget::MyTreeWidget(QWidget *parent):QTreeWidget(parent)
    this->setSelectionMode(QAbstractItemView::SingleSelection);
    this->setDragEnabled(true);
    this->viewport()->setAcceptDrops(true);
    this->setDragDropMode(QAbstractItemView::InternalMove);
void MyTreeWidget::mousePressEvent(QMouseEvent *event)
    if( event->button() == Qt::LeftButton )
        startDragPoint = event->pos();
    QTreeWidget::mousePressEvent(event);
void MyTreeWidget::mouseMoveEvent(QMouseEvent *event)
    if( event->buttons() & Qt::LeftButton)//判断左键按下
        int distance = (event->pos()-startDragPoint ).manhattanLength();
        if( distance > QApplication::startDragDistance())
             QTreeWidgetItem * item = this->currentItem();
             DragImage *dragimag = new DragImage;
             dragimag->setShowText(item->text(0));
             dragimag->setShowIcon(item->icon(0));
             QPixmap pixmap = dragimag->grab();
             TreeItemMimeData *mimedata = new TreeItemMimeData;
             mimedata->SetDragData("ItemMimeData",item);
             QDrag *drag = new QDrag(this);
             drag->setMimeData(mimedata);
             drag->setPixmap(pixmap);
             drag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2));
             if( drag->exec(Qt::MoveAction) == Qt::MoveAction)
                 delete  item;
                 item = nullptr;
    QTreeWidget::mouseMoveEvent(event);
void MyTreeWidget::dragEnterEvent(QDragEnterEvent *event)
    if (event->mimeData()->hasFormat("ItemMimeData") &&  event->source() == this )
        event->setDropAction(Qt::MoveAction);
        event->accept();
        event->ignore();
void MyTreeWidget::dropEvent(QDropEvent *event)
    if(event->mimeData()->hasFormat("ItemMimeData") && event->source() == this )
        const TreeItemMimeData *pMimeData = (const TreeItemMimeData *)(event->mimeData());
        const QTreeWidgetItem *item = pMimeData->DragItemData();
        QTreeWidgetItem *pItem = item->clone();
        QTreeWidgetItem *currentItem = this->itemAt(event->pos());
        bool canceldrag = false;
        if( currentItem == nullptr )
            this->addTopLevelItem(pItem);
            QTreeWidgetItem * currenParent = currentItem->parent();
            DragModeDialog *dlg = new DragModeDialog;
            dlg->setShowText(pItem->text(0),currentItem->text(0));
            dlg->setShowPixm(pItem->icon(0).pixmap(20,20));
            if( dlg->exec() == QDialog::Accepted)
                DragModeDialog::DragMode dragmode = dlg->selMode;
                switch (dragmode)
                    case DragModeDialog::DragAsChild:
                        currentItem->addChild(pItem);
                        break;
                    case DragModeDialog::DragToUp:
                        currenParent == nullptr ? insertTopLevelItem(indexOfTopLevelItem(currentItem),pItem) : currenParent->insertChild(currenParent->indexOfChild(currentItem),pItem);
                        break;
                    case DragModeDialog::DragToDown:
                        currenParent == nullptr ? insertTopLevelItem(indexOfTopLevelItem(currentItem)+1,pItem) : currenParent->insertChild(currenParent->indexOfChild(currentItem)+1,pItem);
                        break;
                canceldrag = true;
        if( !canceldrag)
            event->setDropAction(Qt::MoveAction);
            event->accept();
            event->ignore();
        event->ignore();
void MyTreeWidget::dragMoveEvent(QDragMoveEvent *event)
    if (event->mimeData()->hasFormat("ItemMimeData") && event->source() == this )
         const TreeItemMimeData *pMimeData = (const TreeItemMimeData *)(event->mimeData());
         const QTreeWidgetItem *item = pMimeData->DragItemData();
         QTreeWidgetItem *currentItem = this->itemAt(event->pos());
         bool isok = true;
         while(currentItem)
             if(currentItem == item)
                 isok = false;
                 break;
             currentItem = currentItem->parent();
         if( isok )
             event->setDropAction(Qt::CopyAction);
             event->accept();
             event->ignore();
        event->ignore();

子类化 QMimeData

  1. 概述
    通过子类化 QMimeData 实现自定义数据类型的传递。
  2. 头文件
#ifndef TREEITEMMIMEDATA_H
#define TREEITEMMIMEDATA_H
#include <QMimeData>
class QTreeWidgetItem;
class TreeItemMimeData : public QMimeData
    Q_OBJECT
public:
    TreeItemMimeData():QMimeData(){};
    QStringList formats() const {return m_format ;}
    const QTreeWidgetItem * DragItemData() const { return  m_pDragItem;}
    void SetDragData(QString mimeType,QTreeWidgetItem * pItem);
private:
    QStringList m_format;
    const QTreeWidgetItem * m_pDragItem;
protected:
    QVariant retrieveData(const QString &mimetype, QVariant::Type preferredType) const ;
#endif // TREEITEMMIMEDATA_H
#include "treeitemmimedata.h"
void TreeItemMimeData::SetDragData(QString mimeType, QTreeWidgetItem *pItem)
    m_format << mimeType;
    m_pDragItem = pItem;
QVariant TreeItemMimeData::retrieveData(const QString &mimetype, QVariant::Type preferredType) const
    if( mimetype == "ItemMimeData")
        return m_pDragItem;
        return QMimeData::retrieveData(mimetype,preferredType);

拖拽过程图片显示

  1. 概述
    使用 QPushButton 可以同时显示图标和文字,设置边框样式后可以达到需要的效果。
  2. 头文件
#ifndef DRAGIMAGE_H
#define DRAGIMAGE_H
#include <QWidget>
namespace Ui {
class DragImage;
class DragImage : public QWidget
    Q_OBJECT
public:
    explicit DragImage(QWidget *parent = nullptr);
    ~DragImage();
    void setShowText(QString text);//拖动时显示的文字
    void setShowIcon(QIcon pix);//拖动时显示的图标
private:
    Ui::DragImage *ui;
#endif // DRAGIMAGE_H
#include "dragimage.h"
#include "ui_dragimage.h"
DragImage::DragImage(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::DragImage)
    ui->setupUi(this);
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground, true);
DragImage::~DragImage()
    delete ui;
void DragImage::setShowText(QString text)
    ui->pushButton->setText(text);
void DragImage::setShowIcon(QIcon pix)
     ui->pushButton->setIcon(pix);

拖拽操作选择界面

  1. 概述
    使用 QButtonGroup 创建一个单选按钮集合,以选择拖拽的操作方式。
  2. 头文件
#ifndef DRAGMODEDIALOG_H
#define DRAGMODEDIALOG_H
#include <QDialog>
namespace Ui {
class DragModeDialog;
class QLabel;
class QButtonGroup;
class DragModeDialog : public QDialog
    Q_OBJECT
public:
    explicit DragModeDialog(QWidget *parent = nullptr);
    ~DragModeDialog();
    void setShowText(const QString &dragname,const QString &desname);
    void setShowPixm(const QPixmap &pix);
    enum DragMode { DragAsChild,DragToUp,DragToDown };
    DragMode selMode;
private:
    Ui::DragModeDialog *ui;
    QLabel * iconLabel;
    QLabel * textlabel;
    QButtonGroup * group;
private slots:
    void buttonOkClick();
    void buttonCancelClick();
#endif // DRAGMODEDIALOG_H
#include "dragmodedialog.h"
#include "ui_dragmodedialog.h"
#include <QButtonGroup>
#include <QRadioButton>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QLabel>
#include <QGridLayout>
DragModeDialog::DragModeDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DragModeDialog)
    ui->setupUi(this);
    //创建 QRadioButton 集合
    group = new QButtonGroup(this);
    QRadioButton * bnt1 = new QRadioButton(tr("子节点:添加到目标子节点末尾"),this);
    QRadioButton * btn2 = new QRadioButton(tr("同级节点:添加到目标上方位置"),this);
    QRadioButton * btn3 = new QRadioButton(tr("同级节点:添加到目标下方位置"),this);
    bnt1->setChecked(true);
    group->addButton(bnt1,1);
    group->addButton(btn2,2);
    group->addButton(btn3,3);
    //创建标准按钮框
    QDialogButtonBox * box = new QDialogButtonBox(this);
    QPushButton * buttonOk = new QPushButton(tr("确定"));
    QPushButton * buttonCancel = new QPushButton(tr("取消"));
    box->addButton(buttonCancel,QDialogButtonBox::RejectRole);
    box->addButton(buttonOk,QDialogButtonBox::YesRole);
    buttonOk->setDefault(true);
    iconLabel = new QLabel(this);
    textlabel = new QLabel(this);
    textlabel->setText(tr("选择拖动的行为:"));
    textlabel->setStyleSheet("color:blue");
    QFont ft;
    ft.setBold(true);
    ft.setPointSize(11);
    textlabel->setFont(ft);
    QLabel * tmp = new QLabel(this);
    tmp->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
    QGridLayout *mainLayout = new QGridLayout(this);
    mainLayout->addWidget(iconLabel,0,0,1,1);
    mainLayout->addWidget(textlabel,0,1,1,3);
    mainLayout->addWidget(bnt1,1,0,1,4);
    mainLayout->addWidget(btn2,2,0,1,4);
    mainLayout->addWidget(btn3,3,0,1,4);
    mainLayout->addWidget(tmp,4,0,1,4);
    mainLayout->addWidget(box,5,0,1,4);
    mainLayout->setHorizontalSpacing(10);
    mainLayout->setVerticalSpacing(10);
    mainLayout->setContentsMargins(10,10,10,10);
    setLayout(mainLayout);
    this->setFixedSize(380,210);
    this->setWindowTitle(tr("拖动设置"));
    setWindowFlags(Qt::WindowStaysOnTopHint);
    connect(buttonOk,&QPushButton::clicked,this,&DragModeDialog::buttonOkClick);
    connect(buttonCancel,&QPushButton::clicked,this,&DragModeDialog::buttonCancelClick);
DragModeDialog::~DragModeDialog()
    delete ui;
void DragModeDialog::setShowText(const QString &dragname, const QString &desname)
    textlabel->setText(QString(tr("拖动节点:%1,目标节点:%2,选择操作类型:")).arg(dragname).arg(desname));
void DragModeDialog::setShowPixm(const QPixmap &pix)
    iconLabel->setPixmap(pix);
void DragModeDialog::buttonOkClick()
    int selID = group->checkedId();
    switch (selID)
        case 1:
            selMode = DragAsChild;
            break;
        case 2:
            selMode = DragToUp;
            break;
        case 3:
            selMode = DragToDown;
            break;
    accept();
void DragModeDialog::buttonCancelClick()
    close();
                    前言通过一个例子学习 QTreeWidget 节点的拖拽功能。本示例仅实现树形结构的自我拖拽,用来更改上下级关系或者调整节点顺序。子类化 QTreeWidget概述通过子类化 QTreeWidget 重新实现相关事件,以实现自定义功能。头文件#ifndef MYTREEWIDGET_H#define MYTREEWIDGET_H#include &lt;QTreeWidget&gt;class MyTreeWidget : public QTreeWidget{    Q_OB
private void tv1_ItemDrag(object sender, ItemDragEventArgs e)
    if (e.Button == MouseButtons.Left)
1.原节点(假设为:S)的父级如果不等于目标节点(假设为:T)的父节点,那么发生了跨级,即非同级移动。这个判断很容易。
2.S、T是同一级的,但是S是移动到T下一级,这种情景下,移动过程中,S和T的父节点是一致的,不能判断是否跨级移动,那么怎么办判断呢?
方案1:在afterDrop事件中来判断父节点是否一致,因为移动已经完成,父节点发什么了变化,根据判断结果然后再把节点恢复回去。这种做法很low。
方案2:在移动过程中判断S被移动到T节点的位置:T节点前、T节
刚进公司没多久,老大就让做一个实现拖放的小插件,要求可拖拽,可缩放。从网上查了很多资料,也看了别人写的demo,总算摸索出一些门道,下面分享给大家,如有疑问,可评论,大家一起学习!
QCTreeWidget.cpp
// 左侧项目树
QCTreeWidget::QCTreeWidget(const QString& text, QWidget *parent)
: ...
QTreeWidget是一个树形视图控件,当不需要进行较为复杂的操作时,
QTreeWidget会比QTreeView更方便使用,这是一个继承了QTreeView的封装类,
需要跟QTreeWidgetItem一起使用
所需头文件:
QTreeWidget
QTreeWi...
在鼠标Press的时候,记录起始点为dragPosition(此为成员变量);在Move的时候,检测与起始点的距离,当拖动一定距离的时候,认为开启了一个拖动操作。按下面例子,鼠标会为成拖动的样式,并有一个图片显示被拖动的物体的轮廓。
				
Qt实现树节点的需求拖拽 直接进入正题,各位官爷先了解一下需求: 树上节点拖拽操作,调整设备所属的树节点,里面要做一些类型合法性判断,调整树节点时,也只能放在相同类别的设备文件夹下 代码实现: 首先再创建树的地方加入三行代码: //设置树节点允许拖拽 m_treeWidget->setDragDropMode(QAbstractItemView::DragDrop); //允许接受drop操作,注意,不写的话无法进入重写的事件 m_treeWidget->setAcceptDrops(true)
本节介绍QTreeWidget、QDockWidget的使用,以及用 QLabel 显示图片的方法。实例 samp4_8 QTreeWidgetQTreeWidget 为主要组件,创建一个照片管理器,实例运行时的界面如图 1 所示。 图 1 实例Samp4_8运行时界面 这个实例主要演示如下几个组件的使用方法。 QTreeWidget 目录树组件:QTreeWidget 类是创建和...
Qt中的QTreeWidget是一个常用的树形控件,用于展示层次结构数据。可以通过设置QTreeWidget的相关属性来实现对展开节点的图标进行自定义。具体如下: 1.在Qt Creator中打开界面设计模式,将QTreeWidget控件拖入界面中。 2.选中控件,右键->编辑信号/槽,选择itemExpanded(QTreeWidgetItem *)的“添加”按钮,生成一个响应函数。 3.在响应函数中,首先获取当前所展开的节点item,然后使用setExpandedIcon()函数为该节点设置展开后的图标。 示例代码如下: void MainWindow::on_treeWidget_itemExpanded(QTreeWidgetItem *item) QIcon icon(":/images/expanded.png"); // 定义展开后的图标 item->setExpandedIcon(icon); // 设置节点的展开后图标 以上是设置展开节点的图标的基本步骤,需要注意的是,展开节点的图标还需要与QTreeWidgetItem自身的图标一起设置。可以在初始化时为所有节点设置默认的展开后和折叠后图标,如下: void MainWindow::initTreeWidget() QIcon expanded(":/images/expanded.png"); // 展开后的图标 QIcon collapsed(":/images/collapsed.png"); // 折叠后的图标 ui->treeWidget->setExpandedIcon(expanded); // 设置默认的展开后图标 ui->treeWidget->setCollapsedIcon(collapsed); // 设置默认的折叠后图标 // 循环设置所有节点的图标 for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) { QTreeWidgetItem *item = ui->treeWidget->topLevelItem(i); item->setIcon(0, QIcon(":/images/item.png")); // 设置节点图标 item->setExpandedIcon(expanded); // 设置节点展开后的图标 item->setCollapsedIcon(collapsed); // 设置节点折叠后的图标 上述代码中,initTreeWidget()函数在界面初始化时被调用,用于设置所有节点的图标。其中,topLevelItemCount()函数用于获取第一级节点的数量,setIcon()函数用于设置节点的图标。