单击 QT C++ 上下文菜单中的 "more items..." 选项

Click for "more items..." option on context menu in QT C++

我使用 C++QT 中实施了一个 context menu 项目。我希望上下文菜单中最多显示 5 个元素。但它总共有 15 个元素,我希望用户单击“更多项目...”或某种“向下”箭头按钮,以便扩展上下文菜单以显示超过 5 个元素。可能吗?如果是,我将不胜感激一些指向正确方向的指示。谢谢。

请注意,我尝试实现它的方式是,单击“更多项目”时上下文菜单会消失...所以这不是我想要的。

//代码

void myWidget::contextMenuEvent(QContextMenuEvent *event)
{
    QMenu menu(this);
    menu.addAction(new QAction("Action 1", this));
    menu.addAction(new QAction("Action 2", this));
    menu.addAction(new QAction("Action 3", this));
    menu.addAction(new QAction("Action 4", this));
    menu.addAction(new QAction("Action 5", this));
    QAction *moreItems = new QAction("More items...", this);
    QObject::connect(moreItems , SIGNAL(clicked()), this, SLOT(moreItemsClicked()));
    menu.addAction(moreItems);

    menu.exec(event->globalPos());
}

void myWidget::moreItemsClicked()
{
    //Now what! Need help!
}

首先,我建议您不要使用宏 SIGNALSLOT,或者如果您想使用它们 - 请始终检查连接结果。您的意图是连接到 QActionclicked 信号,但是有 no such signal。 如果您将使用函数指针而不是 SIGNAL - 您将得到编译错误,这总是比运行时断言更好。

关于问题本身,请考虑简单的多级菜单:

但如果你真的想要,你可以这样扩展菜单:

void contextMenuEvent(QContextMenuEvent* event)
    {
        auto menu = new QMenu(this);
        menu->addAction(new QAction("Action 1", this));
        menu->addAction(new QAction("Action 2", this));
        menu->addAction(new QAction("Action 3", this));
        menu->addAction(new QAction("Action 4", this));
        menu->addAction(new QAction("Action 5", this));
        QAction* moreItems = new QAction("More items...", this);
        const auto connection_result = connect(moreItems, &QAction::triggered, [=]() 
            {
            menu->removeAction(moreItems);
            menu->addAction(new QAction("Action 6", this));
            menu->addAction(new QAction("Action 7", this));

            menu->exec(event->globalPos());
            });
        Q_ASSERT(connection_result);
        Q_UNUSED(connection_result);

        menu->addAction(moreItems);
        menu->exec(event->globalPos());
    }

您将被迫重新打开菜单,因为它会在点击后隐藏。

此外,必须在堆上创建菜单才能在 lambda 中使用。这迫使我们注意内存泄漏。为此,您可以添加此连接:

connect(menu, &QMenu::triggered, menu, &QMenu::deleteLater);

但在那种情况下,我们需要依赖连接顺序,因为我们希望在执行 lambda 时菜单仍然存在,这不是最好的。或者从每个动作中调用 QMenu::deleteLater,这也不好。

所以我会像这样重新创建扩展菜单:

QMenu* CreateContextMenu(bool is_extended, QAction* extra_action, QWidget* parent)
    {
        auto menu = new QMenu(parent);

        const auto connection_result = connect(menu, &QMenu::triggered, menu, &QMenu::deleteLater);
        Q_ASSERT(connection_result);
        Q_UNUSED(connection_result);

        menu->addAction(new QAction("Action 1", this));
        menu->addAction(new QAction("Action 2", this));
        menu->addAction(new QAction("Action 3", this));
        menu->addAction(new QAction("Action 4", this));
        menu->addAction(new QAction("Action 5", this));

        if (is_extended)
        {
            menu->addAction(new QAction("Action 6", this));
            menu->addAction(new QAction("Action 7", this));
        } else {
            menu->addAction(extra_action);
        }

        return menu;
    }

    void contextMenuEvent(QContextMenuEvent* event)
    {
        QAction* moreItems = new QAction("More items...", this);
        const auto connection_result = connect(moreItems, &QAction::triggered, [=]() {
            auto extended_menu = CreateContextMenu(true, nullptr, this);
            extended_menu->exec(event->globalPos());
        });

        Q_ASSERT(connection_result);
        Q_UNUSED(connection_result);

        auto menu = CreateContextMenu(false, moreItems, this);
        menu->exec(event->globalPos());
        
    }

您还可以改进 CreateContextMenu 的界面,因为第一个和第二个参数不能同时存在,可能使用 std::variant。但我决定这将是题外话。