如何为QTreeWidget创建委托?

18

这就是我想做的事情(所有的父元素和子元素都必须在右边拥有一个 关闭 按钮,未来只有鼠标悬停在该项上时才能显示关闭按钮):

图片描述

我的委托代码:

class CloseButton : public QItemDelegate
{
     Q_OBJECT

public:
     CloseButton( QObject* parent = 0 )
          : QItemDelegate( parent )
     {};

     QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
     {
          if ( index.column() == 1 )
          {
               QToolButton* button = new QToolButton( parent );
               button->setIcon( QIcon( CLOSE_ICON ) );
               //button->setFixedSize( 16, 16 );
               //button->setAutoRaise( true );
               //button->setVisible( true );

               CONNECT( button, SIGNAL( clicked() ), this, SLOT( emitCommitData() ) );

               return button;
          }
          return ( new QWidget );
     }

private slots:
     void emitCommitData()
     {
          emit commitData( qobject_cast< QWidget* >( sender() ) );
     }

private:
     //Q_DISABLE_COPY( CloseButton );
};

使用 QTreeWidget 连接代码:

recipientsView()->setItemDelegateForColumn( 1, new CloseButton( this ) );

问题在于recipientsView()是一个简单的QTreeWidget

问题是QToolButton根本没有显示出来(它必须在第二列,即树中的列索引为1)。我做错了什么?

我已经检查过关于委托的所有Qt演示示例,并且已经搜索了有关QItemDelegate和类似内容的第一个Google结果。


在VS2008调试器中,构造函数被执行了,但是createEditor()方法没有被执行。 - mosg
3个回答

23

您可以使用QStyledDelegate::paint函数绘制关闭图标,而无需使用任何小部件,并使用editorEvent接收项目的鼠标事件,即使您不使用编辑器或使项目可编辑。

class CloseButton : public QStyledItemDelegate {
    Q_OBJECT
public:

    explicit CloseButton(QObject *parent = 0, 
                         const QPixmap &closeIcon = QPixmap())
        : QStyledItemDelegate(parent)
        , m_closeIcon(closeIcon)
    {
        if(m_closeIcon.isNull())
        {
            m_closeIcon = qApp->style()
                ->standardPixmap(QStyle::SP_DialogCloseButton);
        }
    }

    QPoint closeIconPos(const QStyleOptionViewItem &option) const {
        return QPoint(option.rect.right() - m_closeIcon.width() - margin,
                      option.rect.center().y() - m_closeIcon.height()/2);
    }

    void paint(QPainter *painter, const QStyleOptionViewItem &option,
               const QModelIndex &index) const {
        QStyledItemDelegate::paint(painter, option, index);
        // Only display the close icon for top level items...
        if(!index.parent().isValid()
                // ...and when the mouse is hovering the item
                // (mouseTracking must be enabled on the view)
                && (option.state & QStyle::State_MouseOver))
        {
            painter->drawPixmap(closeIconPos(option), m_closeIcon);
        }
    }

    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index) const
    {
        QSize size = QStyledItemDelegate::sizeHint(option, index);

        // Make some room for the close icon
        if(!index.parent().isValid()) {
            size.rwidth() += m_closeIcon.width() + margin * 2;
            size.setHeight(qMax(size.height(),
                                m_closeIcon.height() + margin * 2));
        }
        return size;
    }

    bool editorEvent(QEvent *event, QAbstractItemModel *model,
                     const QStyleOptionViewItem &option,
                     const QModelIndex &index)
    {
        // Emit a signal when the icon is clicked
        if(!index.parent().isValid() &&
                event->type() == QEvent::MouseButtonRelease) {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);

            QRect closeButtonRect = m_closeIcon.rect()
                    .translated(closeIconPos(option));

            if(closeButtonRect.contains(mouseEvent->pos()))
            {
                emit closeIndexClicked(index);
            }
        }
        return false;
    }

signals:
    void closeIndexClicked(const QModelIndex &);
private:
    QPixmap m_closeIcon;
    static const int margin = 2; // pixels to keep arount the icon

    Q_DISABLE_COPY(CloseButton)
};

alexisdm,我必须说,你的代码非常棒!这真的是解决问题的完美方案! - mosg

1
首先,我应该问一下您是否真的在使用QTreeWidget,还是QTreeView?根据QTreeView的文档,您无法在QTreeWidget中使用自定义委托,因此必须使用QTree*View*和某种形式的QAbstractItemModel才能使用自定义委托。
啊,划掉上面那句话。我看到您正在调用setItemDelegateForColumn,这是一个QTreeView函数,但您应该意识到两者之间的区别,所以我保留了上面的段落。 :)
我会检查您的模型的flags()函数是否作为其项目标志的一部分返回Qt::ItemIsEditable。每当视图报告编辑事件时,都会调用createEditor()方法。(触发编辑的视图事件取决于模型的EditTriggers)通常,默认情况下,双击委托将触发编辑,以及其他一些事件。

我怀疑你不只是想让关闭按钮在双击时出现。如果你想让按钮一直显示,你需要重新实现委托的paint()函数来绘制一个按钮,还有其他的事情。我发现Qt的StarDelegate示例在这方面非常有帮助,我相信你也会觉得它很有用。


首先,感谢您的提问!回答您的问题 - 是的,我正在使用 QTreeWidget!但是,也许您在主题上是正确的,即自定义委托无法与 QTreeWidget 一起使用,但是关于示例呢?我的意思是在 StarDelegate 示例QTableWidget 成功地使用了自定义委托!那怎么可能呢? - mosg
关于从QTreeWidget迁移到QTreeView的问题:在我的情况下,MVC不是正确的方法,因为我使用这个小部件,即QTreeWidget,仅保留4-5行,1-2个父项,就这样,它是所谓的收件人列表,而不是地址簿... - mosg
我认为文档在这方面是不准确的。(关于无法在基于项的视图中使用自定义委托。)QTreeWidget和其它类都是基于QTreeView类的(仅提供了一个简化的接口来使用QTreeItems),因此似乎可以设置自定义委托等。无论如何,您可能仍需要查看委托的paint()函数。如果您对第二列中的每个项目执行了setFlags(flags() |= Qt :: ItemIsEditable),则您现有的代码可能会在双击时显示工具按钮。 - Jonathan Thomas
你知道吗,当我设置了ItemIsEditable标志时,奇怪的事情开始发生。关闭按钮出现在单独的窗口中!%)那时我使用了一种创建没有父级的QToolButton的变体。但是当我将按钮设置为父部件时,根本看不到按钮...我想,更容易创建一个该死的按钮小部件,并将其设置为所有带有setIndexWidgetQTreeWidgetItem...随着我的理解加深,代理是用于编辑目的,而不仅仅是在树/表中绘制小部件...整天我都在浪费时间处理这些东西!无论如何还是谢谢! - mosg

-1

您可以这样使用 QItemDelegate 和 QTreeWidget(以下是 PyQt 的示例):

myTreeWidget = QtGui.QTreeWidget()
myTreeWidget.setItemDelegate(myDelegate())

class myDelegate(QtGui.QItemDelegate):
    def paint(self, painter, option, index):

        #Custom Draw Column 1
        if index.column() == 1:
            icon = QtGui.QIcon(index.data(QtCore.Qt.DecorationRole))
            if icon:
                icon.paint(painter, option.rect)
                #You'll probably want to pass a different QRect

        #Use the standard routine for other columns
        else:
            super(myDelegate, self).paint(painter, option, index)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接