kivyのButtonの色を変更する方法あれこれ
Pythonのマルチプラットフォーム向けGUIライブラリkivyで
ボタンButton
の色を変更しようとすると、なかなか大変なので色々試してみたメモ。
これが通常の方法。
プロパティbackground_normal
、background_down
、background_disabled_normal
、background_disabled_down
に
それぞれに表示する画像ファイルを指定する。
単色で表示するなら1✕1pixelの画像ファイルでかまわない。
ソースはこんな感じ。
別途画像ファイルlightgray.png'
、red.png
、gray.png
をカレントディレクトリに用意しておく。
ソースは↓の「開く」をクリックすると表示されます。
if __name__ == '__main__':
from kivy.app import App
from kivy.uix.button import Button
from kivy.lang import Builder
# GUIlレイアウト
Layout = Builder.load_string(
'''
# この中はインデントに意味があるので余計なインデントを入れてはいけない
BoxLayout:
orientation:'horizontal'
BoxLayout:
orientation : 'vertical'
size_hint : (0.3, 0.1)
Button:
id : button_test
text : "TEST"
size_hint : (0.2, 0.1)
pos_hint : {'center_x': 0.5, 'center_y': 0.5}
on_press : app.on_test()
background_normal: 'lightgray.png'
background_down: 'red.png'
background_disabled_normal: 'gray.png'
background_disabled_down: 'gray.png'
Button:
id : button_ctrl
text : "Ena/Dis"
size_hint : (0.2, 0.1)
pos_hint : {'center_x': 0.5, 'center_y': 0.5}
on_press : app.on_ctrl()
BoxLayout:
orientation : 'vertical'
size_hint : (0.3, 0.1)
'''
)
class MyApp(App):
def __init__(self):
super().__init__()
self.button_test = Layout.ids.button_test
self.button_ctrl = Layout.ids.button_ctrl
def build(self):
return Layout
def on_test(self) :
print('pressed')
def on_ctrl(self) :
self.button_test.disabled = not self.button_test.disabled
MyApp().run()
実行すると、Buttonが2つ表示され、TESTボタンが指定されたファイルのイメージで表示される。
Ena/DisボタンをクリックするとTESTボタンのDisable/Enableが切り替えらる。
画像ファイルを使用すると、使用する色の分だけ画像ファイルを用意し、処理を流用する度に
忘れずにすべてのファイルをコピーしないといけない。
そこで、画像ファイルをbase64エンコードした文字列をpyファイル(またはkvファイル)に保存する方法を試してみる。
まず、上で用意したpngファイルを以下のコマンドでbase64エンコード(前に特定の文字列を付加)する。
echo "data:image/png;base64,`base64 -w 0 ≪pngファイル≫`"
付加されているdata:image/png;base64,
は続くデータがpngイメージであることを示している。
出力された文字列を以下のようにファイル名の代わりに記載する。
background_normal : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGHaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49J++7vycgaWQ9J1c1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCc/Pg0KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyI+PHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj48cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0idXVpZDpmYWY1YmRkNS1iYTNkLTExZGEtYWQzMS1kMzNkNzUxODJmMWIiIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj48dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPjwvcmRmOkRlc2NyaXB0aW9uPjwvcmRmOlJERj48L3g6eG1wbWV0YT4NCjw/eHBhY2tldCBlbmQ9J3cnPz4slJgLAAAADUlEQVQYV2M4fPjwfwAH3wNJzT7giwAAAABJRU5ErkJggg=='
background_down : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAANSURBVBhXY3gro/IfAAVUAi3GPZKdAAAAAElFTkSuQmCC'
background_disabled_normal : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGHaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49J++7vycgaWQ9J1c1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCc/Pg0KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyI+PHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj48cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0idXVpZDpmYWY1YmRkNS1iYTNkLTExZGEtYWQzMS1kMzNkNzUxODJmMWIiIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj48dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPjwvcmRmOkRlc2NyaXB0aW9uPjwvcmRmOlJERj48L3g6eG1wbWV0YT4NCjw/eHBhY2tldCBlbmQ9J3cnPz4slJgLAAAADUlEQVQYV2Oor6//DwAFewJ9z0FqqgAAAABJRU5ErkJggg=='
background_disabled_down : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGHaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49J++7vycgaWQ9J1c1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCc/Pg0KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyI+PHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj48cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0idXVpZDpmYWY1YmRkNS1iYTNkLTExZGEtYWQzMS1kMzNkNzUxODJmMWIiIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj48dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPjwvcmRmOkRlc2NyaXB0aW9uPjwvcmRmOlJERj48L3g6eG1wbWV0YT4NCjw/eHBhY2tldCBlbmQ9J3cnPz4slJgLAAAADUlEQVQYV2Oor6//DwAFewJ9z0FqqgAAAABJRU5ErkJggg=='
これにより、pngファイルを削除しても動作するようになる。
ただし、pyファイル(またはkvファイル)が大きくなってしまうことと、一目で指定されている色が把握できないというデメリットがある。
画像ファイルを用意すること自体面倒なので、RGBA値で指定する方法はないかと考えてみる。
Label
と同様にcanvas.before
で背景色を指定してみることを試してみたが、うまくいかなかった。
Button:
・・・
canvas.before:
Color:
rgba : (1,0,0,1)
Rectangle:
pos : self.pos
size : self.size
これはButton
がcanvas.before
をサポートしていないわけではなく、
ちゃんと指定通りに描画しているが、
その前面にButton
のBackground_XXXが表示されているためらしい。
それならば、Buttonの表示を透明にしてやればcanvas.before
の表示が見えるはずである。
以下のように変更して試してみる。
background_color : (0, 0, 0, 0)
が透明色にしている部分で、
background_XXX
をヌル文字列にしているのはすべてのピクセルを(255,255,255,255)にするためであるが、
透明になるのであまり関係ないかもしれない。
Button:
・・・
background_normal : ''
background_down : ''
background_disabled_normal : ''
background_disabled_down : ''
background_color : (0, 0, 0, 0) # 透明
canvas.before:
Color:
rgba : (1,0,0,1)
Rectangle:
pos : self.pos
size : self.size
これでボタンが赤色になった。
しかし、これでばボタンを押下した時やDisableにした時に色が変化しない。
そこで、さらに以下のようにして押下した時やDisableにした時に色が変わるようにしてみた。
canvas.before.Color.rgba
を self.disabled
とself.state
によって変更している。
下にGUIリソース定義部分全体を記載しておく。
# 分かりやすいようにグローバル変数で定義しておく
#:set BG_COLOR_NORMAL (0.75, 0.75, 0.75, 1.0)
#:set BG_COLOR_DOWN (1.00, 0.00, 0.00, 1.0)
#:set BG_COLOR_DISABLED (0.50, 0.50, 0.50, 1.0)
BoxLayout:
orientation:'horizontal'
BoxLayout:
orientation : 'vertical'
size_hint : (0.3, 0.1)
Button:
id : button_test
text : "TEST"
size_hint : (0.2, 0.1)
pos_hint : {'center_x': 0.5, 'center_y': 0.5}
on_press : app.on_test()
background_normal : ''
background_down : ''
background_disabled_normal : ''
background_disabled_down : ''
background_color : (0, 0, 0, 0) # 透明
canvas.before:
Color:
rgba : BG_COLOR_DISABLED if self.disabled else BG_COLOR_NORMAL if self.state == 'normal' else BG_COLOR_DOWN
Rectangle:
pos : self.pos
size : self.size
Button:
id : button_ctrl
text : "Ena/Dis"
size_hint : (0.2, 0.1)
pos_hint : {'center_x': 0.5, 'center_y': 0.5}
on_press : app.on_ctrl()
BoxLayout:
orientation : 'vertical'
size_hint : (0.3, 0.1)
上でとりあえず目的は達成されたが、ボタンを配置する度に上記のような設定を書くのは面倒なので、 カスタムボタンウィジェットを作成してみる。
ソースは↓の「開く」をクリックすると表示されます。
ダウンロードしたいときは
こちら
からどうぞ。
上の「canvas.befor で指定する」の方法をkv言語を使用せずpythonで定義している。
それぞれの色を自由に変更できるようにプロパティを用意した。
bg_color_normal
通常時の色bg_color_down
押下時の色bg_color_disabled
無効時の色また、そのままだとボタンを並べて表示したときに境界が分からなくなるので、枠線を描画するため、以下のプロパティを用意した。
デフォルトは黒で幅1。
border_color
枠線の色border_width
枠線の幅初期化時にボタン本体の背景を透明にし、canvas.before
に描画命令を追加して背景の描画(背景色と枠線)することと、
状態、位置、プロパティが変化したときに描画パラメータを再設定する処理をbindしている。
bindした処理では状態や位置に合わせてcanvas.before
の処理のパラメータを変更している。
枠線を描画するためのLine
はwidth×2の幅で描画されるようなので、本来の矩形のwidthの半分だけ内側に描画されるようにしている。
GUIレイアウトを定義するときは、通常のButtonと同様のプロパティ設定で配置できる。
もちろん、上の通常時の色、押下時の色、無効時の色を変更したい場合は追加で指定すれば良い。