2016年5月16日月曜日

QWidget の選択時のデザインを CSS で変える方法

ひさびさに書きやすいネタができたので、1ヶ月ぶりくらいに書きます。

ここ1ヶ月の作業内容


タイトルのとおりなのですが、
テーマがいきなりマニアックでとまどったり、
デスチェリー農場の続編はどうしたんだよおおおお!
とか新作を楽しみにしている生命が宇宙のどこかにいるかもしれないので
あらかじめ書いておくと、

ここ1ヶ月は日記ソフトを作ろうと
python + pyside + sqlite の勉強をしていました。

勉強というか、ウィンドウ起動から初めて、
GUI をせっせと作ったり、ファイル保存と復元を作ったりしてます。
Copy & Paste や Redo/Undo など未実装の処理も多くあります。

今回は、タブ遷移とかクリックで選択されたウィジェットの、
背景色が変わるとか、
枠線が出るとか
そんな設定を CSS で実現します。

PySide と CSS について

PySide は前も Raspberry Pi 2 で使う mount コマンドの GUI ツールで使いました。
Python の GUI フレームワークです。

さすがに HSP ほど簡単にウィンドウを出すことはできませんが、
かなり楽だし、 GUI の種類や機能もかなり充実しています。

ある程度 GUI を作っていて、
こんな露骨なウィンドウだと受けが悪いんじゃないかな
と思いました。
なんか今っぽいおしゃれなデザインにしたい。
そんな誘惑にかられて調べていると、 CSS でお手軽にデザインを変更できることがわかりました。

QWidget 派生のオブジェクトを作る際に、 setStyleSheet(str_css)で css を読み込ませることで簡単に実現できます。
次の例では file.css というファイルに書いた css を QWidget 派生クラスのオブジェクトに読み込ませます。

# css の読み込み
with open("file.css","r") as f:
    self.setStyleSheet("".join(f.readlines()))


いろいろな GUI の部品ごとに css の書き方の例もありました。

Qt Style Sheets Examples | Qt 4.8


QComboBox も css に次のように書くことでおしゃれになりました。
上記の書き方の例の QComboBox のスタイルシートを使っています。



QComboBox {
border: 1px solid gray;
border-radius: 3px;
padding: 1px 18px 1px 3px;
min-width: 6em;
}

QWidget への CSS の適用


簡単に css でデザインを変えられるのですが、 QWidget にはなぜかすぐには適用されません。
正確には QWidget を派生したカスタムウィジェットクラスです。

QWidget:focus {
border: 3px solid green;
background-color: #EEFFEE;
}
こんな感じの css を読み込ませることで、
選択時にフォーカスした QWidget のデザインが変わります。
フォーカスイベントで色を自分で塗るような処理を書かなくても css で設定することでできるのはとても楽だし、変更も容易で魅力的です。

ただし、すぐにはこれを反映してくれません。
2つの手続きが必要でした。

手続き1 選択されるようにする。

QComboBoxなどは配置するだけで、選択ができます。タブストップもします。
しかし、その土台となる QWidget は選択できないようになっています。
まずはこの設定を setFocusPolicy() で変更します。

フォーカスポリシーは、タブ選択(TabFocus)、クリック選択(ClickFocus)、両方の選択(StrongFocus)、フォーカスしない(NoFocus) から選びます。

QWidget 派生クラスの __init__ やメンバ関数で次のように呼び出します。

class InputWidget(QtGui.QWidget) :
    def __init__(self):
QtGui.QWidget.__init__(self)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)

手続き2 描画処理で css に対応する。

wiki になにやらそれらしいコードがあります。


あいにく C++ だったので、 pyside 用に書き換えて実行するとうまくいきました。

QWidget 派生のメンバ関数として、次の関数をオーバーライドします。

class InputWidget(QtGui.QWidget) :
def paintEvent(self, qPaintEvent):
   # css のウィジェットの背景色や focus の設定を反映するために必要なオーバーライド
   painter = QtGui.QPainter(self)
   option = QtGui.QStyleOption()
   option.initFrom(self)
   self.style().drawPrimitive(QtGui.QStyle.PE_Widget, option, painter, self)

   return

結果



クリックすることで、緑色の枠で囲んで背景色も変更できました。
少しコードを追加する必要はありますが、
 css で外部からいつでもデザインが変えられるこの機能は強力だと思います。

あとがき

1ヶ月ぶりにブログを書いて、なぜかほっとしています。
別にノルマがあるわけじゃないのに何なんだろう。。。

引き続きしばらくは日記ソフトを作ってみようと思います。
結果の図でちょろっと見せたのがそのソフトです。
まだ、かなり作りかけですが、日記だけどGUIをばりばり使って簡単に入力ができるようにしようと思っています。(`・ω・´)

完成するかどうかもわからないので、ぽしゃってしまうかもしれません。
だけど、この PySide + CSS の強力なデザインのための Tips が書けただけでも成果はあったと思います。

これが一段落したら、またゲーム製作もやってみたいです。



0 件のコメント:

コメントを投稿