В моем приложении 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!";
}
всплывающее меню при нажатии кнопки меню
Я взял ваш пример кода и изменил его, чтобы проиллюстрировать то, что я описал в моих последних комментариях:
Добавьте переменную-член к 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, чтобы быть уверенным, что этот буду работать.
Читая ваш вопрос, я не совсем понял, в чем ваша проблема. К сожалению, из-за политики безопасности нашей компании, 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 бит). Вот как это выглядит:
Обновленная версия с производным QLabel
выглядит так: