如何在qt中的状态栏中创建菜单,如PC开始菜单

时间:2017-06-09 03:56:44

标签: c++ qt statusbar

在我的qt应用程序中,我设计了带有少量图标的状态栏。在状态栏的左侧,我添加了菜单图标(QPixmap),当我点击该图标时,我需要显示类似于PC开始菜单的菜单。我搜索了很多,但我没有找到Qwidget。

这是我的qt应用程序窗口

根据我最近的评论,我在下面添加了我编辑的代码,因为我无法在评论部分添加代码,plz建议解决方案

//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!";

}
单击菜单按钮

弹出菜单

2 个答案:

答案 0 :(得分: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)。所以,请带上一粒盐&#34;。

注意:

我刚刚意识到你使用&#34; pre-Qt5&#34;连接信号处理程序的风格。 Qt5引入了一个很好的extension to its signals支持函数指针以及方法指针。因此,我能够将信号处理程序简单地作为lambdas。 (lambda是这些[](bool){/*...*/}我连接到动作槽。我总是怀疑这些新的lambdas,直到我意识到它们是一个很好的工具,可以为信号处理程序编写非常简单的适配器,以及制作非常紧凑的样本代码。)

关于Lambdas的更新:

括号[]可用于将变量添加到lambda的环境(或上下文)。如果它是空的(因为我使用它),只能使用全局变量和函数。使用[this]表示将当前this指针添加到环境中。因此,lambda可以访问当前类实例的成员变量。

当我第一次认出lambdas时,我经常看到[=]意味着lambda获取调用函数的当前上下文(例如每个本地函数变量的副本)。我个人认为这很危险,特别是对于信号处理程序。我会详细说明一下:

lambda的环境允许从外部访问&#34;变量&#34;在lambda体内。可以通过值或通过引用添加变量。如果变量是通过引用提供的,则可以在lambda体中访问它,但它不会授予&#34;外部&#34;的使用寿命。变量足够长。 (如果不是它会导致&#34;悬空参考&#34;使用相同的未定义行为,就像悬挂指针一样。)它是否有助于防止引用?不可以。它适用于原始类型(例如intfloat)但对于对象可能效率低(甚至语义错误)。使用指向对象的指针与使用引用一样危险。因此,lambda的环境应该与 care 一起使用。

根据经验,我个人使用以下方法来实现lambdas:我总是从一个空的环境开始([])。如果我在lambda中识别出我需要的变量,我将它添加到环境中,我认为它足够的生命周期(或寻找替代方案)。

更新

由于我是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应用程序中的图标副本,以确保可以正常工作。

答案 1 :(得分:2)

阅读你的问题,我不太清楚你的实际问题是什么。很遗憾,由于我们公司的安全政策,i.stack.imgur.com被屏蔽,我只是在办公室。也许,你的快照已经为我澄清了一些事情。 (当我再次回到家时,我可以看看它。)

然而,出于好奇,我试了试运气并希望展示我所拥有的东西:

实际上,使用QStatusBar::insertPermanentWidget()将小部件添加到状态栏非常容易。

不幸的是,永久小部件位于右侧。如果要求(将其放在左侧)是必要的,可以通过将两个状态栏嵌套到彼此来调整。 (实际上,每个其他布局也应该有能力,但QMainWindow::setStatusBar()需要QStatusBar*。)

内部状态栏是必须用于临时消息的状态栏。为防止重复大小夹点的滑稽效果,内部状态栏禁用大小夹点(使用QStatusBar::setSizeGripEnabled())。

嵌套状态栏的另一个副作用是内部状态栏左侧和右侧的分隔符可视化。我认为这不是那么糟糕(关于美学方面)而且并不关心它。

可能是,我本可以使用QLabel(而不是内部状态栏)来显示消息(失去QStatusBar提供的额外功能,例如自动清除消息的超时)。

要提供开始按钮,我最初使用的是QPushButton。请记住,我必须以某种方式放置开始菜单,并将其替换为QToolBar,因为它可能包含QAction,而QLabel可以随时配备管理子菜单。

当我开始工作时,我意识到当我设置菜单时出现在开始按钮上的指示器。为了让样本尽可能短,我现在决定忍受它。

在与提问者进行了一些沟通之后,我使用从Snapshot of testQStartBtn on Windows 10派生的类添加了一个替代实现。菜单放置必须自己完成。但最后,它并不复杂......

如果定义了USE_LABEL(在源代码的开头附近),则激活2 nd 版本。

我的样本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();
}

我在Windows 10(64位)上使用VS2013和Qt5.6编译。这是它的外观:

Snapshot of updated testQStartBtn on Windows 10

带有派生QLabel的更新版本如下所示:

{{3}}