Gestaltung von Qt-Oberflächen über Stylesheets (CSS)

Einleitung

Normalerweise passen sich Widgets optisch an die verwendete Oberfläche an. Qt-Oberflächen können aber auch über in der Web-Programmierung übliche CSS-Stylesheets gestaltet werden. Dadurch ist es möglich, seiner Anwendung ein ganzes bestimmtes Aussehen zu verleihen.

Syntax

Allgemein

Größtenteils kann der in der Web-Programmierung übliche Syntax verwendet werden. Qt bietet aber einige zusätzliche Funktionen, um die Gestaltung zu vereinfachen. An dieser Stelle wird aber nur eine grobe Übersicht über die Verwendung von CSS geboten, nähere Informationen befinden sich in einem eigenen CSS-Tutorial.
Die Gestaltung funktioniert über Änderung von Attributen für bestimmte Objekte. Über einen sogennanten Selektor werden Objekte ausgewählt. CSS verwendet folgenden Syntax:

selector { property:value; property:value; ... }

Dabei können beliebig viele property:value-Paare verwendet werden. Bei nur einem Paar kann der Semikolon weggelassen werden. Folgendes Beispiel färbt alle Objekte der Klasse QPushButton rot:

QPushButton { color:red }

Als Selektor dient in diesem Fall die Klasse QPushButton, dessen Attribut color auf red gesetzt wird.

Selektoren

Qt bietet jedoch viel flexiblere Selektoren als nur Klassennamen. Um die die Selektoren zu testen, verwenden wir folgendes Programm:

#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QCommandLinkButton>
#include <QVariant>
 
int main( int argc, char *argv[] )
{
  QApplication app( argc, argv );
  QWidget *widget = new QWidget();
  QPushButton *pButton1 = new QPushButton( "QPushButton 1" ),
      *pButton2 = new QPushButton( "QPushButton 2" );
  QCommandLinkButton *cLinkButton = new QCommandLinkButton( "QCommandLinkButton" );
  QLineEdit *lineEdit1 = new QLineEdit(),
      *lineEdit2 = new QLineEdit();
  pButton1->setObjectName( "pButton1" );
  pButton2->setFlat( true );
  lineEdit2->setProperty( "mandatory", true );
  //app.setStyleSheet( "" );
  QVBoxLayout *vLayout = new QVBoxLayout();
  QHBoxLayout *hLayout = new QHBoxLayout();
  hLayout->addWidget( pButton1 );
  hLayout->addWidget( pButton2 );
  vLayout->addLayout( hLayout );
  hLayout = new QHBoxLayout();
  hLayout->addWidget( cLinkButton );
  vLayout->addLayout( hLayout );
  hLayout = new QHBoxLayout();
  hLayout->addWidget( lineEdit1 );
  hLayout->addWidget( lineEdit2 );
  vLayout->addLayout( hLayout );
  widget->setLayout( vLayout );
  widget->show();
  return app.exec();
}

An der auskommentierten Stelle wird jeweils das entsprechende Stylesheet übergeben. Ohne Stylesheet sieht das Widget unter KDE4 so aus:

Universalselektor *

Der Universalselektor * wählt alle Widgets aus.

Beispiel

Folgendes Beispiel setzt die Vordergrundfarbe aller Widgets auf Rot:

* { color:red }

Typ-Selektor

Gibt man als Selektor einen Klassennamen an, wird die Klasse und dere Subklassen ausgewählt.

Beispiel
QPushButton { color:red }


Anzumerken ist hier, dass sowohl QPushButton, als auch die Subklasse QCommandLinkButton verändert wird, alle andern Widgets ihr Aussehen jedoch behalten.

Attribut-Selektor

Es können auch nur Widgets mit bestimmten Eigenschaften ausgewählt werden. Dabei können alle mit QVariant kompatiblen Attribute verwendet werden. Die Bedingung wird dabei in eckigen Klammern nach dem Typ- bzw. Universalselektor geschrieben. Attribute können auch über die Methode QObject::setProperty() gesetzt werden. Die Abfrage erfolgt auf gleichheit mit dem =-Operator, bzw. mit ~=, ob das Attribut den angegeben Text enthält. Der Text steht in Anführungszeichen und muss in C++ natürlich dementsprechend maskiert werden.
Widgets die über diesen Selektor ausgewählt wurden, werden aber nicht automatisch aktualisiert, nachdem sich ihr Wert ändert! Dieses Verhalten muss über Siganle/Slots und das neuerliche Setzen eines Stylesheets manuell erledigt werden.

Beispiel
QPushButton[flat="true"] { color:red }
QLineEdit[mandatory="true"] { background:blue }


Hier werden nur der Button, auf den setFlat( true ) angewendet wurde, sowie die QLineEdit-Instanz mit dem manuell gesetzten Attribut mandatory ausgewählt.

Klassen-Selektor

Dieser Selektor funktioniert ähnlich wie der Typ-Selektor, jedoch werden abgeleitete Klassen nicht berücksichtigt. Vor dem Klassennamen wird der .-Operator verwendet.

Beispiel
.QPushButton { color:red }


Wie beim Typ-Selektor werden die zwei QPushButton-Instanzen rot, jedoch behält die abgeleitete Klasse QCommandLinkButton ihr aussehen.

ID-Selektor

In HTML können Tags über eine ID identifziert werden. Qt-Stylesheets erkennen Objekte über ihren Objektnamen, der über QObject::setObjectName() gesetzt wird. Dazu wird nach dem Klassennamen (es kann sowohl ein Typ- als auch ein Klassen-Selektor verwendet werden) der #-Operator mit nachfolgendem Obejktnamen verwendet.

Beispiel
QPushButton#pButton1 { color:red }


Der Button mit dem Objektnamen pButton1 wird ausgewählt, sonst nichts.

Für die nächsten beiden Beispiele wird eine leicht erweiterte Version des Codes verwendet, bei dem die beiden QLineEdit-Instanzen sich zusätzlich in einer QGroupBox befinden und am Ende noch zusätzlich ein QLineEdit eingefügt wird.

#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QCommandLinkButton>
#include <QVariant>
#include <QGroupBox>
 
int main( int argc, char *argv[] )
{
  QApplication app( argc, argv );
  QWidget *widget = new QWidget();
  QPushButton *pButton1 = new QPushButton( "QPushButton 1" ),
      *pButton2 = new QPushButton( "QPushButton 2" );
  QCommandLinkButton *cLinkButton = new QCommandLinkButton( "QCommandLinkButton" );
  QLineEdit *lineEdit1 = new QLineEdit(),
      *lineEdit2 = new QLineEdit();
  QGroupBox *box = new QGroupBox();
  pButton1->setObjectName( "pButton1" );
  pButton2->setFlat( true );
  QVBoxLayout *vLayout = new QVBoxLayout();
  vLayout->addWidget( lineEdit1 );
  vLayout->addWidget( lineEdit2 );
  box->setLayout( vLayout );
  lineEdit2->setProperty( "mandatory", true );
  //app.setStyleSheet( "" );
  vLayout = new QVBoxLayout();
  QHBoxLayout *hLayout = new QHBoxLayout();
  hLayout->addWidget( pButton1 );
  hLayout->addWidget( pButton2 );
  vLayout->addLayout( hLayout );
  hLayout = new QHBoxLayout();
  hLayout->addWidget( cLinkButton );
  vLayout->addLayout( hLayout );
  vLayout->addWidget( box );
  vLayout->addWidget( new QLineEdit() );
  widget->setLayout( vLayout );
  widget->show();
  return app.exec();
}

Ohne Stylesheet sieht das folgendermaßen aus:

Nachfahr-Selektor

Wählt Widgets aus, die in der Objekt-Hierarchie unter einem bestimmten anderen stehen. Dabei wird zuerst das Objekt angegeben dessen Nachfahren behandelt werden sollen, dann eine Einschränkung der Nachfahren. In beiden Fällen können sowohl- als auch ein Klassen-Selektoren verwendet werden.

Beispiel
.QWidget QLineEdit { background:blue }


In diesem Fall bekommen alle QLineEdit-Objekte die in der Hierarchie unter einem QWidget (ohne Subklassen!) stehen, einen blauen Hintergrund.

Kind-Selektor

Wie der Nachfahr-Selektor, jedoch sind nur direkte Nachfahren betroffen.

Beispiel


Hier ist nur das letzte QLineEdit betroffen, da nur direkte Kinder augewählt werden.

Sub-Elemente

Zustände

Anwendung im Programm

Konflikte beheben