繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM