【QSS】QSSの理解を深めよう Subcontrol編 ②【PySide2】

python,qt,PySide,PySide2,QSS

初めに、この記事は【QSS】QSSの理解を深めよう Subcontrol編 ①【PySide2】の続きになります
QSSやSubControlについて全く分からない方はぜひひとつ前の記事から読んでみてください


今回はQSSの理解を深めよう Subcontrol編 2回目の記事となります
この記事のひとつ前に公開しました【QSS】コピペで実装できるデザインQSpinBox 4種 【QSpinBox 】を基にSubcontrolを具体的に説明していきましょう

QSpinBox Subcontrol

QSpinBoxのサブコントロールは4種類になります

QSpinBox Subcontrol 説明
::up-button subcontrol-originはQSpinBoxに相対的です
::down-button subcontrol-originはQSpinBoxに相対的です
::up-arrow ::up-arrowは::up-buttonに表示され、subcontrol-originは::up-buttonからの相対的です
::down-arrown ::down-arrowは::down-buttonに表示され、subcontrol-originは::down-buttonからの相対的です

共有できるウィジェット

QDateEdit、QTimeEdit、QDateTimeEditのサブコントロールはQSpinBoxと同じで、QSpinBoxをQDateEdit、QTimeEdit、QDateTimeEditに置き換えるだけで使用できます

QSSの解説


上の画像のテンプレートを基に解説していきます

QSSは「Cascading 連鎖的に伝わる」という風に上流で定義されたものが下流へ引き継がれてウィジェットに適用されます
つまりテンプレートも同じなため上から順を追って解説していきます

QSpinBox#spinBox {
    color: lightgray;
    background: #242424;
    height: 25px;
}

fontの色、背景の色、高さの指定を行っています
これはQSpinBoxとそのサブクラスのインスタンスにマッチします

QSpinBox#spinBox {
/*略*/
}
QSpinBox::down-button#spinBox,
QSpinBox::up-button#spinBox {
    subcontrol-origin: border;
    background: #40dcda;
    width: 20px;
    height: 25px;
}

::down-buttonと::up-buttonに対してQSSの設定を行っています
背景食と横幅、高さの指定にsubcontrol-originをborderに指定しています
つまりborderの領域に::down-buttonと::up-buttonを描画することを指定しています

この設定ですとheightが::down-buttonと::up-buttonともに同じ大きさ且つ、描画の場所を指定していないため::down-buttonと::up-buttonが重なって表示されています

height: 6pxにすると下の画像のような見た目になります

height: 25pxに指定している理由はSpinBoxの高さと同じにしたいためです

QSpinBox#spinBox {
/*略*/
}
QSpinBox::down-button#spinBox,
QSpinBox::up-button#spinBox {
/*略*/
}
QSpinBox::down-button#spinBox {
    subcontrol-position: center left;
    border-bottom-left-radius: 3px;
    border-top-left-radius: 3px;
}
QSpinBox::up-button#spinBox {
    subcontrol-position: center right;
    border-bottom-right-radius: 3px;
    border-top-right-radius: 3px;
}


上のコードではsubcontrol-positionを指定しています

::down-buttonはcenter left、::up-buttonはcenter rightに配置するようにsubcontrol-positionで指定しています
また外側を丸くしたいため、それぞれのボタンの外側の角を丸くしています
内側はまっすぐしたいため角はそのままにしています

QSpinBox#spinBox {
/*略*/
}
QSpinBox::down-button#spinBox,
QSpinBox::up-button#spinBox {
/*略*/
}
QSpinBox::down-button#spinBox {
/*略*/
}
QSpinBox::up-button#spinBox {
/*略*/
}
QSpinBox::up-button:hover#spinBox,
QSpinBox::down-button:hover#spinBox {
    background: #788787;
}
QSpinBox::up-button:pressed#spinBox,
QSpinBox::down-button:pressed#spinBox {
    background: #40dcda;
}

:hoverで::up-buttonと::down-buttonにダイナミック疑似クラスを設定しています
:hoverの名の通りhoverすると機能するQSSになります
:pressedも同じく名の通りpressedしている間の状態を設定するQSSです

疑似クラスを指定する際に注意するべきことがあります

QSpinBox::up-button:pressed#spinBox,
QSpinBox::down-button:pressed#spinBox {
    background: #40dcda;
}
QSpinBox::up-button:hover#spinBox,
QSpinBox::down-button:hover#spinBox {
    background: #788787;
}


例えば先ほど指定した:hoverと:pressedをひっくり返し:pressedを上流に:hoverを下流に持っていきます
すると:hoverは機能していますがpressedが機能しなくなります
これはどちらのセレクタも同じ個別性を持っているので
ボタンがpressedになっている間にマウスがボタンがとhoverした場合、2番目のルール、つまりhoverが優先されてしまうために起きてしまう現象です

自分もよく間違えることがあるのでもし設定したQSSがうまくいかないときはセレクターの個別性が高いものを確認する必要があります

QSpinBox#spinBox {
/*略*/
}
QSpinBox::down-button#spinBox,
QSpinBox::up-button#spinBox {
/*略*/
}
QSpinBox::down-button#spinBox {
/*略*/
}
QSpinBox::up-button#spinBox {
/*略*/
}
QSpinBox::up-button:hover#spinBox,
QSpinBox::down-button:hover#spinBox {
/*略*/
}
QSpinBox::up-button:pressed#spinBox,
QSpinBox::down-button:pressed#spinBox {
/*略*/
}
QSpinBox::up-arrow#spinBox,
QSpinBox::down-arrow#spinBox {
    subcontrol-origin: content;
    width: 12px;
    height: 12px;
}

QSpinBox::up-arrow#spinBox{
    image: url(":/icons/QSpinBox/plus-fill_hover.png");
}

QSpinBox::down-arrow#spinBox{
    image: url(":/icons/QSpinBox/minus-fill_hover.png");
}

上流ではQSpinBox::up-arrowとQSpinBox::down-arrowの描画領域をどこにするか指定しています
またサイズの指定を行うことで大きな画像でも縮尺して表示できるようにしています
下流ではそれぞれどのimageを使用するかしています
subcontrol-origin: content;

もし上流の指定を行ってない場合は下の画像のような見た目で描画されてしまう場合があります
これは使用している画像によって変わりますがそういったものを回避するために指定するのも一つの手です

以上がSubcontrolの説明となります

皆さんのQSSづくりの役に立てればと思います
また、今後もUnPySideスタイルレシピ【UnPySide QSS recipes】を増やしていく予定ですのでお楽しみに