2012-12-28 9 views
6

Vorrei evidenziare un QFrame, se uno dei suoi widget bambino è messa a fuoco (così gli utenti sanno dove cercare il cursore ;-)Come modificare lo sfondo di un widget genitore quando è attivo un widget figlio?

usando qualcosa insieme

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

evidenzia QFrame quando clicco su di esso, ma perde il suo focus quando viene selezionato uno dei suoi widget figli.

possibili approcci:

  • potevo connect()QApplication::focusChanged(old,now) e verificare ogni nuovo oggetto se si tratta di un bambino della mia QFrame, ma questo diventa disordinato.

  • Potrei anche sottoclasse di ciascun widget bambino e reimplementare focusInEvent()/focusOutEvent() e reagire su quello, ma con un sacco di diversi widget, questo è anche un sacco di lavoro.

Esiste una soluzione più elegante?

risposta

7

Bene, è possibile estendere QFrame per farlo ascoltare sul cambio di messa a fuoco dei suoi widget figli. Oppure puoi anche installare un filtro eventi sui widget per bambini per catturare QFocusEvent.

Ecco un esempio:

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); 
} 
+0

Archie, grazie per la risposta. Puoi darmi un puntatore su come estendere QFrame per questo scopo? – Elwood

+0

Perfetto. Mi è particolarmente piaciuta l'idea di 'hookChildrenWidgetsFocus()'. (Ma ho un po 'di cattiva coscienza perché ho portato via l'accettazione da Fred, che ha avuto un approccio simile prima. Grazie mille!) – Elwood

2

In primo luogo, creare un semplice sottoclasse di QFrame che reimplementa la funzione virtuale 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); 
}; 

Uso MyFrame invece di QFrame per contenere i widget. Poi, da qualche parte nel codice in cui si creano i widget contenuti nella MyFrame, installare un filtro eventi su quei widget:

// ... 
    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); 
    //... 

A quel punto, MyFrame::eventFilter() saranno chiamati prima ogni caso viene consegnato al widget, lasciando agisci su di esso prima che il widget ne sia a conoscenza. Entro MyFrame::eventFilter(), restituire true se si desidera filtrare l'evento (ovvero non si desidera che il widget elabori l'evento) o restituire false in caso contrario.

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 
} 
+0

Questo è quello che stavo cercando, grazie mille, Fred! C'è un motivo per 'if (guardato == m_button)' in 'eventFilter()'? Funziona bene senza e posso aggiungere tanti widget senza preoccuparmi ... – Elwood

+0

Nel tuo caso particolare, non ne hai bisogno, ma 'MyFrame' potrebbe voler vedere diversi widget per diversi eventi, quindi un semplice controllo su' guardato' ti fa sapere quale widget ha ricevuto un evento (es. vuoi usare un colore di sfondo diverso a seconda del widget che ha il focus) – Fred

+0

Argh, perché non posso accettare due risposte? Tu e Archie lavoravate allo stesso tempo per aiutarmi, entrambi avete trovato una soluzione simile. Mi dispiace di aver finalmente accettato la risposta di Archie poiché la sua soluzione è un po 'più completa. Ma le tue spiegazioni erano migliori. Spero che tu possa perdonarmi ...;) – Elwood

Problemi correlati