Qt:更改 Mac OS X 上的应用程序 QMenuBar 内容

syr*_*ius 3 c++ macos qt qmenu qmenubar

我的应用程序对多个“页面”使用 QTabWidget,其中顶级菜单根据用户所在的页面而变化。

我的问题是,尝试重新创建菜单栏的内容会导致严重的显示问题。它在除 Mac OS X 之外的所有平台上按预期使用第一种和第三种样式(尚未测试第二种,但我不想使用该样式)。

第一个菜单是按照我在应用程序中创建的大多数方式创建的,并且它们收到正确的标题,但在重新创建菜单后立即消失。

第二个菜单出现在菜单栏的初始填充和重新填充上,但在这两种情况下都具有标签“无标题”。第二个菜单的样式只是在尝试解决此问题时创建的,因此这是我能够保留菜单的唯一方法。

第三个动态菜单永远不会出现,就这样。我使用这种样式来动态填充即将显示的菜单。

我尝试删除 QMenuBar 并重新创建一个

m_menuBar = new QMenuBar(0);
Run Code Online (Sandbox Code Playgroud)

并使用它来代替m_menuBar->clear()但它具有相同的行为。

我没有足够的声誉来内嵌发布图像,所以我将包含 imgur 链接:

启动行为: https: //i.stack.imgur.com/V4N6O.png

发布按钮点击行为 https://i.stack.imgur.com/fytDc.png

我创建了一个最小的示例来在带有 Qt 5.3 的 Mac OS X 10.9.4 上重现此行为。

主窗口.cpp

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    m_menuBar = new QMenuBar(0);
    m_dynamicMenu = new QMenu("Dynamic");
    connect(m_dynamicMenu, SIGNAL(aboutToShow()), this, SLOT(updateDynamicMenu()));

    changeMenuBar();

    QPushButton *menuBtn = new QPushButton("Test");
    connect(menuBtn, SIGNAL(clicked()), this, SLOT(changeMenuBar()));

    setCentralWidget(menuBtn);
}

void MainWindow::changeMenuBar() {
    m_menuBar->clear();

    // Disappears as soon as this is called a second time
    QMenu *oneMenu = m_menuBar->addMenu("One");
    oneMenu->addAction("foo1");
    oneMenu->addAction("bar1");
    oneMenu->addAction("baz1");

    // Stays around but has 'Untitled' for title in menu bar
    QMenu *twoMenu = new QMenu("Two");
    twoMenu->addAction("foo2");
    twoMenu->addAction("bar2");
    twoMenu->addAction("baz2");
    QAction *twoMenuAction = m_menuBar->addAction("Two");
    twoMenuAction->setMenu(twoMenu);

    // Never shows up
    m_menuBar->addMenu(m_dynamicMenu);
}

void MainWindow::updateDynamicMenu() {
    m_dynamicMenu->clear();
    m_dynamicMenu->addAction("foo3");
    m_dynamicMenu->addAction("bar3");
    m_dynamicMenu->addAction("baz3");
}
Run Code Online (Sandbox Code Playgroud)

主窗口.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtWidgets>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);

private slots:
    void changeMenuBar();
    void updateDynamicMenu();

private:
    QMenuBar *m_menuBar;
    QMenu *m_dynamicMenu;
};

#endif // MAINWINDOW_H
Run Code Online (Sandbox Code Playgroud)

Max*_* Go 5

所有这些看起来都像是 OS X 上的 Qt bug。实际上,这是一个非常古老的 bug。

您可以采取解决方法,并且不通过 QMenuBar::addMenu 函数调用来使用 QMenu,如下所示:

m_menuBar->addMenu("One");

通过动态创建 QMenu 实例,然后为 QMenu::menuAction 检索到的 QAction 实例调用 QMenuBar::addAction,而不是使用从 QMenu 检索的 QAction,如下所示:

m_menuBar->addAction(oneMenu->menuAction());
Run Code Online (Sandbox Code Playgroud)

除了 QMenuBar::addAction 之外,如果您只想动态创建某些特定的菜单项,还可以使用 QMenuBar::removeAction 和 QMenuBar::insertAction。

根据您的源代码,它是它的修改版本,它处理每次单击按钮时的所有菜单动态创建(您在源代码中执行此操作),并且每次单击按钮时,菜单“动态”都会填充不同数量的项目。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtWidgets>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);

private slots:
    void changeMenuBar();

private:
    QMenuBar *m_menuBar;
    QMenu *m_dynamicMenu;
    int m_clickCounter;

};

#endif // MAINWINDOW_H
Run Code Online (Sandbox Code Playgroud)
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      m_clickCounter(1)
{
    m_menuBar = new QMenuBar(this);

    connect(m_dynamicMenu, SIGNAL(aboutToShow()), this, SLOT(updateDynamicMenu()));

    changeMenuBar();

    QPushButton *menuBtn = new QPushButton("Test");
    connect(menuBtn, SIGNAL(clicked()), this, SLOT(changeMenuBar()));

    setCentralWidget(menuBtn);
}

void MainWindow::changeMenuBar() {
    ++m_clickCounter;

    m_menuBar->clear();

    QMenu *oneMenu = new QMenu("One");

    oneMenu->addAction("bar1");
    oneMenu->addAction("baz1");
    m_menuBar->addAction(oneMenu->menuAction());

    QMenu *twoMenu = new QMenu("Two");
    twoMenu->addAction("foo2");
    twoMenu->addAction("bar2");
    twoMenu->addAction("baz2");

    m_menuBar->addAction(twoMenu->menuAction());

    m_dynamicMenu = new QMenu("Dynamic");
    for (int i = 0; i < m_clickCounter; ++i) {
        m_dynamicMenu->addAction(QString("foo%1").arg(i));
    }

    m_menuBar->addAction(m_dynamicMenu->menuAction());
}
Run Code Online (Sandbox Code Playgroud)

此外,在为 OS X 开发菜单逻辑时,最好记住:

  • 可以使用 QMenuBar::setNativeMenuBar 禁用 QMenuBar 本机行为
  • 由于默认打开 QMenuBar 本机行为,具有标准 OS X 标题(“关于”、“退出”)的 QAction 将由 Qt 以预定义的方式自动放置在屏幕上;空的 QMenu 实例将根本不会显示。

  • 我想指出的是,虽然添加 `oneMenu-&gt;menuAction()` 是正确的并且适用于前 2 个菜单,但显然 **强制**(在 Mac OS X 上)菜单在添加时必须具有现有操作到`QMenuBar`。由于我的动态菜单示例在发出“aboutToShow()”信号之前不会添加操作,因此我的解决方法是使用 1 个“QAction”填充动态菜单,该菜单除了最初显示之外没有其他任何用途。然后,在我的“updateDynamicMenu()”插槽中,我清除并重新填充动态菜单。 (2认同)