【问题标题】:Keyboard events QMainWindow inside QMainWindowQMainWindow 中的键盘事件 QMainWindow
【发布时间】:2017-08-14 12:11:53
【问题描述】:

我正在编写一个将在具有一些硬件自定义按钮(映射到功能键组合)的屏幕上使用的应用程序。每个按钮都用来改变显示的屏幕,所以我认为堆叠的小部件似乎是合理的。

为了开发,我创建了一个调试窗口,其中包含这些按钮作为 QPushButtons,并嵌入了“普通”主窗口。在发布版本中运行时,我打算将其作为主窗口运行

这一切都很好,但是按键事件没有像我预期的那样传播。阅读 Qt 文档,它表明具有焦点的小部件将接收事件,因此我假设堆叠小部件中显示的小部件将接收按键但调试窗口始终接收按键事件,无论我设置任何焦点策略

调试窗口:

DebugWindow::DebugWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::GVADemo)
{
    ui->setupUi(this);

    MainWindow* main = new MainWindow(this);
    QLayout* layout = new QHBoxLayout;
    layout->addWidget(main);
    layout->setContentsMargins(0,0,0,0);
    ui->frame->setLayout(layout);
}

void GVADemo::keyPressEvent(QKeyEvent *event)
{
    qDebug("GVADemo keyPressEvent %d", event->key());
}

主窗口:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    Screen* screen1 = new Screen();

    QStackedWidget* stackedWidget = new QStackedWidget(this);

    stackedWidget->addWidget(screen1);
    stackedWidget->setCurrentWidget(screen1);
    setCentralWidget(stackedWidget);
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    qDebug("MainWindow keyPressEvent %d", event->key());
}

堆叠小部件中的屏幕示例:

Screen::Screen(QWidget *parent) : QWidget(parent), ui(new Ui::Screen)
{
    ui->setupUi(this);
}

void SAScreen::keyPressEvent(QKeyEvent *event)
{
    qDebug("Screen keyPressEvent %d", event->key());
}

【问题讨论】:

  • 我记得您可以安装事件过滤器,这些过滤器会在事件“传递”之前触发。 (我自己没有做——因此我只能提供这个粗略的提示......)
  • 请看一下 Qt 文档。 Event Filters。这看起来很有希望。甚至还有一小段QEvent::KeyPress的示例代码...
  • 另一个选项可能是快捷方式(在 Qt 世界之外也称为“加速键”)...SO: Assign shortcut keys to buttons - Qt C++SO: How to Program custom Keyboard Shortcuts。 (奇怪,这不是我的第一个想法……)

标签: qt


【解决方案1】:

使用快捷键可以达到预期的效果(实际上很简单)。

我准备了一个类似于问题中描述的MCVE

// Qt header:
#include <QtWidgets>

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
  QWidget qWin;
  QGridLayout qGrid;
  enum { N = 5 };
  QStackedLayout qStack;
  struct Pane {
    QWidget qWidget;
    QVBoxLayout qVBox;
    QLabel qLbl;
    QLineEdit qTxt;
  } panes[N];
  for (int i = 0; i < N; ++i) {
    Pane &pane = panes[i];
    pane.qLbl.setText(QString::fromUtf8("<b>Pane %0</b>").arg(i + 1));
    pane.qLbl.setTextFormat(Qt::RichText);
    pane.qVBox.addWidget(&pane.qLbl, 1, Qt::AlignCenter);
    pane.qVBox.addWidget(&pane.qTxt);
    pane.qWidget.setLayout(&pane.qVBox);
    qStack.addWidget(&pane.qWidget);
  }
  qGrid.addLayout(&qStack, 0, 0, 1, N);
  qGrid.setRowStretch(0, 1);
  QPushButton qBtns[N];
  for (int i = 0; i < N; ++i) {
    QPushButton &qBtn = qBtns[i];
    qBtn.setText(QString::fromUtf8("F%0").arg(i + 1));
    qBtn.setShortcut(Qt::Key_F1 + i);
    qGrid.addWidget(&qBtn, 1, i);
  }
  qWin.setLayout(&qGrid);
  qWin.show();
  // install signal handlers
  for (int i = 0; i < N; ++i) {
    QPushButton &qBtn = qBtns[i];
    QObject::connect(&qBtn, &QPushButton::clicked,
      [&qStack, i](bool) { qStack.setCurrentIndex(i); });
  }
  // run application
  return qApp.exec();
}

我在 Windows 10(64 位)中使用 VS2013 和 Qt5.6 编译测试:

注意:

我测试了单击按钮以及使用相应的功能键。因此,我将焦点设置到 QLineEdit 以确保它独立于当前焦点小部件工作。


更新:

在 OP 中,按钮是硬件按钮。因此,我修改了示例的第一个版本以在没有QPushButtons 的情况下工作。直接添加到主窗口的QActions 可以达到同样的效果(使用QWidget::addAction()):

// Qt header:
#include <QtWidgets>

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
  QWidget qWin;
  QGridLayout qGrid;
  enum { N = 5 };
  QStackedLayout qStack;
  struct Pane {
    QWidget qWidget;
    QVBoxLayout qVBox;
    QLabel qLbl;
    QLineEdit qTxt;
  } panes[N];
  for (int i = 0; i < N; ++i) {
    Pane &pane = panes[i];
    pane.qLbl.setText(QString::fromUtf8("<b>Pane %0</b>").arg(i + 1));
    pane.qLbl.setTextFormat(Qt::RichText);
    pane.qVBox.addWidget(&pane.qLbl, 1, Qt::AlignCenter);
    pane.qVBox.addWidget(&pane.qTxt);
    pane.qWidget.setLayout(&pane.qVBox);
    qStack.addWidget(&pane.qWidget);
  }
  qGrid.addLayout(&qStack, 0, 0, 1, N);
  qGrid.setRowStretch(0, 1);
  QLabel qLbls[N];
  for (int i = 0; i < N; ++i) {
    QLabel &qLbl = qLbls[i];
    qLbl.setText(QString::fromUtf8("F%0").arg(i + 1));
    qGrid.addWidget(&qLbl, 1, i);
  }
  qWin.setLayout(&qGrid);
  QAction *pQCmds[N];
  for (int i = 0; i < N; ++i) {
    QAction *pQCmd = pQCmds[i] = new QAction(&qWin);
    pQCmd->setShortcut(Qt::Key_F1 + i);
    qWin.addAction(pQCmd);
  }
  qWin.show();
  // install signal handlers
  for (int i = 0; i < N; ++i) {
    QObject::connect(pQCmds[i], &QAction::triggered,
      [&qStack, i](bool) { qStack.setCurrentIndex(i); });
  }
  // run application
  return qApp.exec();
}

我在Windows 10(64位)中使用VS2013和Qt5.6再次编译测试:

注意:

QLineEdits 被聚焦(默认),而功能键已用于切换QStackLayout 的当前索引。

【讨论】:

  • @Firedragon 我添加了第二个版本,其中使用QAction 而不是QPushButton,记住问题中的“硬件按钮”分配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多