Как создать меню в строке состояния в QT, как меню запуска ПК

В моем приложении QT я разработал строку состояния с несколькими иконками. Слева от строки состояния я добавил значок меню (QPixmap), когда я щелкаю по этому значку, мне нужно показать меню, похожее на меню запуска ПК. Я искал много, но я не нашел Qwidget для этого.

Это мое окно приложения qt

Согласно моему недавнему комментарию я добавил свой отредактированный код ниже, так как я не могу добавить код в разделе комментариев, пожалуйста, предложите решение

//Menu Button
menuBtn->setIcon(QPixmap(":/new/prefix1/ic_menu_black.png"));
statusBar()->addWidget(menuBtn);
connect(menuBtn,SIGNAL(clicked(bool)),this,SLOT(showPopupMenu()));

void MainWindow::showPopupMenu(){QMenu qMenuStart;

QAction qCmdStart1(QString::fromUtf8("Product Details"), &qMenuStart);
qMenuStart.addAction(&qCmdStart1);

QAction qCmdStart2(QString::fromUtf8("Memory Status"), &qMenuStart);
qMenuStart.addAction(&qCmdStart2);

QObject::connect(&qCmdStart1, &QAction::triggered,[](bool){
qDebug() << "Product Details triggered"; }
);
QObject::connect(&qCmdStart2, &QAction::triggered,[](bool){
qDebug() << "Memory Status triggered"; }
);

qMenuStart.exec();

menuBtn->setMenu(&qMenuStart);

qMenuStart.show();
qMenuStart.popup(mapToGlobal(pos() - QPoint(0, qMenuStart.height())));

qDebug()<<"Menu Clicked!";

}

всплывающее меню при нажатии кнопки меню

3

Решение

Я взял ваш пример кода и изменил его, чтобы проиллюстрировать то, что я описал в моих последних комментариях:

Добавьте переменную-член к MainWindow объявление:

QMenu *menuStart;

Измененный пример кода:

// configure start menu:
menuStart = new QMenu(menuBtn);
menuStart->addAction(QString::fromUtf8("Product Details"),
[](bool){
qDebug() << "Product Details triggered"; }
);
menuStart->addAction(QString::fromUtf8("Memory Status"),
[](bool){
qDebug() << "Memory Status triggered"; }
);
//Menu Button
menuBtn->setIcon(QPixmap(":/new/prefix1/ic_menu_black.png"));
menuBtn->setMenu(menuStart);
statusBar()->addWidget(menuBtn);
connect(menuBtn,SIGNAL(clicked(bool)),this,SLOT(showPopupMenu()));

Замечания:

Я поставил menuBtn как родитель menuStart, Таким образом, menuBtn должен управлять уничтожением menuStart когда он уничтожается сам.

Обработчик сигнала становится короче соответственно:

void MainWindow::showPopupMenu()
{
menuStart->show(); // <-- trick to force layout of the menu before height() is called
// position menu relative to menuBtn at popup
menuStart->popup(
mapToGlobal(menuBtn->pos() - QPoint(0, menuStart->height())));

qDebug()<<"Menu Clicked!";
}

Я сделал это осторожно, но не смог проверить это (так как это не MCVE). Так что, пожалуйста, возьмите это с «зерном соли».

Замечания:

Я только что понял, что вы используете стиль «pre-Qt5» для подключения обработчиков сигналов. Qt5 представил хороший расширение его сигналов указатели вспомогательных функций, а также указатели методов. Благодаря этому я смог сделать обработчики сигналов просто лямбды. (Лямбды это [](bool){/*...*/} Я подключен к игровым автоматам. Я всегда с подозрением относился к этим новым лямбдам, пока не понял, что они являются отличным инструментом для написания очень простых адаптеров для обработчиков сигналов, а также для создания довольно компактного примера кода.)

Обновление о Lambdas:

Скобки [] может быть использован для добавления переменных в среда (или контекст) лямбды. Если он пуст (как я его использовал), можно использовать только глобальные переменные и функции. С помощью [this] означает текущий this указатель добавляется в среду. Таким образом, лямбда может получить доступ к переменным-членам текущего экземпляра класса.

Когда я впервые узнал лямбду, я часто видел [=] это означает, что лямбда получает текущий контекст вызывающей функции (например, копии каждой локальной переменной функции). Я лично считаю это опасным, особенно для обработчиков сигналов. Я разработаю это немного:

Окружение лямбды позволяет получить доступ к «переменным извне» в теле лямбды. Переменные могут быть добавлены по значению или по ссылке. Если переменная предоставляется по ссылке, к ней можно получить доступ в лямбда-теле, но это не гарантирует, что время жизни «внешней» переменной достаточно велико. (Если это не так, это приводит к «свисающей ссылке» с тем же Неопределенное поведение как свисающий указатель.) Помогает ли это вообще предотвращать ссылки? Нет. Это хорошо для примитивных типов (например, int или же float) но может быть неэффективным (или даже семантически неправильным) для объектов. Использование указателя на объекты так же опасно, как использование ссылок. Таким образом, среда лямбда должна использоваться с уход.

Как правило, я лично использую следующий метод для реализации лямбды: я всегда начинаю с пустой среды ([]). Если я узнаю переменную, которая мне нужна в лямбда-выражении, я добавляю ее в среду, в которой я рассматриваю ее достаточное время жизни (или ищу альтернативу).

Обновить:

Как я личный поклонник SVG, Я подготовил (аналогичный) значок меню в виде файла SVG:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN""http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"width="32" height="32">
<title>Start Menu</title>
<desc>Start Menu</desc>
<rect id="line"x="4" y="6" width="24" height="4"style="stroke:none;fill:#444"/>
<use xlink:href="#line" transform="translate(0,8)"/>
<use xlink:href="#line" transform="translate(0,16)"/>
</svg>

Вы можете сохранить это в текстовом файле ic_menu_black.svg и попробуйте загрузить его в ваше приложение.

К сожалению, импортер Qt имеет ограниченную поддержку SVG. Таким образом, не каждый трюк, который возможен в SVG, будет работать правильно в Qt. Поэтому я изменил копию иконки из нашего приложения Qt, чтобы быть уверенным, что этот буду работать.

3

Другие решения

Читая ваш вопрос, я не совсем понял, в чем ваша проблема. К сожалению, из-за политики безопасности нашей компании, i.stack.imgur.com заблокирован и я просто в офисе. Возможно, ваш снимок прояснил для меня вещи. (Я мог бы взглянуть на это, когда я снова дома.)

Однако из любопытства я попытал счастья и хочу показать, что у меня есть:

На самом деле, довольно просто добавить виджет в строку состояния, используя QStatusBar::insertPermanentWidget().

К сожалению, постоянные виджеты расположены справа. Если требование (разместить его слева) является существенным, это можно изменить, вложив две строки состояния друг в друга. (На самом деле, любой другой макет должен быть способен, кроме QMainWindow::setStatusBar() требует QStatusBar*.)

Внутренняя строка состояния — это та, которая должна использоваться для временных сообщений. Чтобы предотвратить забавный эффект дублированных ручек размера, ручку размера отключают для внутренней строки состояния (используя QStatusBar::setSizeGripEnabled()).

Еще одним побочным эффектом вложенных строк состояния является визуализация разделителей слева и справа от внутренней строки состояния. Я считал, что это не так уж плохо (в отношении эстетического аспекта), и мне было все равно.

Может быть, я мог бы использовать просто QLabel (вместо внутренней строки состояния) для отображения сообщений (теряя дополнительные возможности, предоставляемые QStatusBar например тайм-аут для автоматического удаления сообщения).

Чтобы предоставить кнопку запуска, я изначально использовал QPushButton. Помня, что я должен как-то разместить меню «Пуск», я заменил его на QToolBar как это может содержать QAction который в свою очередь готов оборудован для управления подменю.

Когда я заработал, я понял индикатор, который появился на кнопке «Пуск» при настройке меню. Чтобы сделать образец как можно более коротким, я решил пока жить с этим.

После некоторого общения с спрашивающим я добавил альтернативную реализацию, используя класс, производный от QLabel. Размещение меню должно быть сделано самостоятельно. Но, наконец, это не так сложно …

2й версия активируется, если USE_LABEL определяется (в начале исходного кода).

Мой образец testQStartBtn.cc:

#include <QtWidgets>

#define USE_LABEL

#ifdef USE_LABEL
class StartButton: public QLabel {

private:
QMenu *_pQMenu;

public:
StartButton(QWidget *pQParent = 0): QLabel(pQParent), _pQMenu(nullptr) { }

QMenu* menu() { return _pQMenu; }
QMenu* setMenu(QMenu *pQMenu) { std::swap(_pQMenu, pQMenu); return pQMenu; }

protected:
virtual void mousePressEvent(QMouseEvent *pQEvent);
};

void StartButton::mousePressEvent(QMouseEvent *pQEvent)
{
if (_pQMenu && pQEvent->button() == Qt::LeftButton) {
if (!_pQMenu->isVisible()) {
_pQMenu->show(); // a trick to force computation of height() before
_pQMenu->popup(mapToGlobal(pos()) - QPoint(0, _pQMenu->height()));
} else _pQMenu->hide();
}
}
#endif // USE_LABEL

int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
// main application
#undef qApp // undef macro qApp out of the way
QApplication qApp(argc, argv);
// setup GUI
QMainWindow qWin;
QLabel qLblCentral(QString::fromUtf8("Central\nWidget"));
qLblCentral.setAlignment(Qt::AlignCenter);
qWin.setCentralWidget(&qLblCentral);
QStatusBar qStBarLayout;
// the "Windows Start Menu" alike
#ifdef USE_LABEL
StartButton qBtnStart;
qBtnStart.setPixmap(QPixmap(
QApplication::applicationDirPath() + QString::fromLatin1("/Start.png")));
#else // (not) USE_LABEL
QToolBar qToolbarStart;
QIcon qIcnStart(
QApplication::applicationDirPath() + QString::fromLatin1("/Start.png"));
QAction qCmdStart(qIcnStart, QString::fromUtf8("Start"), &qToolbarStart);
#endif // USE_LABEL
QMenu qMenuStart;
QAction qCmdStart1(QString::fromUtf8("Command 1"), &qMenuStart);
qMenuStart.addAction(&qCmdStart1);
QAction qCmdStart2(QString::fromUtf8("Command 2"), &qMenuStart);
qMenuStart.addAction(&qCmdStart2);
QAction qCmdStart3(QString::fromUtf8("Command 3"), &qMenuStart);
qMenuStart.addAction(&qCmdStart3);
#ifdef USE_LABEL
qBtnStart.setMenu(&qMenuStart);
qStBarLayout.insertPermanentWidget(0, &qBtnStart);
#else // (not) USE_LABEL
qCmdStart.setMenu(&qMenuStart);
qToolbarStart.addAction(&qCmdStart);
qStBarLayout.insertPermanentWidget(0, &qToolbarStart);
#endif // USE_LABEL
// the rest of status bar
QStatusBar qStBar;
qStBar.showMessage(QString::fromUtf8("<- Start Menu"), 3000 /* ms */);
qStBar.setSizeGripEnabled(false);
qStBarLayout.insertPermanentWidget(1, &qStBar, 1);
qWin.setStatusBar(&qStBarLayout);
qWin.show();
// install signal handlers
QObject::connect(&qCmdStart1, &QAction::triggered,
[](bool){ qDebug() << "Command 1 triggered"; });
QObject::connect(&qCmdStart2, &QAction::triggered,
[](bool){ qDebug() << "Command 2 triggered"; });
QObject::connect(&qCmdStart3, &QAction::triggered,
[](bool){ qDebug() << "Command 3 triggered"; });
// run application
return qApp.exec();
}

Я скомпилировал с VS2013 и Qt5.6 на Windows 10 (64 бит). Вот как это выглядит:

Снимок testQStartBtn в Windows 10

Обновленная версия с производным QLabel выглядит так:

Снимок обновленного testQStartBtn в Windows 10

2

По вопросам рекламы [email protected]