Qt 如何更改父窗口小部件';子窗口小部件具有焦点时的背景?
如果QFrame的一个子部件具有焦点,我想突出显示QFrame(这样用户就知道在哪里查找光标;-) 用一些东西Qt 如何更改父窗口小部件';子窗口小部件具有焦点时的背景?,qt,qt4,focus,qwidget,Qt,Qt4,Focus,Qwidget,如果QFrame的一个子部件具有焦点,我想突出显示QFrame(这样用户就知道在哪里查找光标;-) 用一些东西 ui->frame->setFocusPolicy(Qt::StrongFocus); ui->frame->setStyleSheet("QFrame:focus {background-color: #FFFFCC;}"); 当我点击QFrame时,它会高亮显示,但一旦它的一个子部件被选中,它就会失去焦点 可能的办法: 我可以connect()QApp
ui->frame->setFocusPolicy(Qt::StrongFocus);
ui->frame->setStyleSheet("QFrame:focus {background-color: #FFFFCC;}");
当我点击QFrame时,它会高亮显示,但一旦它的一个子部件被选中,它就会失去焦点
可能的办法:
- 我可以
connect()
检查每个新对象是否是我的QFrame的子对象,但这会变得很混乱QApplication::focusChanged(old,now)
- 我还可以对每个子窗口小部件进行子类化,并重新实现
/focusInEvent()
,并对其做出反应,但对于许多不同的窗口小部件,这也是一项艰巨的工作focusOutEvent()
有更优雅的解决方案吗?好吧,您可以扩展
QFrame
,让它监听子部件的焦点变化。
或者,您也可以在子窗口小部件上安装事件过滤器,以捕获QFocusEvent
以下是一个例子:
MyFrame.h
#如果取消我的框架#
#定义我的框架
#包括
类MyFrame:publicqframe
{
Q_对象
公众:
显式MyFrame(QWidget*parent=0,Qt::WindowFlags f=0);
void hookChildrenWidgetsFocus();
受保护的:
bool事件过滤器(QObject*对象,QEvent*事件);
私人:
QString m_原始样式表;
};
#endif//MYFRAME\u H
MyFrame.cpp
#包括
#包括“MyFrame.h”
MyFrame::MyFrame(QWidget*父项,Qt::WindowFlags f)
:QFrame(父对象,f)
{
m_originalStyleSheet=样式表();
}
void MyFrame::hookChildrenWidgetsFocus()
{
foreach(QObject*child,children()){
如果(子->isWidgetType()){
子->installEventFilter(此);
}
}
}
bool MyFrame::eventFilter(QObject*对象,QEvent*事件)
{
如果(事件->类型()==QEvent::FocusIn){
设置样式表(“背景色:#FFFFCC;”);
}else if(事件->类型()==QEvent::FocusOut){
设置样式表(m_原始样式表);
}
返回QObject::eventFilter(对象,事件);
}
MainWindow.cpp
#包括
#包括
#包括
#包括“MyFrame.h”
#包括“mainwindow.h”
主窗口::主窗口(QWidget*父窗口):
QMainWindow(父窗口)
{
设置窗口标题(tr(“测试”);
MyFrame*frame1=新的MyFrame(此);
frame1->setLayout(新的QVBoxLayout());
frame1->layout()->addWidget(新的QLineEdit());
frame1->layout()->addWidget(新的QLineEdit());
frame1->layout()->addWidget(新的QLineEdit());
frame1->hookChildrenWidgetsFocus();
MyFrame*frame2=新的MyFrame(此);
frame2->setLayout(新的QVBoxLayout());
frame2->layout()->addWidget(新的QLineEdit());
frame2->layout()->addWidget(新的QLineEdit());
frame2->layout()->addWidget(新的QLineEdit());
frame2->hookChildrenWidgetsFocus();
QHBoxLayout*centralLayout=新的QHBoxLayout();
centralLayout->addWidget(框架1);
centralLayout->addWidget(框架2);
QWidget*centralWidget=新的QWidget();
centralWidget->setLayout(centralLayout);
setCentralWidget(centralWidget);
}
首先,创建QFrame
的一个简单子类,它重新实现eventFilter(QObject*,QEvent*)
虚拟函数:
class MyFrame : public QFrame {
Q_OBJECT
public:
MyFrame(QWidget *parent = 0, Qt::WindowFlags f = 0);
~MyFrame();
virtual bool eventFilter(QObject *watched, QEvent *event);
};
使用MyFrame
而不是QFrame
来包含小部件。然后,在代码中创建MyFrame
中包含的小部件的某个地方,在这些小部件上安装一个事件过滤器:
// ...
m_myFrame = new MyFrame(parentWidget);
QVBoxLayout *layout = new QVBoxLayout(myFrame);
m_button = new QPushButton("Widget 1", myFrame);
layout->addWidget(m_button);
m_button->installEventFilter(myFrame);
//...
此时,MyFrame::eventFilter()
将在将任何事件传递到小部件之前调用,让您在小部件意识到之前对其进行操作。在MyFrame::eventFilter()
中,如果要筛选出事件(即不希望小部件处理事件),则返回true
,否则返回false
bool MyFrame::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_button) { // An event occured on m_button
switch (event -> type()) {
case QEvent::FocusIn:
// Change the stylesheet of the frame
break;
case QEvent::FocusOut:
// Change the stylesheet back
break;
default:
break;
}
}
return false; // We always want the event to propagate, so always return false
}
我相信你得到的两个答案都是错误的。它们适用于简单的箱子,但极其脆弱和笨拙。我相信最好的解决办法就是你在问题中实际提出的建议。我会选择连接到
QApplication::focusChanged(from,to)
。您只需将主帧对象连接到此信号,然后在插槽中检查to
对象(接收焦点的对象)是否是帧对象的子对象
Frame::Frame(...)
{
// ...
connect(qApp, &QApplication::focusChanged, this, &Frame::onFocusChanged);
// ...
}
// a private method of your Frame object
void Frame::onFocusChanged(QWidget *from, QWidget *to)
{
auto w = to;
while (w != nullptr && w != this)
w = w->parentWidget();
if (w == this) // a child (or self) is focused
setStylesheet(highlightedStylesheet);
else // something else is focused
setStylesheet(normalStylesheet);
}
优势是显而易见的。这段代码简短明了。您只连接一个信号插槽,不需要捕获和处理事件。它可以很好地响应创建对象后所做的任何布局更改。如果你想优化掉不必要的重画,你应该缓存信息,不管是否有子对象被聚焦,并且只在这个缓存的值被改变时才改变样式表。那么解决方案将是完美的。阿尔奇,谢谢你的回复。你能给我一个关于如何扩展QFrame的指针吗?太好了。我特别喜欢hookchildrenwidgetsfous()的想法。(但我有点良心不好,因为我剥夺了弗雷德的认可,他早些时候提出了类似的方法。非常感谢你们两位!)恐怕这不是公认的答案。它适用于这种简单的情况,但远不是一个好的解决方案。它非常脆弱,很容易出错。1) 如果布局变得动态,并且在运行时添加或删除了一些子窗口小部件,该怎么办。2) 如果该对象希望在其他对象及其焦点事件(子对象除外)上安装事件过滤器,则可能会执行其他操作。@V.K.我认为您试图将特定的实现问题引入其中。拟议的解决方案并不意味着具有普遍性。作为一名开发人员,您知道特定设计的局限性(例如,您是否希望使用其他事件过滤器或动态布局),因此您应该根据自己的设计调整此代码段。无论如何,这些都不是原问题中提到的先决条件。@Archie我认为一个人的目标应该是最简单和最简单的
// ...
m_myFrame = new MyFrame(parentWidget);
QVBoxLayout *layout = new QVBoxLayout(myFrame);
m_button = new QPushButton("Widget 1", myFrame);
layout->addWidget(m_button);
m_button->installEventFilter(myFrame);
//...
bool MyFrame::eventFilter(QObject *watched, QEvent *event)
{
if (watched == m_button) { // An event occured on m_button
switch (event -> type()) {
case QEvent::FocusIn:
// Change the stylesheet of the frame
break;
case QEvent::FocusOut:
// Change the stylesheet back
break;
default:
break;
}
}
return false; // We always want the event to propagate, so always return false
}
Frame::Frame(...)
{
// ...
connect(qApp, &QApplication::focusChanged, this, &Frame::onFocusChanged);
// ...
}
// a private method of your Frame object
void Frame::onFocusChanged(QWidget *from, QWidget *to)
{
auto w = to;
while (w != nullptr && w != this)
w = w->parentWidget();
if (w == this) // a child (or self) is focused
setStylesheet(highlightedStylesheet);
else // something else is focused
setStylesheet(normalStylesheet);
}