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

Posted

技术标签:

【中文标题】Qt:在 Mac OS X 上更改应用程序 QMenuBar 内容【英文标题】:Qt: Change application QMenuBar contents on Mac OS X 【发布时间】:2014-09-23 21:13:54 【问题描述】:

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

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

第一个菜单是以我在应用程序中创建最多的方式创建的,它们会收到正确的标题,但一旦重新创建菜单就会消失。

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

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

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

m_menuBar = new QMenuBar(0);

并使用它而不是m_menuBar->clear(),但它具有相同的行为。

我没有足够的声望来内联发布图片,所以我将包含 imgur 链接:

启动行为:http://i.imgur.com/ZEvvGKl.png

发布按钮点击行为:http://i.imgur.com/NzRmcYg.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");

主窗口.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

【问题讨论】:

【参考方案1】:

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

您可以通过 QMenuBar::addMenu 函数调用来解决问题,但不要使用 QMenu,就像您在此处所做的那样:

m_menuBar-&gt;addMenu("One");

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

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

除了 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
#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());

另外,在为 OS X 开发菜单逻辑时,请记住:

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

【讨论】:

我想指出,虽然添加 oneMenu-&gt;menuAction() 是正确的并且适用于前 2 个,但显然 强制(在 Mac OS X 上)对于菜单添加到QMenuBar 时具有现有操作。由于我的动态菜单示例在发出 aboutToShow() 信号之前不会添加操作,因此我的解决方法是使用 1 QAction 填充动态菜单,除了让它最初显示之外没有其他用途。然后在我的updateDynamicMenu() 插槽中清除并重新填充动态菜单。 @syrius,请注意,可以使用 QMenuBar::setNativeMenuBar 禁用菜单栏平台原生行为。但这会带来更多的头痛。同样在实际应用中,我们使用 QActions 来监听它们的信号。为了避免开销,最佳解决方案是将所有 QAction 保留为私有成员,并在每次我们需要更改 QMenu 时使用它们填充动态菜单。 但在实际应用程序中,例如编辑器的“窗口”菜单,您不会对当前打开的每个文件都有私有成员操作。这就是我在连接到aboutToShow() 信号的插槽中填充菜单的原因。我刚刚提到,在您的动态菜单示例中,如果在发出 aboutToShow() 信号之前不填充它,它将永远不会出现在菜单栏中,这就是我发布关于需要一个初始评论的原因空 QAction(因为你还不知道内容)。我认为将其包含在答案中对其他人会有所帮助。 @syrius “添加到 QMenuBar 时,菜单显然是强制性的(在 Mac OS X 上)具有现有操作”您能提供参考吗?我正在尝试解决这个确切的问题。【参考方案2】:

我认为你的问题是这一行:

QMenu *oneMenu = m_menuBar->addMenu("One");

要将菜单添加到菜单栏,您需要如下代码:

QMenuBar *m = new QMenuBar;
m->addMenu( new QMenu("Hmmm") );
m->show();

创建菜单,然后添加操作,然后将菜单添加到菜单栏:

QMenu *item = new QMenu( "Test1" );
item->addAction( "action1" );

QMenuBar *t = new QMenuBar;
t->addMenu( item );
t->show();

【讨论】:

以上是关于Qt:在 Mac OS X 上更改应用程序 QMenuBar 内容的主要内容,如果未能解决你的问题,请参考以下文章

在 Mac OS X 上安装多个版本的 Qt

Qt 5.4 - Mac OS X 10.6 支持

Qt 4.8 - 在 mac-mini (OS X Lion) 上检测 sd 卡的插入和移除

Qt Designer 4.8 在 Mac OS X 10.7 上启动时崩溃

使用 Qt 在 OS X/Mac 上构建 C++ 库

Mac OS X 上的 Qt 安装问题