如何在子窗口小部件具有焦点时更改父窗口小部件的背景?

时间:2012-12-28 15:16:55

标签: qt qt4 focus qwidget

我想强调一个QFrame,如果其中一个子窗口小部件具有焦点(这样用户就知道在哪里寻找光标; - )

使用

中的内容
ui->frame->setFocusPolicy(Qt::StrongFocus);
ui->frame->setStyleSheet("QFrame:focus {background-color: #FFFFCC;}");
当我点击它时,

突出显示QFrame,但是一旦选择了其中一个子窗口小部件,它就会失去焦点。

可能的方法:

  • 我可以connect() QApplication::focusChanged(old,now)并检查每个新对象是否是我的QFrame的子项,但这会变得混乱。

  • 我还可以对每个子窗口小部件进行子类化并重新实现focusInEvent() / focusOutEvent()并对此做出反应,但是对于许多不同的窗口小部件,这也是很多工作。

有更优雅的解决方案吗?

3 个答案:

答案 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
}