11

子ウィジェットの 1 つにフォーカスがある場合、QFrame を強調表示したいと思います (したがって、ユーザーはカーソルを探す場所を知ることができます ;-)

一緒に何かを使用して

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

クリックするとQFrameが強調表示されますが、子ウィジェットの1つが選択されるとフォーカスが失われます。

可能なアプローチ:

  • connect() QApplication::focusChanged(old,now)新しいオブジェクトが QFrame の子であるかどうかを確認することはできますが、これは面倒です。

  • また、各子ウィジェットをサブクラス化し、 focusInEvent()/を再実装focusOutEvent()してそれに反応することもできますが、さまざまなウィジェットが多数あるため、これも大変な作業です。

よりエレガントなソリューションはありますか?

4

3 に答える 3

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);
}
于 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);
}

利点は明らかです。このコードは短くてきれいです。信号スロットを 1 つだけ接続します。イベントをキャッチして処理する必要はありません。オブジェクトの作成後に行われたレイアウトの変更にうまく対応します。また、不要な再描画を最適化したい場合は、子がフォーカスされているかどうかの情報をキャッシュし、このキャッシュされた値が変更された場合にのみスタイルシートを変更する必要があります。その後、解決策は完璧です。

于 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()に呼び出されるため、ウィジェットが認識する前にアクションを実行できます。内で、イベントを除外する (つまり、ウィジェットにイベントを処理させたくない) 場合は return を、そうでない場合は return を返します。MyFrame::eventFilter()truefalse

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 に答える