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

C++ 如何为每个*类型*的QTreeWidgetItem创建不同的弹出(上下文)菜单,c++,qt,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); pTree

我能够为我的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方法1:覆盖QTreeWidget 正如您所发现的,分配给
    QTreeWidget
    本身的上下文菜单不允许您为不同的项目设置不同的上下文菜单

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

  • 创建
    QTreeWidget
    的子类
  • customContextMenuRequested(const QPoint&)
    信号连接到自定义插槽
  • 显示所需的关联菜单
  • 我发了一封信。需要注意的一些细节包括:

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

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

    • 显示关联菜单时,必须将全局位置传递给
      exec()
      。要从窗口小部件空间中的某个位置正确映射,必须使用项目的视口小部件


    方法2:重写QItemDelegate(和QTreeWidget…) 另一种方法是实现自己的
    qabstractemdelegate
    子类,并将其分配给树小部件。在项目委托中,可以重写
    editorEvent()
    ,以同样的方式处理鼠标按下

    尽管这种方法frres实际上更符合Qt的项目视图API设计,但这种方法有几个主要缺点:

    • 项目代理使用
      QModelIndex
      对象来表示项目。要转换为
      QTreeWidgetItem
      ,必须使用
      QTreeWidget::itemFromIndex()
      方法。不幸的是,这是受保护的,因此实际上需要您为代理提供此API的子类
      QTreeWidget
      。这会给代码增加更多的样板文件复杂性

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

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


    核心代码非常相似,但还是有更多的样板文件。我也发布了。

    我稍微修改了jmk的代码,以展示如何使用

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

    桃金娘

    #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);
    };
    
    #包括
    静态常量int ItemType1=QTreeWidgetItem::UserType+1;
    静态常量int ItemType2=QTreeWidgetItem::UserType+2;
    类MyTreeWidget:公共QTreeWidget
    {
    Q_对象
    公众:
    MyTreeWidget(QWidget*parent=0);
    专用插槽:
    无效显示上下文菜单(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));
    }
    
    #包括“mytreewidget.h”
    #包括
    #包括
    MyTreeWidget::MyTreeWidget(QWidget*父项)
    :QTreeWidget(父级)
    {
    setContextMenuPolicy(Qt::CustomContextMenu);
    连接(此,信号(customContextMenuRequested(const QPoint&)),
    插槽(showContextMenu(constqpoint&));
    }
    作废MyTreeWidget::showContextMenu(常量QPoint和pos)
    {
    QMenu菜单;
    QTreeWidgetItem*item=itemAt(位置);
    开关(项目->类型()){
    案例项目类型1:
    menu.addAction(“这是类型1”);
    打破
    案例项目类型2:
    menu.addAction(“这是类型2”);
    打破
    }
    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();
    }
    
    #包括
    #包括“mytreewidget.h”
    int main(int argc,字符**argv)
    {
    QApplication应用程序(argc、argv);
    MyTreeWidget w;
    //添加测试项目。
    w、 addTopLevelItem(新的QTreeWidgetItem(QStringList)(“A(类型1)”),
    项目类型1);
    w、 addTopLevelItem(新的QTreeWidgetItem(QStringList)(“B(类型1)”),
    项目类型1);
    w、 addTopLevelItem(新的QTreeWidgetItem(QStringList(“C(类型2)”)),
    项目类型2);
    w、 addTopLevelItem(新的QTreeWidgetItem(QStringList(“D(类型2)”)),
    项目类型2);
    w、 show();
    返回app.exec();
    }
    
    在第一种方法中,更为集中的方法是设置use setContextMenuPolicy(Qt::CustomContextMenu),而不是侦听鼠标事件;在QTreeWidget上。这将导致发出QWidget::customContextMenuRequested(const QPoint&pos),您可以将MyTreeWidget::showContextMenu()的当前内容放在其中。谢谢大家。我还在努力,因为我们有一个不太好的基于设计师的系统。完成后,我会将此标记为答案。对不起,迪瓦诺夫,但我会投你的票。@jmk工作了。除了我必须在MyTreeWidget c'tor中创建QMenus(针对每种类型)&然后在开关中调用.exec()。您可能希望在示例中改变这一点。非常感谢。是在构造函数中创建菜单还是在插槽中打开菜单只是内存消耗/性能难题。但我相信在这种情况下,菜单很少创建,操作并不繁重,所以jmk的代码更可取,因为您将受益于较小的内存占用。谢谢,但我也修改了我的答案。(你也可以编辑我的答案,因为它们非常相似,以减少混淆。)我不能,因为你已经在stackoverflow github的外部发布了代码。实际上,你可以!吉斯特