【问题标题】:QWidget in QTreeWidgetItem disappearing after reordering the QTreeWidgetItem重新排序 QTreeWidgetItem 后 QTreeWidgetItem 中的 QWidget 消失
【发布时间】:2018-10-29 09:30:29
【问题描述】:

我将 QTreeWidget 子类化(称为 ToolsSelectorWidget)并通过覆盖 QTreeWidget::dropEvent() 在其中启用重新排序

void ToolsSelectorWidget::dropEvent(QDropEvent *event) {
    QModelIndex droppedIndex = indexAt(event->pos());

    if( !droppedIndex.isValid() || droppedIndex.parent().isValid()) {
        return;
    }
   QTreeWidget::dropEvent(event);
}

另外,我正在将 QWidgets(QPushButton、QLineEdit)添加到 QTreeWidget 的顶级项目:

ToolsSelectorWidget::ToolsSelectorWidget(QWidget *parent) : QTreeWidget(parent) {
    header()->hide();
    setSelectionMode(QAbstractItemView::SingleSelection);
    setDragEnabled(true);
    viewport()->setAcceptDrops(true);
    setDropIndicatorShown(true);
    setDragDropMode(QAbstractItemView::InternalMove);

    for(int i=0; i<4; ++i) {
        QTreeWidgetItem *part = new QTreeWidgetItem(this);
        part->setFlags(part->flags() & Qt::ItemFlag((~Qt::ItemIsDropEnabled)));
        setItemWidget(part, 0, new QLabel("Part" + QString::number(i) + " Preview", this));
        setItemWidget(part, 1, new QLineEdit("Part" + QString::number(i) + " Name", this));
        setItemWidget(part, 2, new QCheckBox("Part" + QString::number(i) + " Visible", this));
        setItemWidget(part, 3, new QCheckBox("Part" + QString::number(i) + " Locked", this));

    }
}

所以现在我有 4 个顶级项目,每个项目都包含 4 个 QWidget。它很好地填充它们,但是当我通过拖放重新排列它们时,QWidgets 消失了,我最终得到了一个空行。我应该怎么做才能保护它们?

之前:

Part2被移动到Part4之下后,它的children被保留了,但是它的conents,也就是QWidgets,没有了:

【问题讨论】:

  • 你为什么用part-&gt;setFlags(part-&gt;flags() &amp; Qt::ItemFlag((~Qt::ItemIsDropEnabled)));
  • @eyllanesc,因为我不希望将顶级项目放入另一个顶级项目。我也不希望子元素能够移动,我只希望用户能够更改顶级节点的顺序。
  • 我只是有时间,我将努力解决这个问题。总之,您希望父母只能在同一水平上移动,并且孩子可以与其他父母的其他孩子交流。您还希望有一个 QPushButton 按下时会更改您的背景图像。我是对的?
  • @eyllanesc,确切地说。我还将继承 QPushButton,因此它可以连接到我的应用程序模型并根据模型更改其外观。但是,您不必担心那部分。我知道 QTreeView 有自己的模型,所以你可以把一切都建立在它的基础上。谢谢:)
  • 好的,我建议延长赏金,这样你会吸引其他答案:)

标签: c++ qt qt-creator qwidget qtwidgets


【解决方案1】:

为什么要删除小部件?

执行拖放时,所选项目的数据被编码(角色和关联值)并保存在QMimeData中。当接受丢弃时,源项目被删除并使用存储在QMimeData 中的信息创建新项目,在保存的信息中没有小部件信息,因为这与模型无关。由于项目被删除,它们的小部件也被删除。

要检查它,我们可以使用以下示例

#include <QApplication>
#include <QLabel>
#include <QTreeWidget>
#include <QDebug>

static void on_destroyed(){
    qDebug()<<"destroyed";
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTreeWidget w;
    w.setSelectionMode(QAbstractItemView::SingleSelection);
    w.setDragEnabled(true);
    w.viewport()->setAcceptDrops(true);
    w.setDropIndicatorShown(true);
    w.setDragDropMode(QAbstractItemView::InternalMove);

    for(int i=0; i< 5; i++){
        QTreeWidgetItem *it = new QTreeWidgetItem(&w);
        QLabel *lbl = new QLabel(QString::number(i));
        QObject::connect(lbl, &QObject::destroyed, on_destroyed);
        w.setItemWidget(it, 0, lbl);
    }
    w.show();
    return a.exec();
}

它表明,当您拖放项目时,小部件将发出它们破坏的信号。

可能的解决方法:

一种可能的解决方案是在接受放置之前删除小部件并将它们设置在我尚未实现的新项目中。

我已经探索了另一种解决方案,即将QTreeWidget 更改为QTreeView + QStandardItemModel。在QCheckBox 的情况下,带有Qt::ItemIsUserCheckable 标志的复选框被启用,在QLineEdit 的情况下,将使用委托并始终显示,必须使用openPersistentEditor() 方法。

#include <QApplication>
#include <QStandardItemModel>
#include <QTreeView>
#include <QHeaderView>
#include <QDropEvent>
#include <QStyledItemDelegate>
#include <QLineEdit>

class ToolsSelectorDelegate: public QStyledItemDelegate{
public:
    using QStyledItemDelegate::QStyledItemDelegate;
    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const{
        QLineEdit *le = new QLineEdit(parent);
        return le;
    }
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const{
        QRect r(option.rect);
        r.adjust(2, 2, -2, -2);
        editor->setGeometry(r);
    }
};

class ToolsSelectorWidget: public QTreeView{
    QStandardItemModel model;
public:
    ToolsSelectorWidget(QWidget *parent=nullptr): QTreeView(parent){
        setItemDelegate(new ToolsSelectorDelegate(this));
        setModel(&model);
        header()->hide();
        setSelectionMode(QAbstractItemView::SingleSelection);
        setDragEnabled(true);
        viewport()->setAcceptDrops(true);
        setDropIndicatorShown(true);
        setDragDropMode(QAbstractItemView::InternalMove);

        for(int i=0; i<4; ++i) {
            QList<QStandardItem *> items;
            for(const QString & text: {"Preview", "Name", "Visible", "Locked"}){
                QStandardItem *it = new QStandardItem(QString("Part%1 %2").arg(i).arg(text));
                it->setFlags(it->flags() & ~Qt::ItemIsDropEnabled & ~Qt::ItemIsEditable);
                items.append(it);
                if(text == "Visible" || text == "Locked"){
                    it->setFlags(it->flags() | Qt::ItemIsUserCheckable);
                    it->setCheckState(Qt::Unchecked);
                }
                else if (text == "Name") {
                    it->setFlags(it->flags() | Qt::ItemIsEditable);
                }
            }
            for(const QString & children: {"The", "quick", "Brown", "fox", "jump...", "over", "the", "lazy", "dog"})
                items.first()->appendRow(new QStandardItem(children));

            model.invisibleRootItem()->appendRow(items);
            for( int i = 0; i < model.rowCount(); ++i )
                openPersistentEditor(model.index(i, 1));
        }
    }
protected:
    void dropEvent(QDropEvent *event) {
        QModelIndex droppedIndex = indexAt(event->pos());
        if( !droppedIndex.isValid() || droppedIndex.parent().isValid())
            return;
        QTreeView::dropEvent(event);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ToolsSelectorWidget w;
    w.show();
    return a.exec();
}

【讨论】:

  • 非常感谢您的详细回答。我实际上正在探索类似的方法,但这很有帮助!但是有一个问题,如果我决定使用 QPushButton 而不是 QLineEdit,我是用同样的方式处理它,还是必须以不同的方式处理它?
  • @armanali 和 QLineEdit 你能编辑存储的文本吗,在 QPushButton 的情况下你想在按下按钮时做什么?
  • 我的计划是子类qpushbutton,使它可以检查,并用图像叠加,所以当检查并取消选中它将显示不同的图像。这几乎是为了保持状态,但我不想在那里有一个 QCheckBox 。此外,此按钮将包含整个应用程序模型,并连接到其信号,因此如果模型中与它相关的内容发生变化,QPushButton 也会发生变化。
  • 我还看到了 setEditorData、setModelData 之类的方法,难道我也应该使用它们吗?
  • @armanali 是的,我将向您展示一个示例,在 QLineEdit 的情况下不要修改它们,因为默认委托是一个从 QLineEdit 继承的小部件,它没有边框并使用 QLineEdit 的功能,所以它只对其进行子分类以显示边框。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-09
  • 2010-12-12
  • 1970-01-01
  • 2013-08-11
  • 1970-01-01
  • 2019-01-04
  • 2012-08-21
相关资源
最近更新 更多