QTreeWidgetItem 的拖放不能正常工作
Posted
技术标签:
【中文标题】QTreeWidgetItem 的拖放不能正常工作【英文标题】:Drag and drop of QTreeWidgetItem does not work properly 【发布时间】:2021-01-27 00:19:51 【问题描述】:我将QTreeWidget
子类化以重新实现drag
和drop
的protected
功能,以便拖放QTreeWidget
的父母和子女。
它几乎可以工作,问题是,一旦我尝试拖放孩子或父母,一旦我放下它们,它们就会被删除。 源代码可以在here 找到,以防您需要查看发生了什么。
此外,如果我尝试拖放父级,不幸的是它不会移动,也没有任何反应。
看看下面对孩子们的奇怪效果:
我将子“Original”拖放到“Sample”下方,此过程似乎可以正常工作:
删除“原始”后,您可以看到索引似乎在那里,但似乎被部分删除:
我不确定到底发生了什么,为什么孩子似乎没有完全正确地被丢弃,为什么父母真的不动。
下面是最小可验证示例的代码:
mainwindow.h
#include <QMainWindow>
class QTreeWidget;
class QTreeWidgetItem;
QT_BEGIN_NAMESPACE
namespace Ui class MainWindow;
QT_END_NAMESPACE
class MainWindow : public QMainWindow
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QTreeWidgetItem *createItem(const QString &name, const QString &iconPath);
private:
Ui::MainWindow *ui;
QTreeWidget *widget;
QString m_Name;
QString m_Path;
;
#endif // MAINWINDOW_H
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
ui->setupUi(this);
ui->treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->treeWidget->setDragEnabled(true);
ui->treeWidget->viewport()->setAcceptDrops(true);
ui->treeWidget->setDropIndicatorShown(true);
ui->treeWidget->setDragDropMode(QAbstractItemView::InternalMove);
auto *top1 = createItem("Images", "/home/ui/qrc/laserscan.png");
auto *top2 = createItem("Path", "/home/ui/qrc/laserscan.png");
top1->addChild(createItem("Original", "/home/ui/qrc/laserscan.png"));
top1->addChild(createItem("Sample", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Left Side", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Right Side", "/home/ui/qrc/laserscan.png"));
ui->treeWidget->addTopLevelItems( top1, top2 );
// Below I am assigning to each parent/children a checkbox
const int n_tops = ui->treeWidget->topLevelItemCount();
for(int a = 0; a<n_tops; ++a)
const int n_children = ui->treeWidget->topLevelItem(a)->childCount();
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a));
for(int b = 0; b<n_children; ++b)
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a)->child(b));
MainWindow::~MainWindow()
delete ui;
QTreeWidgetItem *MainWindow::createItem(const QString &name, const QString &iconPath)
auto *item = new QTreeWidgetItem(QStringListname);
item->setIcon(0, QIcon(iconPath));
return item;
maphelpers.h
#include <QTreeWidget>
#include <QTreeWidgetItem>
void qtreewidgetitem_assign_qcheckbox(QTreeWidget *tree_widget, QTreeWidgetItem *tree_item);
#endif // MAPHELPERS_H
maphelpers.cpp
#include <QTreeWidget>
void qtreewidgetitem_assign_qcheckbox(QTreeWidget *tree_widget, QTreeWidgetItem *tree_item)
MyCheckBoxMap *myCheckBoxMap = new MyCheckBoxMap(tree_item);
tree_widget->setItemWidget(tree_item, MAP_COLUMN, myCheckBoxMap);
下面是我对QTreeWidget
的子类化:
customtreewidget.h
#include <QTreeWidget>
class CustomTreeWidget : public QTreeWidget
public:
CustomTreeWidget(QWidget *parent = Q_NULLPTR);
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
private:
QTreeWidgetItem *draggedItem;
;
#endif // CUSTOMTREEWIDGET_H
customtreewidget.cpp
CustomTreeWidget::CustomTreeWidget(QWidget *parent)
QTreeWidgetItem* parentItem = new QTreeWidgetItem();
parentItem->setText(0, "Test");
parentItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled);
int num_rows = topLevelItemCount();
for(int i = 0; i < num_rows; ++i)
int nChildren = topLevelItem(i)->childCount();
QTreeWidgetItem* pItem = new QTreeWidgetItem(parentItem);
for(int j = 0; j < nChildren; ++j)
pItem->setText(0, QString("Number %1").arg(i) );
pItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
pItem->addChild(pItem);
topLevelItem(i)->child(j);
void CustomTreeWidget::dragEnterEvent(QDragEnterEvent *event)
draggedItem = currentItem();
QTreeWidget::dragEnterEvent(event);
void CustomTreeWidget::dropEvent(QDropEvent *event)
QModelIndex droppedIndex = indexAt(event->pos());
if(!droppedIndex.isValid())
return;
if(draggedItem)
QTreeWidgetItem *mParent = draggedItem->parent();
if(mParent)
if(itemFromIndex(droppedIndex.parent()) != mParent)
return;
mParent->removeChild(draggedItem);
mParent->insertChild(droppedIndex.row(), draggedItem);
到目前为止我做了什么以及我尝试了什么:
1) 根据官方文档,可以使用QTreeWidgetItemIterator Class,我尝试采用这种方式,实际上您可以在下面看到我最初的实现想法,但这并没有真正带来我在任何地方:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
ui->setupUi(this);
auto *top1 = createItem("Images", "/home/ui/qrc/laserscan.png");
auto *top2 = createItem("Path", "/home/ui/qrc/laserscan.png");
top1->addChild(createItem("Original", "/home/ui/qrc/laserscan.png"));
top1->addChild(createItem("Sample", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Left Side", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Right Side", "/home/ui/qrc/laserscan.png"));
ui->treeWidget->addTopLevelItems( top1, top2 );
const int n_tops = ui->treeWidget->topLevelItemCount();
// Below I am assigning to each parent/children a checkbox
for(int a = 0; a<n_tops; ++a)
const int n_children = ui->treeWidget->topLevelItem(a)->childCount();
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a));
for(int b = 0; b<n_children; ++b)
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a)->child(b));
QTreeWidgetItem *parentItem = Q_NULLPTR;
QTreeWidgetItem *childItem = Q_NULLPTR;
QTreeWidgetItemIterator it(ui->treeWidget);
while (*it)
parentItem = new QTreeWidgetItem(ui->treeWidget);
//parentItem->setText(0, it.key());
foreach (const auto &str, it)
childItem = new QTreeWidgetItem;
childItem->setText(0, str);
parentItem->addChild(childItem);
2) 在官方文档中进一步研究后,我决定解决应用 QMapIterator Class 的问题以不同的方式重写构造函数,基本上是下面的实现思想,但不想通过那种方式:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
ui->setupUi(this);
auto *top1 = createItem("Images", "/home/ui/qrc/laserscan.png");
auto *top2 = createItem("Path", "/home/ui/qrc/laserscan.png");
top1->addChild(createItem("Original", "/home/ui/qrc/laserscan.png"));
top1->addChild(createItem("Sample", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Left Side", "/home/ui/qrc/laserscan.png"));
top2->addChild(createItem("Right Side", "/home/ui/qrc/laserscan.png"));
ui->treeWidget->addTopLevelItems( top1, top2 );
ui->treeWidget->addTopLevelItems( top1, top2 );
const int n_tops = ui->treeWidget->topLevelItemCount();
// Below I am assigning to each parent/children a checkbox
for(int a = 0; a<n_tops; ++a)
const int n_children = ui->treeWidget->topLevelItem(a)->childCount();
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a));
for(int b = 0; b<n_children; ++b)
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a)->child(b));
QTreeWidgetItem *parentItem = Q_NULLPTR;
QTreeWidgetItem *childItem = Q_NULLPTR;
QMapIterator<QString, QStringList> i(treeMap);
while (i.hasNext())
i.next();
parentItem = new QTreeWidgetItem(widget);
parentItem->setText(0, i.key());
foreach (const auto &str, i.value())
childItem = new QTreeWidgetItem;
childItem->setText(0, str);
parentItem->addChild(childItem);
3) 经过更多研究后,我发现了this source 和this other source,它们对实现QTreeWidget
的子类化很有用(尤其是最后一篇文章)。
现在我被困住了,因为我试图弄清楚为什么尽管我尝试了所有的事情,但我还是实现了 90% 的工作应用程序,因此,它缺少什么?
感谢您提供有关如何解决此问题的指导。
【问题讨论】:
如果只需要拖放,为什么还要子类化? 在我看来,代码的分配复选框部分是罪魁祸首。如果注释掉该部分,则可以正常拖放项目。也许复选框正在处理鼠标事件。 @NgocMinhNguyen,感谢您过来阅读问题。确实!!罪魁祸首是复选框!如果我评论说 for 循环一切正常......但我不明白为什么......我也在问题中添加了那个类。您是否看到任何触发该行为的具体内容? 【参考方案1】:我认为复选框正在处理鼠标事件,并且不要让事件传递到项目。要获得所需的行为,您应该删除此部分:
const int n_tops = ui->treeWidget->topLevelItemCount();
for(int a = 0; a<n_tops; ++a)
const int n_children = ui->treeWidget->topLevelItem(a)->childCount();
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a));
for(int b = 0; b<n_children; ++b)
qtreewidgetitem_assign_qcheckbox(ui->treeWidget, ui->treeWidget->topLevelItem(a)->child(b));
在createItem()
函数中:
QTreeWidgetItem *MainWindow::createItem(const QString &name, const QString &iconPath)
auto *item = new QTreeWidgetItem(QStringListname);
item->setCheckState(0, Qt::Unchecked);
item->setIcon(0, QIcon(iconPath));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
return item;
【讨论】:
我明白了!是的,现在它工作得非常好,完全符合我的要求!非常感谢您的编辑以及您花时间阅读和回答我的问题! :) :)以上是关于QTreeWidgetItem 的拖放不能正常工作的主要内容,如果未能解决你的问题,请参考以下文章