2

我能够为我的 QTreeWidget 创建一个上下文菜单,如下所示

QMenu* pContextMenu = new QMenu(this)
QTreeWidget* pTreeWidget = new QTreeWidget();
QAction* pOpenFile = new QAction(tr("Open A File"), pContextMenu);
pTreeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
pTreeWidget->addAction(pOpenFile);

但我想要一个与叶子不同的分支弹出窗口。如何根据单击的小部件项的类型分配不同的弹出窗口?

我的树:

  • 分支 1 <-- 弹出窗口 1
    • 叶1
    • Leaf2 <-- Popup2
  • 分支2
    • 分支3
      • 叶1

QWidget::actions() 未列为虚拟。否则我会从 QTreeWidget 派生我自己的类并重新实现 actions()。

4

2 回答 2

6

方法一:覆盖 QTreeWidget

正如您所发现的,分配给QTreeWidget自身的上下文菜单不会让您为不同的项目提供不同的上下文菜单。

由于 Qt 项目视图没有用于上下文菜单的特殊 API,因此您必须自己实现这一点。幸运的是,这不是很困难。你只需要:

  1. 创建 的子类QTreeWidget
  2. 将信号连接customContextMenuRequested(const QPoint&)到自定义插槽。
  3. 显示所需的上下文菜单。

我已经发布了一个完整的工作示例。一些需要注意的细节包括:

  • QTreeWidgetItem提供了一个方便的type属性,让您无需强制转换、字符串解析或其他笨拙/脆弱的方法即可轻松识别项目。

  • 自定义QTreeWidgetItem类型值应大于或等于QTreeWidgetItem::UserType

  • 显示上下文菜单时,您必须将全局位置传递给exec(). 要从插槽中小部件空间中的位置正确映射,您必须使用项目的视口小部件。


方法2:覆盖QItemDelegate(和QTreeWidget ...)

另一种方法是实现您自己的QAbstractItemDelegate子类,并将其分配给您的树小部件。在您的项目委托中,您可以覆盖editorEvent()以相同的方式处理鼠标按下。

虽然这种方法 frres 实际上更符合 Qt 的项目视图 API 设计,但这种方法有几个关键缺点:

  • 项目委托使用QModelIndex对象来表示项目。要转换为 a QTreeWidgetItem,您必须使用该QTreeWidget::itemFromIndex()方法。不幸的是,这是受保护的,因此它实际上需要您进行子类QTreeWidget化才能为您的委托提供此 API。这为您的代码增加了一些样板复杂性。

  • editorEvent()项目视图处理事件之前调用钩子。这意味着您不能轻松地显示上下文菜单并同时允许默认行为(例如选择右键单击的项目)。

  • 由于editorEvent()处理程序会看到各种不同的事件,因此您必须更加小心地正确处理它们。如果您的行为很复杂,您还必须小心不要让这个单一的处理程序失控。

核心代码非常相似,但同样有更多样板。我也发布了这种方法的示例

于 2012-10-17T16:21:47.997 回答
2

我稍微修改了 jmk 的代码,以展示如何做到这一点

setContextMenuPolicy(Qt::CustomContextMenu) 和 customContextMenuRequested(const QPoint&) 信号。

mytreewidget.h

#include <QTreeWidget>

static const int ItemType1 = QTreeWidgetItem::UserType + 1;
static const int ItemType2 = QTreeWidgetItem::UserType + 2;

class MyTreeWidget : public QTreeWidget
{
    Q_OBJECT
public:
    MyTreeWidget(QWidget *parent = 0);

private slots:
    void showContextMenu(const QPoint &pos);
};

mytreewidget.cpp:

#include "mytreewidget.h"

#include <QMenu>
#include <QTreeWidgetItem>

MyTreeWidget::MyTreeWidget(QWidget *parent)
  : QTreeWidget(parent)
{
    setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
            SLOT(showContextMenu(const QPoint&)));
}

void MyTreeWidget::showContextMenu(const QPoint &pos)
{
  QMenu menu;

  QTreeWidgetItem* item = itemAt(pos);
  switch (item->type()) {
  case ItemType1:
    menu.addAction("This is a type 1");
    break;

  case ItemType2:
    menu.addAction("This is a type 2");
    break;
  }

  menu.exec(mapToGlobal(pos));
}

主.cpp:

#include <QApplication>

#include "mytreewidget.h"

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    MyTreeWidget w;

    // Add test items.
    w.addTopLevelItem(new QTreeWidgetItem(QStringList("A (type 1)"),
                                          ItemType1));
    w.addTopLevelItem(new QTreeWidgetItem(QStringList("B (type 1)"),
                                          ItemType1));

    w.addTopLevelItem(new QTreeWidgetItem(QStringList("C (type 2)"),
                                      ItemType2));
    w.addTopLevelItem(new QTreeWidgetItem(QStringList("D (type 2)"),
                                          ItemType2));
    w.show();

    return app.exec();
}
于 2012-10-17T16:51:35.150 回答