如何为QTreeWidgetItem的每个*类型*创建不同的弹出(上下文)菜单

时间:2012-10-17 15:27:07

标签: c++ qt

我能够为我的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);

但我想要一个分支的不同弹出窗口而不是叶子。如何根据单击的widgetitem类型分配不同的弹出窗口?

我的树:

  • Branch1< - Popup1
    • Leaf1
    • Leaf2< - Popup2
  • 店2
    • 店3
      • Leaf1

QWidget :: actions()未列为虚拟。否则,我会从QTreeWidget& amp;重新实现的行动()。

2 个答案:

答案 0 :(得分:6)

方法1:覆盖QTreeWidget

正如您所发现的那样,分配给QTreeWidget本身的上下文菜单将不允许您为不同的项目设置不同的上下文菜单。

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

  1. 创建QTreeWidget
  2. 的子类
  3. customContextMenuRequested(const QPoint&)信号连接到自定义插槽。
  4. 显示所需的上下文菜单。
  5. 我发布了complete working example。需要注意的一些细节包括:

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

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

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


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

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

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

    • 项目委托使用QModelIndex个对象来表示项目。要转换为QTreeWidgetItem,您必须使用QTreeWidget::itemFromIndex()方法。不幸的是,这是受保护的,因此它实际上要求您继承QTreeWidget,以便为您的委托提供此API。这为代码增加了一些样板复杂性。

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

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

    核心代码非常相似,但同样,还有更多的样板。我也发布了an example of this approach

答案 1 :(得分: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));
}

main.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();
}