[英]How to change a parent widget's background when a child widget has focus?
我想强调一个QFrame,如果它的一个子小部件有焦点(所以用户知道在哪里寻找光标;-)
用的东西
ui->frame->setFocusPolicy(Qt::StrongFocus);
ui->frame->setStyleSheet("QFrame:focus {background-color: #FFFFCC;}");
当我点击它时突出显示QFrame,但是一旦选择了其中一个子窗口小部件,它就会失去焦点。
可能的方法:
我可以connect()
QApplication::focusChanged(old,now)
并检查每个新对象是否是我的QFrame的子对象,但这会变得混乱。
我也可以子类化每个子窗口小部件并重新实现focusInEvent()
/ focusOutEvent()
并对此作出反应,但是对于许多不同的窗口小部件,这也是很多工作。
有更优雅的解决方案吗?
好吧,您可以扩展QFrame
以使其监听其子窗口小部件的焦点更改。 或者,您还可以在子窗口小部件上安装事件过滤器以捕获QFocusEvent
。
这是一个例子:
MyFrame.h
#ifndef MYFRAME_H
#define MYFRAME_H
#include <QFrame>
class MyFrame : public QFrame
{
Q_OBJECT
public:
explicit MyFrame(QWidget* parent = 0, Qt::WindowFlags f = 0);
void hookChildrenWidgetsFocus();
protected:
bool eventFilter(QObject *object, QEvent *event);
private:
QString m_originalStyleSheet;
};
#endif // MYFRAME_H
MyFrame.cpp
#include <QEvent>
#include "MyFrame.h"
MyFrame::MyFrame(QWidget *parent, Qt::WindowFlags f)
: QFrame(parent, f)
{
m_originalStyleSheet = styleSheet();
}
void MyFrame::hookChildrenWidgetsFocus()
{
foreach (QObject *child, children()) {
if (child->isWidgetType()) {
child->installEventFilter(this);
}
}
}
bool MyFrame::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::FocusIn) {
setStyleSheet("background-color: #FFFFCC;");
} else if (event->type() == QEvent::FocusOut) {
setStyleSheet(m_originalStyleSheet);
}
return QObject::eventFilter(object, event);
}
MainWindow.cpp
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLineEdit>
#include "MyFrame.h"
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setWindowTitle(tr("Test"));
MyFrame *frame1 = new MyFrame(this);
frame1->setLayout(new QVBoxLayout());
frame1->layout()->addWidget(new QLineEdit());
frame1->layout()->addWidget(new QLineEdit());
frame1->layout()->addWidget(new QLineEdit());
frame1->hookChildrenWidgetsFocus();
MyFrame *frame2 = new MyFrame(this);
frame2->setLayout(new QVBoxLayout());
frame2->layout()->addWidget(new QLineEdit());
frame2->layout()->addWidget(new QLineEdit());
frame2->layout()->addWidget(new QLineEdit());
frame2->hookChildrenWidgetsFocus();
QHBoxLayout *centralLayout = new QHBoxLayout();
centralLayout->addWidget(frame1);
centralLayout->addWidget(frame2);
QWidget *centralWidget = new QWidget();
centralWidget->setLayout(centralLayout);
setCentralWidget(centralWidget);
}
我相信你给出的两个答案都是错的。 它们适用于简单的情况,但非常脆弱和笨拙。 我相信最好的解决方案就是您在问题中的实际建议。 我会去连接到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
子类,重新实现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
包含的小部件的代码中的某个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
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.