我想强调一个QFrame,如果其中一个子窗口小部件具有焦点(这样用户就知道在哪里寻找光标; - )
使用
中的内容ui->frame->setFocusPolicy(Qt::StrongFocus);
ui->frame->setStyleSheet("QFrame:focus {background-color: #FFFFCC;}");
当我点击它时,突出显示QFrame,但是一旦选择了其中一个子窗口小部件,它就会失去焦点。
可能的方法:
我可以connect()
QApplication::focusChanged(old,now)
并检查每个新对象是否是我的QFrame的子项,但这会变得混乱。
我还可以对每个子窗口小部件进行子类化并重新实现focusInEvent()
/ focusOutEvent()
并对此做出反应,但是对于许多不同的窗口小部件,这也是很多工作。
有更优雅的解决方案吗?
答案 0 :(得分:8)
好吧,您可以扩展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);
}
答案 1 :(得分:5)
我相信你给出的两个答案都是错的。它们适用于简单的情况,但非常脆弱和笨拙。我相信最好的解决方案就是您在问题中的实际建议。我会去连接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);
}
优势显而易见。这段代码简短而干净。您只连接一个信号槽,您不需要捕获和处理事件。它可以很好地响应创建对象后所做的任何布局更改。如果你想优化掉不必要的重绘,你应该缓存信息是否有任何孩子聚焦,只改变样式表,并且只有当这个缓存的值被改变时。然后解决方案将是完美的。
答案 2 :(得分:2)
首先,创建一个简单的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
}