Как добавить список QActions в QMenu и обработать их одним слотом?

Во-первых, у меня есть список QWidget, что я не буду знать длину до времени выполнения. Затем я создаю QListWidget, где я их показываю, и когда кто-то нажимает на них, я использую сигнал currentItemChanged(QListWidgetItem*, QListWidgetItem*), чтобы поймать его и получить индекс щелкнутого элемента.

Теперь я хочу сделать аналогичную вещь в QMenu. Я узнаю список, когда QMenu и его действия будут созданы, но я не смогу это сделать.

Как я могу создавать действия, ловить их сигналы и подключать их к одному слоту, который выполняет разные действия в зависимости от позиции действия (индекса) в списке меню? Должен быть какой-то способ решить эту проблему, поскольку другие приложения используют это. Я попытался посмотреть на отображение, но я не мог понять, как использовать его для этого.

Я попытался захватить sender в слоте, но не смог получить от него полезную информацию.

Ответ 1

Вы можете связать индекс (или любые другие данные) с каждым действием, когда они созданы с помощью QAction::setData, и подключить сигнал QMenu::triggered(QAction*) к вашему слоту.

Затем вы сможете получить данные с помощью функции QAction::data() вашего параметра слота.

MyClass::MyClass() {
    // menu creation
    for(...) {
        QAction *action = ...;
        action->setData(10);
        ...
        menu->addAction(action);
    }
    // only one single signal connection
    connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(mySlot(QAction*)));
}

void MyClass::mySlot(QAction *action) {
   int value = action->data().toInt();

}

Другие методы: отображение сигналов или использование sender(), объясняются в этой статье Qt Quaterly.

Ответ 2

Более общий (не специфический для QMenu) способ приблизиться к этому - это класс QActionGroup. Это позволяет вам изолировать определенные пункты меню в качестве связанной группы или группировать различные виджеты вместе.

void MyClass::InitMenu(QMenu* menu)
{
    QActionGroup* actions1 = new QActionGroup(menu);
    actions1->setExclusive(false);
    actions1->addAction(menu->addAction(tr("Action1")))->setData(1);
    actions1->addAction(menu->addAction(tr("Action2")))->setData(2);
    actions1->addAction(menu->addAction(tr("Action3")))->setData(3);
    actions1->addAction(menu->addAction(tr("Action4")))->setData(4);
    actions1->addAction(menu->addAction(tr("Action5")))->setData(5);
    connect(actions1, SIGNAL(triggered(QAction*)), SLOT(MySlot(QAction*)));

    QActionGroup* actions2 = new QActionGroup(menu);
    actions2->addAction(menu->addAction(tr("Undo Action1")))->setData(1);
    actions2->addAction(menu->addAction(tr("Undo Action2")))->setData(2);
    //...
    connect(actions2, SIGNAL(triggered(QAction*)), SLOT(MyUndoSlot(QAction*)));
}

и в слоте:

void MyClass::MySlot(QAction* triggeredAction)
{
    // use either the action itself... or an offset
    int value = triggeredAction->data().toInt()
}