11

我想突出显示一个 QFrame,如果它的其中一个子小部件具有焦点(因此用户知道在哪里寻找光标 ;-)

一起使用某些东西

ui->frame->setFocusPolicy(Qt::StrongFocus);
ui->frame->setStyleSheet("QFrame:focus {background-color: #FFFFCC;}");

当我单击它时突出显示 QFrame,但是一旦选择了它的一个子小部件,它就会失去焦点。

可能的方法:

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

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

有没有更优雅的解决方案?

4

3 回答 3

8

好吧,您可以扩展QFrame以使其监听其子小部件的焦点变化。或者你也可以在子部件上安装一个事件过滤器来捕捉QFocusEvent

这是一个例子:

我的框架.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

我的框架.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);
}

主窗口.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);
}
于 2012-12-28T16:12:13.607 回答
6

我相信你给出的两个答案都是错误的。它们适用于简单的情况,但非常脆弱和笨拙。我相信最好的解决方案是您在问题中实际提出的建议。我会去连接到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);
}

优势很明显。这段代码简洁明了。你只连接一个信号槽,你不需要捕捉和处理事件。它可以很好地响应对象创建后所做的任何布局更改。如果你想优化掉不必要的重绘,你应该缓存信息是否有任何孩子被聚焦,并且只有当这个缓存的值被改变时才改变样式表。那么解决方案将是完美的。

于 2018-04-24T12:34:26.320 回答
3

首先,创建一个简单的子类,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
}
于 2012-12-28T19:45:19.260 回答