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);

しかし、葉ではなく枝に別のポップアップが必要です。クリックされたウィジェット項目のタイプに応じて異なるポップアップを割り当てるにはどうすればよいですか?

私のツリー:

  • Branch1 <-- Popup1
    • 葉1
    • Leaf2 <-- Popup2
  • 支店2
    • 支店3
      • 葉1

QWidget::actions() は仮想としてリストされていません。そうでなければ、QTreeWidget から独自のクラスを派生させ、actions() を再実装したでしょう。

4

2 に答える 2

6

方法 1: QTreeWidget をオーバーライドする

あなたが発見したように、それ自体に割り当てられたQTreeWidgetコンテキストメニューでは、さまざまなアイテムに対してさまざまなコンテキストメニューを使用できません。

Qt アイテム ビューにはコンテキスト メニュー用の特別な API がないため、これを自分で実装する必要があります。幸いなことに、それほど難しいことではありません。あなたはただする必要があります:

  1. のサブクラスを作成しますQTreeWidget
  2. customContextMenuRequested(const QPoint&)信号をカスタム スロットに接続します。
  3. 目的のコンテキスト メニューを表示します。

完全な作業例を投稿しました。注意すべき詳細は次のとおりです。

  • QTreeWidgetItemtypeキャスト、文字列解析、またはその他の扱いにくい/壊れやすい方法を使用せずに、アイテムを簡単に識別できる便利なプロパティを提供します。

  • カスタムQTreeWidgetItemタイプの値は、以上である必要がありますQTreeWidgetItem::UserType

  • コンテキスト メニューを表示するときは、グローバル位置を に渡す必要がありますexec()。スロット内のウィジェットのスペース内の位置から正しくマッピングするには、アイテムのビューポート ウィジェットを使用する必要があります。


方法 2: QItemDelegate (および QTreeWidget ...) をオーバーライドする

別の方法は、独自のQAbstractItemDelegateサブクラスを実装し、それをツリー ウィジェットに割り当てることです。アイテム デリゲートでは、editorEvent()マウス プレスを同じ方法で処理するようにオーバーライドできます。

このアプローチ frres は、実際には Qt のアイテム ビュー API の設計とより一致していますが、このアプローチにはいくつかの重要な欠点があります。

  • アイテム デリゲートは、QModelIndexオブジェクトを使用してアイテムを表します。に変換するには、 メソッドQTreeWidgetItemを使用する必要がありますQTreeWidget::itemFromIndex()。残念ながら、これはprotectedQTreeWidgetであるため、デリゲートにこの 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));
}

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();
}
于 2012-10-17T16:51:35.150 に答える