Qt: изменение содержимого приложения QMenuBar в Mac OS X

В моем приложении используется 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

Я создал минимальный пример, чтобы воспроизвести это поведение в Mac OS X 10.9.4 с Qt 5.3.

mainwindow.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

person syrius    schedule 23.09.2014    source источник


Ответы (2)


Все это выглядит как ошибка Qt в OS X. И на самом деле это очень старая ошибка.

Вы можете сделать обходной путь и не работать с QMenu через вызовы функции QMenuBar::addMenu, как вы делаете здесь:

m_menuBar->addMenu("One");

Вместо этой работы с QAction, полученным из QMenu, путем динамического создания экземпляра QMenu и последующего вызова QMenuBar::addAction для экземпляра QAction, полученного QMenu::menuAction, следующим образом:

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 с помощью QMenuBar::setNativeMenuBar
  • Из-за включенного по умолчанию собственного поведения QMenuBar QActions со стандартными заголовками OS X («О программе», «Выход») будут автоматически размещаться Qt на экране предопределенным образом; Пустые экземпляры QMenu вообще не будут отображаться.
person Max Go    schedule 24.09.2014
comment
Я хотел бы отметить, что хотя добавление oneMenu->menuAction() является правильным и работает для первых 2, по-видимому, обязательно (в Mac OS X), чтобы меню имело существующие действия при добавлении к QMenuBar. Поскольку в моем примере с динамическим меню не добавляются действия до тех пор, пока не будет испущен сигнал aboutToShow(), мой обходной путь состоит в том, чтобы заполнить динамическое меню 1 QAction, которое не служит никакой другой цели, кроме как заставить его первоначально отображаться. Затем в моем слоте updateDynamicMenu() я очищаю и повторно заполняю динамическое меню. - person syrius; 24.09.2014
comment
@syrius, просто для справки: можно отключить собственное поведение платформы строки меню с помощью QMenuBar::setNativeMenuBar. Но это может принести гораздо больше головной боли. Также в реальном приложении мы используем QActions для прослушивания их сигналов. Чтобы избежать накладных расходов, оптимальное решение заключается в сохранении всех QActions в качестве частных членов и заполнении ими динамических меню каждый раз, когда нам нужно изменить QMenu. - person Max Go; 24.09.2014
comment
но в реальном приложении, таком как меню «Окно» для редактора, у вас не будет частных действий для каждого открытого в данный момент файла. Вот почему я заполняю меню в слоте, связанном с сигналом aboutToShow(). Я только что упомянул, что в вашем примере для динамического меню, если вы не заполните его до тех пор, пока не будет испущен сигнал aboutToShow(), он никогда не появится в строке меню, что является моей причиной публикации моего первоначального комментария о необходимости пустого QAction (потому что вы еще не знаете содержание). Я думаю, что другим было бы полезно включить это в ответ. - person syrius; 24.09.2014
comment
@syrius, по-видимому, обязательно (в Mac OS X), чтобы меню имело существующие действия при добавлении в QMenuBar. Можете ли вы дать ссылку на это? Я пытаюсь решить именно эту проблему. - person astrojuanlu; 26.03.2015

Я думаю, что ваша проблема в этой строке:

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();
person davepmiller    schedule 24.09.2014