如何将父小部件焦点重定向到子小部件?

Posted

技术标签:

【中文标题】如何将父小部件焦点重定向到子小部件?【英文标题】:How to redirect parent widget focus to a child widget? 【发布时间】:2019-09-10 07:24:12 【问题描述】:

有一个名为FloatingPointPropertyEditor 的简单类。它继承自 QWidget 并包含一个带有浮点数验证器的 QLineEdit 实例。

class FloatingPointPropertyEditor : public QWidget

    Q_OBJECT

    // ...

private:
    QLineEdit* m_lineEdit;
;

问题是我必须将FloatingPointPropertyEditor 实例的焦点重定向到内部QLineEdit 实例并选择其中的所有文本。也就是说,当FloatingPointPropertyEditor 获得焦点时,用户已经可以在QLineEdit 实例中输入文本,而无需之前单击它。你能解释一下我该怎么做吗?

【问题讨论】:

不能从QLineEdit 继承FloatingPointPropertyEditor @Scheff 我想在将来在FloatingPointPropertyEditor 中添加其他小部件,例如QPushButton。所以这个编辑器可能是一个复杂的小部件。 【参考方案1】:

来自 Qt。博士。关于QWidget::focusPolicy

focusPolicy : Qt::FocusPolicy

此属性保存小部件接受键盘焦点的方式

如果小部件通过 Tab 键接受键盘焦点,则策略为 Qt::TabFocus,如果小部件通过单击接受焦点,则为 Qt::ClickFocus,如果两者都接受,则为 Qt::StrongFocus,如果同时接受,则为 Qt::NoFocus(默认值)它根本不接受焦点。

如果小部件处理键盘事件,您必须为它启用键盘焦点。这通常由小部件的构造函数完成。例如,QLineEdit 构造函数调用 setFocusPolicy(Qt::StrongFocus)。

如果小部件有焦点代理,则焦点策略将传播给它。

关于提到的焦点代理,关于QWidget::setFocusProxy()

void QWidget::setFocusProxy(QWidget *w)

将小部件的焦点代理设置为小部件 w。如果 w 为 nullptr,则该函数将此小部件重置为没有焦点代理。

一些小部件可以“拥有焦点”,但会创建一个子小部件(例如 QLineEdit)来实际处理焦点。在这种情况下,小部件可以将行编辑设置为其焦点代理。

setFocusProxy() 设置小部件,当“这个小部件”获得它时,它实际上将获得焦点。如果有焦点代理,setFocus() 和 hasFocus() 对焦点代理进行操作。

TL;DR

QWidget 的默认焦点策略是Qt::NoFocusQLineEdit 的默认焦点策略是Qt::StrongFocus。有了这个,它应该可以开箱即用(尽管关于setFocusProxy() 的文档使这个恕我直言不明显)。

可以肯定的是,我做了一个小演示testQWidgetFocus.cc

#include <QtWidgets>

class Editor: public QWidget 

  private:
    QHBoxLayout _qHBox;
    QLineEdit _qEdit;
    QPushButton _qBtn0;

  public:
    Editor(QWidget *pQParent = nullptr):
      QWidget(pQParent),
      _qBtn0(">|<")
    
      _qHBox.addWidget(&_qEdit, 1);
      _qBtn0.setFocusPolicy(Qt::NoFocus); 
      _qHBox.addWidget(&_qBtn0);
      setLayout(&_qHBox);
      // signal handler
      connect(&_qBtn0, &QPushButton::clicked,
        [&](bool)  _qEdit.clear(); );
    
    virtual ~Editor() = default;
    Editor(const Editor&) = delete;
    Editor& operator=(const Editor&) = delete;

;

int main(int argc, char **argv)

  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  QWidget qWinMain;
  QFormLayout qForm;
  QLineEdit qEdit1;
  qForm.addRow("QLineEdit:", &qEdit1);
  Editor qEdit2;
  qForm.addRow("Editor:", &qEdit2);
  qDebug() << "qEdit2.focusPolicy():" << qEdit2.focusPolicy();
  qDebug() << "qEdit2.focusProxy():" << qEdit2.focusProxy();
  Editor qEdit3;
  qForm.addRow("Editor:", &qEdit3);
  qWinMain.setLayout(&qForm);
  qWinMain.show();
  return app.exec();

输出:(编译于 VS2017,Qt 5.13)

Qt Version: 5.13.0
qEdit2.focusPolicy(): Qt::NoFocus
qEdit2.focusProxy(): QWidget(0x0)

输出:(用cygwin64编译)

$ g++ --version
g++ (GCC) 7.4.0

$ qmake-qt5 testQWidgetFocus.pro

$ make && ./testQWidgetFocus  
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQWidgetFocus.o testQWidgetFocus.cc
g++  -o testQWidgetFocus.exe testQWidgetFocus.o   -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread 
Qt Version: 5.9.4
qEdit2.focusPolicy(): Qt::FocusPolicy(NoFocus)
qEdit2.focusProxy(): QWidget(0x0)

注意:

我更改了涉事QPushButton的关注策略。因此,它在跳格中被跳过(但仍然可以通过鼠标单击使用)。在不改变其焦点策略的情况下,它也被考虑在 Tab-jumping 中。


构建脚本:

CMakeLists.txt:

project(QWidgetFocus)

cmake_minimum_required(VERSION 3.10.0)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(Qt5Widgets CONFIG REQUIRED)

include_directories("$CMAKE_SOURCE_DIR")

add_executable(testQWidgetFocus
  testQWidgetFocus.cc)
target_link_libraries(testQWidgetFocus
  Qt5::Widgets)

# define QT_NO_KEYWORDS to prevent confusion between of Qt signal-slots and
# other signal-slot APIs
target_compile_definitions(testQWidgetFocus PUBLIC QT_NO_KEYWORDS)

testQWidgetFocus.pro:

SOURCES = testQWidgetFocus.cc

QT += widgets

【讨论】:

以上是关于如何将父小部件焦点重定向到子小部件?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Flutter 中的子小部件调用父小部件功能

如何在颤动中从父小部件调用子小部件的initState()方法

如何从Tkinter,Python中的Text小部件中移除焦点

如何将“stdout”重定向到标签小部件?

如何在构建小部件中使用导航器进行重定向

Qt半透明背景导致子小部件在父小部件中“印记”