====== 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:start|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:start|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
#include
#include
#include
#include
#include
#include
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: \\
{{:frameworks:qt:gui:default.png}}
=== Universalselektor * ===
Der Universalselektor * wählt alle Widgets aus.
== Beispiel ==
Folgendes Beispiel setzt die Vordergrundfarbe aller Widgets auf Rot:
* { color:red }
{{:frameworks:qt:gui:universal.png}}
=== Typ-Selektor ===
Gibt man als Selektor einen Klassennamen an, wird die Klasse und dere Subklassen ausgewählt.
== Beispiel ==
QPushButton { color:red }
{{:frameworks:qt:gui:type.png}}
\\
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 [[frameworks:qt:basic:connections|Siganle/Slots]] und das neuerliche Setzen eines Stylesheets manuell erledigt werden.
== Beispiel ==
QPushButton[flat="true"] { color:red }
QLineEdit[mandatory="true"] { background:blue }
{{:frameworks:qt:gui:property.png}}
\\
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 }
{{:frameworks:qt:gui:class.png}}
\\
Wie beim Typ-Selektor werden die zwei ''QPushButton''-Instanzen rot, jedoch behält die abgeleitete Klasse ''QCommandLinkButton'' ihr aussehen.
=== ID-Selektor ===
In [[html:start|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 }
{{:frameworks:qt:gui:id.png}}
\\
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
#include
#include
#include
#include
#include
#include
#include
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:\\
{{:frameworks:qt:gui:default2.png}}
=== 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 }
{{:frameworks:qt:gui:descendant.png}}
\\
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 ==
{{:frameworks:qt:gui:child.png}}
\\
Hier ist nur das letzte ''QLineEdit'' betroffen, da nur direkte Kinder augewählt werden.
==== Sub-Elemente ====
==== Zustände ====
===== Anwendung im Programm =====
===== Konflikte beheben =====