はじめに
Kivyは、PythonのGUIライブラリ。GUI部分をkv言語で、Pythonファイルと切り離して記述可能。本記事は、kivyの使い方を備忘録としてまとめる。
Kivy https://kivy.org/
目次
Kivy概要
KivyはtkinterのようなPythonのGUIライブラリ。pip3 install kivy
でライブラリをインストール可能。GUI部分をpythonファイルから切り離して .kv ファイルとして記述可能。Kivyの記述と起動方法は3種類ある。以降、本記事は「①kvファイル名とClass名を同じにする。」でまとめる。
①kvファイル名とClass名を同じにする。
.kvファイルとpython内に記述するClass名を同じにすることで紐づける(大文字は小文字に変換される)。 hello world の例は以下の通り。.run()
で実行。
from kivy.app import App
#class名称XXXをkvファイル(小文字でOK)にしてファイルをリンクさせる(gui_sample.kv)
class GUI_sample(App):
title = 'GUI sample'
if __name__ == '__main__':
GUI_sample().run()
Label:
text: "Hello World"
②任意のkvファイル名にする。
.kvファイルとpython内に記述するClass名を別名する場合はBuilder.load_file()
を使う。.run()
でKivy実行時にclass内のbuild()
関数が呼ばれる。build()
のreturn値で、該当のファイル名を返信することで、kvファイルとclassが紐づく。
from kivy.app import App
from kivy.lang import Builder
class GUI_sample(App):
def build(self):
self.title = 'GUI sample'
return Builder.load_file('hello_test.kv')
if __name__ == '__main__':
GUI_sample().run()
Label:
text: "Hello World"
③pythonファイル内にkv記述する。
python内にkv記述し、1ファイルにする場合はBuilder.load_string()
を使う。②と同じく.run()
でKivy実行時に、class内のbuild()
関数が呼ばれる。build()
のreturn値でBuilder.load_string()
を返信することでkv文字とclassが紐づく。
from kivy.app import App
from kivy.lang import Builder
#kivy言語で記述
Layout = Builder.load_string('''
Label:
text: "Hello World"
''')
class GUI_sample(App):
def build(self):
self.title = 'GUI sample'
return Layout
if __name__ == '__main__':
GUI_sample().run()
レイアウト
Kivyのレイアウトは、何種類か用意されている。本記事ではBoxLayout
、GridLayout
についてボタンのレイアウトを例にしてまとめる。詳細や、その他のレイアウトは [公式サイト:Getting Started » Layouts] を参照。
BoxLayout
コンテナを垂直vertical:
、水平horizontal:
で指定してレイアウトする方法。BoxLayout:
を1ブロックのコンテナとして記述する。.kvファイル内に記述したコンテナは、Python内に同名のClassを定義することで紐づけられる。本例では、コンテナ名をRoot_Layout
として記述する。
レイアウトは階層化することも可能。BoxLayout:
コンテナの中にBoxLayout:
を記載すれば階層化できる。
👆の実行結果の.py/.kvファイルはこちら。
例内に使用している各コマンドの意味は以下の通り。
-
id:
は、各部品を識別するためのID。 -
background_color:
は、バックグランドの色を指定するコマンド。r(赤), g(緑), b(青) ,a(透明度)を有効値0~1で指定して使う。 -
size_hint_y:
コンテナサイズの垂直方向の割合を有効値0~1で指定して使う。水平方向の場合はsize_hint_x:
で指定。
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout #boxのレイアウト
#.kvファイル内に記述したモジュール名と同じにする。
class Root_Layout(BoxLayout):
pass
#class名称XXXをkvファイル(小文字でOK)にしてファイルをリンクさせる(gui_sample.kv)
class GUI_sample(App):
title = 'GUI sample'
if __name__ == '__main__':
GUI_sample().run()
Root_Layout:
#第一階層
BoxLayout:
orientation:'vertical'
Button:
id:button_R # モジュールを識別するためのid
background_color:(1,0,0,1) #「r(赤), g(緑), b(青) ,a(透明度)」有効値0~1
size_hint_y:0.1 #「y軸のサイズ」有効値0~1
text:'R'
#第二階層
BoxLayout:
orientation:'horizontal'
Button:
id:button_G-1
background_color:(0,1,0,1)
text:'G-1'
Button:
id:button_G-2
background_color:(0,1,0,1)
text:'G-2'
Button:
id:label_G-3
background_color:(0,1,0,1)
text:'G-3'
#第二階層
BoxLayout:
orientation:'vertical'
Button:
id:button_B-1
background_color:(0,0,1,1)
text:'B-1'
Button:
id:button_B-2
background_color:(0,0,1,1)
text:'B-2'
Button:
id:button_B-3
background_color:(0,0,1,1)
text:'B-3'
Button:
id:button_B-4
background_color:(0,0,1,1)
text:'B-4'
Button:
id:button_B-5
background_color:(0,0,1,1)
text:'B-5'
GridLayout
コンテナを行rows:
、列cols:
の数で指定してレイアウトする方法。GridLayout:
を1ブロックのコンテナとして記述する。BoxLayoutと同様にレイアウトの階層化も可能。
👆の実行結果の.py/.kvファイルはこちら。
-
id:
は、各部品を識別するためのID。 -
background_color:
は、バックグランドの色を指定するコマンド。r(赤), g(緑), b(青) ,a(透明度)を有効値0~1で指定して使う。 -
size: root.size
コンテナをルートのサイズに合わせる。これを実施しない場合、全体的に縮小されてしまう。
from kivy.app import App
from kivy.uix.gridlayout import GridLayout #gridlayoutのレイアウト
#.kvファイル内に記述したモジュール名と同じにする。
class Root_Layout(GridLayout):
pass
#class名称XXXをkvファイル(小文字でOK)にしてファイルをリンクさせる(gui_sample.kv)
class GUI_sample(App):
title = 'GUI sample'
if __name__ == '__main__':
GUI_sample().run()
Root_Layout:
#第一階層
GridLayout:
rows: 2 #行
cols: 1 #列
size: root.size #サイズをrootに合わせる
#第二階層
GridLayout:
rows: 1 #行
cols: 1 #列
Button:
id:button_R # モジュールを識別するためのid
background_color:(1,0,0,1) #「r(赤), g(緑), b(青) ,a(透明度)」有効値0~1
text:'R'
#第二階層
GridLayout:
rows: 1 #行
cols: 2 #列
#第三階層
GridLayout:
rows: 2 #行
cols: 2 #列
Button:
id:button_G-1
background_color:(0,1,0,1)
text:'G-1'
Button:
id:button_G-2
background_color:(0,1,0,1)
text:'G-2'
Button:
id:label_G-3
background_color:(0,1,0,1)
text:'G-3'
Button:
id:label_G-4
background_color:(0,1,0,1)
text:'G-4'
#第三階層
GridLayout:
rows: 5 #行
cols: 1 #列
Button:
id:button_B-1
background_color:(0,0,1,1)
text:'B-1'
Button:
id:button_B-2
background_color:(0,0,1,1)
text:'B-2'
Button:
id:button_B-3
background_color:(0,0,1,1)
text:'B-3'
Button:
id:button_B-4
background_color:(0,0,1,1)
text:'B-4'
Button:
id:button_B-5
background_color:(0,0,1,1)
text:'B-5'
ウィジェット
ウィジェットは画面上に表示する部品。ここでは代表的なウィジェットを記述する。
- ラベル_Label
- テキストボックス_TextInput
- ボタン_Button
- チェックボックス_CheckBox
- スライダー_Slider
- スピナー_Spinner
- ファイル選択_FileChooser
- タブ_TabbedPanel
- その他調査中
- 日本語化
- ウィジェットの使用例
ラベル_Label
文字列を表示する。文字サイズは font_size:
。文字の色はcolor:
。で設定可能。
Label:
id:label_A
text: 'Hello World'
font_size:20
color:(1,0,0,1) #「r(赤), g(緑), b(青) ,a(透明度)」有効値0~1
テキストボックス_TextInput
文字列を入力、出力する。文字サイズは、ラベル同様に設定可能。キーボードからの入力文字を直接テキストボックスへ出力するこができる。1行のテキストボックスの場合はmultiline:False
と記載する。また、ボタンなどのイベントの入出力として使用できる。入出力として使用する場合はidを指定する。 text_A.text='XXX'
とすれば、テキストボックスに'XXX'の文字列が出力される。
TextInput:
id:text_A
text: ''
font_size:15
ボタン_Button
クリックでイベントを発生させる。ボタンの背景色はbackground_color:
で設定可能。イベントは押したときon_press:
、離したときon_release:
それぞれで設定可能。.kvファイルに直接イベント発生時の処理を記載することもできるし、Pythonの関数コールにすることもできる。関数コールにする場合は、クラス内にイベントの関数を記述すること。root.関数名
でコールする。
ボタンを押す、離すのタイミングで、テキストボックスに文字列を表示する例を示す。
#直接処理
Button:
id:button_G-1
background_color:(0,1,0,1)
text:'G-1'
on_press:text_A.text=text_A.text+'Push:G-1\n'
on_release:text_A.text=text_A.text+'Release:G-1\n'
#関数処理
Button:
id:button_G-2
background_color:(0,1,0,1)
text:'G-2'
on_press:root.g2_push('G-2')
on_release:root.g2_release('G-2')
class Root_Layout(BoxLayout):
def g2_push(self,indata):
self.ids.text_A.text = self.ids.text_A.text + 'Push' + indata +'\n'
def g2_release(self,indata):
self.ids.text_A.text = self.ids.text_A.text + 'Release'+ indata +'\n'
チェックボックス_CheckBox
チェック状態を確認する。グループ化するとラジオボタンにもなる。active:
0,1でチェック状態を設定する。チェックの状態は、チェックボックスのidを指定して、activeで確認する。グループ化させる場合はgroup:
で同じ文字列を指定する。
CheckBox:
id: Radio_1
group:'radio_box' #グループ化する場合設定。
active:1
CheckBox:
id: Radio_2
group:'radio_box' #グループ化する場合設定。
active:0
CheckBox:
id: Radio_3
group:'radio_box' #グループ化する場合設定。
active:0
Button:
id:button_radio
text:'Radio_Set'
on_press:root.radio()
class Root_Layout(BoxLayout):
def radio(self):
if(self.ids.Radio_1.active==1):
self.ids.text_A.text = self.ids.text_A.text + 'Radio_1=ON\n'
elif(self.ids.Radio_2.active==1):
self.ids.text_A.text = self.ids.text_A.text + 'Radio_2=ON\n'
elif(self.ids.Radio_3.active==1):
self.ids.text_A.text = self.ids.text_A.text + 'Radio_3=ON\n'
スライダー_Slider
スライダーでイベントを発生させる。max: min:
スライダーの範囲を指定する。cursor_width: cursor_height:
でスライダーのつまみのサイズを指定する。イベントは、押したときon_touch_up:
、スライドした時on_touch_move:
それぞれで設定可能。.kvファイルに直接イベント発生時の処理を記載することもできるし、Pythonの関数コールにすることもできる。関数コールにする場合は、Button:
と同様に、クラス内にイベントの関数を記述すること。root.関数名でコールする。スライダーの値は、スライダーのidを指定して、value
で確認する。
ボタンをスライドした時に値を、テキストボックスに文字列を表示する例を示す。
Slider:
id :slider_1
max :100 #カーソルの最大値
min :0 #カーソルの最小値
cursor_width :16 #カーソルのサイズ(幅)
cursor_height :16 #カーソルのサイズ(高)
on_touch_move :root.slider_move() #カーソルを動かしたときの動作
on_touch_up :root.slider_move() #カーソルをタッチした時の動作
class Root_Layout(BoxLayout):
def slider_move(self):
self.ids.slider_text_A.text = str(int(self.ids.slider_1.value))+'%'
スピナー_Spinner
スピナーはドロップダウンのボタンリストのウィジェット。values:
はドロップダウンするボタンのリストを設定する。text:
は初期値を設定する。イベントはButtonと同じくon_press:
が使える。on_text:
はボタンのリストから選択されたときのイベントを設定できる。values
は動的に値を変更できる。また、text
に現在選択されている値が格納される。
Spinner:
id :spinner_1
size_hint_x :0.8 #x軸のサイズ
text:'spinner' # Spinnerの初期値
values: '1','2','3','4' # Spinnerの選択できる値
on_text:root.spinner_select() # Spinnerからアイテムを選択されたとき
on_press:root.spinner_push() # Spinnerを押されたとき
class Root_Layout(BoxLayout):
def spinner_push(self):
#Spnner押されるごとにインクリメントしたボタン追加
self.ids.spinner_1.values.append( f'{len(self.ids.spinner_1.values)+1}' )
def spinner_select(self):
self.ids.slider_text_A.text = self.ids.spinner_1.text
タブ_TabbedPanel
TabbedPanel
は、下図のようにTabボタンの押下で画面を切り替えたいときに使うウィジェット。TabbedPanelItem:
の配下に画面に表示したいウィジェットを列挙する。tab_pos:
はタブボタンの配置位置を決める。デフォルトのタブを指定したくない場合はdo_default_tab:False
とする。
Root_Layout:
TabbedPanel:
tab_pos: 'top_left' #Tab選択ボタンの位置(top_left/top_right/bottom_left/bottom_right)
do_default_tab: False #DefaultのTab有り無し
#Tab_A
TabbedPanelItem:
text:'Tab_A'
Label:
text:'Tab_A Hello!'
#Tab_B
TabbedPanelItem:
text:'Tab_B'
Label:
text:'Tab_B Hello!'
ファイル選択_FileChooser
FileChooserIconLayout
FileChooserListLayout
FileChooserはファイル選択画面を表示する。図のようにアイコン形式FileChooserIconLayout
と、リスト形式FileChooserListLayout
の二種類が準備されている。表示する種類をFileChooser
内に記述する。
.view_mode
できる替えができる。
ファイルファイアログの初期フォルダパスはpath:
で記述する。ファイルをフィルタしたい場合はfilters:
でフィルタ内容を記述する。
現在のフォルダパスは.path
。現在選択されているファイルは.selection
から取得できる。selectionはリスト形式で取得する。ファイルはselection[0]に格納される。ファイルがないフォルダの場合は空のストになるので注意。
FileChooser:
id :FileCh
path :'./' #カレントフォルダ指定
filters :['*.py'] #拡張子が.pyのファイル
FileChooserIconLayout #Icon表示
FileChooserListLayout #List表示
# FileChooser動作確認のためのボタン----------------
Button:
id :Btn_list
text:'List'
on_press:root.btn_list_on()
Button:
id:Btn_icon
text:'Icon'
on_press:root.btn_icon_on()
Button:
id:Btn_select
text:'Select'
# FileCh.path 現在のフォルダパス
# FileCh.selection 現在選択されているファイル
on_press:root.btn_select_on(FileCh.path, FileCh.selection)
TextInput:
id:TextBox
text: ''
class Root_Layout(BoxLayout):
def btn_icon_on(self):
self.ids.FileCh.view_mode = 'icon' #アイコン表示にする。
def btn_list_on(self):
self.ids.FileCh.view_mode = 'list' #リスト表示にする。
def btn_select_on(self,path,selection):
#パスを表示
self.ids.TextBox.text = self.ids.TextBox.text + '[PATH]'+path+'\n'
#現在選択しているファイルをリスト形式で取得する。ファイルはselection[0]に格納
#ファイルがないと空のリストにアクセスしてエラーになるでlen()で確認してから処理。
if len(selection):
self.ids.TextBox.text = self.ids.TextBox.text + '[FILE]'+ selection[0] + '\n'
👆の実行結果の.py/.kvファイルはこちら。
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout #boxのレイアウト
#.kvファイル内に記述したモジュール名と同じにする。
class Root_Layout(BoxLayout):
def btn_icon_on(self):
self.ids.FileCh.view_mode = 'icon' #icon view
def btn_list_on(self):
self.ids.FileCh.view_mode = 'list' #list view
def btn_select_on(self,path,selection):
self.ids.TextBox.text = self.ids.TextBox.text + '[PATH]'+path+'\n'
#現在選択しているファイルをリスト形式で取得する。ファイルはselection[0]に格納
#ファイルがないと空のリストにアクセスしてエラーになるでlen()で確認してから処理。
if len(selection):
self.ids.TextBox.text = self.ids.TextBox.text + '[FILE]'+ selection[0] + '\n'
#class名称XXXをkvファイル(小文字でOK)にしてファイルをリンクさせる(gui_sample.kv)
class GUI_sample(App):
title = 'GUI sample'
if __name__ == '__main__':
GUI_sample().run()
Root_Layout:
#第一階層
BoxLayout:
orientation:'vertical'
#背景設定
canvas:
Color:
rgba: 0.3,0.3,0.3,1
Rectangle:
size: self.size
pos: self.pos
orientation:'vertical'
#Listボタン
Button:
id:Btn_list
text:'List'
font_size:20
size_hint_y:0.1
on_press:root.btn_list_on()
#Iconボタン
Button:
id:Btn_icon
text:'Icon'
font_size:20
size_hint_y:0.1
on_press:root.btn_icon_on()
#Selectボタン
Button:
id:Btn_select
text:'Select'
font_size:20
size_hint_y:0.1
on_press:root.btn_select_on(FileCh.path,FileCh.selection)
#Text
TextInput:
id:TextBox
text: ''
font_size:20
size_hint_y:1
#ファイルダイアログ
FileChooser:
id:FileCh
path:'./'
filters:['*.py']
FileChooserIconLayout
FileChooserListLayout
日本語化
Kivyで日本語使うと文字化けする。下記に日本語化するライブラリが公開されていました。便利。
https://github.com/momijiame/japanize-kivy
ウィジェットの使用例
👆の実行結果の.py/.kvファイルはこちら。
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout #boxのレイアウト
import japanize_kivy # https://github.com/momijiame/japanize-kivy 日本語化
#.kvファイル内に記述したモジュール名と同じにする。
class Root_Layout(BoxLayout):
def g2_push(self,indata):
self.ids.text_A.text = self.ids.text_A.text + 'Push' + indata +'\n'
def g2_release(self,indata):
self.ids.text_A.text = self.ids.text_A.text + 'Release'+ indata +'\n'
def reset(self):
self.ids.text_A.text = ''
def radio(self):
if(self.ids.Radio_1.active==1):
self.ids.text_A.text = self.ids.text_A.text + 'Radio_1=ON\n'
elif(self.ids.Radio_2.active==1):
self.ids.text_A.text = self.ids.text_A.text + 'Radio_2=ON\n'
elif(self.ids.Radio_3.active==1):
self.ids.text_A.text = self.ids.text_A.text + 'Radio_3=ON\n'
def slider_move(self):
self.ids.slider_text.text = str(int(self.ids.slider_1.value))+'%'
def spinner_push(self):
#Spnner押されるごとにインクリメントしたボタン追加
self.ids.spinner_1.values.append( f'{len(self.ids.spinner_1.values)+1}' )
def spinner_select(self):
self.ids.spinner_text.text = self.ids.spinner_1.text
def btn_icon_on(self):
self.ids.FileCh.view_mode = 'icon' #icon view
def btn_list_on(self):
self.ids.FileCh.view_mode = 'list' #list view
def btn_select_on(self,path,selection):
self.ids.TextBox.text = self.ids.TextBox.text + '[PATH]'+path+'\n'
#現在選択しているファイルをリスト形式で取得する。ファイルはselection[0]に格納
#ファイルがないと空のリストにアクセスしてエラーになるでlen()で確認してから処理。
if len(selection):
self.ids.TextBox.text = self.ids.TextBox.text + '[FILE]'+ selection[0] + '\n'
#class名称XXXをkvファイル(小文字でOK)にしてファイルをリンクさせる(gui_sample.kv)
class GUI_sample(App):
title = 'GUI sample'
if __name__ == '__main__':
GUI_sample().run()
Root_Layout:
#第一階層
TabbedPanel:
tab_pos: 'bottom_left'
do_default_tab: False
#Tab_A=====================================
TabbedPanelItem:
text:'Tab_A'
background_color:(1,0,0,1)
#第二階層
BoxLayout:
orientation:'vertical'
#背景設定
canvas:
Color:
rgba: 0.3,0.3,0.3,1
Rectangle:
size: self.size
pos: self.pos
Label:
id:label_A
text: 'Hello World'
font_size:20
color:(1,0,0,1) #「r(赤), g(緑), b(青) ,a(透明度)」有効値0~1
size_hint_y:0.2 #y軸のサイズ
TextInput:
id:text_A
text: ''
font_size:15
size_hint_y:0.8 #y軸のサイズ
#第三階層(G-1/G-2/G-3)
BoxLayout:
orientation:'horizontal'
size_hint_y:0.2 #y軸のサイズ
#直接処理
Button:
id:button_G-1
background_color:(0,1,0,1)
text:'G-1'
on_press:text_A.text=text_A.text+'Push:G-1\n'
on_release:text_A.text=text_A.text+'Release:G-1\n'
#関数処理
Button:
id:button_G-2
background_color:(0,1,0,1)
text:'G-2'
on_press:root.g2_push('G-2')
on_release:root.g2_release('G-2')
#関数処理
Button:
id:button_reset
background_color:(0,1,0,1)
text:'Reset'
on_press:root.reset()
#第三階層(RadioBox/Radio_Set)
BoxLayout:
orientation:'horizontal'
size_hint_y:0.2 #y軸のサイズ
#第四階層(RadioBox)
BoxLayout:
orientation:'vertical'
BoxLayout:
orientation:'horizontal'
CheckBox:
id: Radio_1
group:'radio_box'
active:1
size_hint_x:0.2 #x軸のサイズ
Label:
text: 'radio_1'
BoxLayout:
orientation:'horizontal'
CheckBox:
id: Radio_2
group:'radio_box'
active:0
size_hint_x:0.2 #x軸のサイズ
Label:
text: 'radio_2'
BoxLayout:
orientation:'horizontal'
CheckBox:
id: Radio_3
group:'radio_box'
active:0
size_hint_x:0.2 #x軸のサイズ
Label:
text: 'radio_3'
#第四階層(Radio_Set)
Button:
id:button_radio
background_color:(0,0,1,1)
text:'Radio_Set'
on_press:root.radio()
#第三階層(Slider)
BoxLayout:
orientation:'horizontal'
size_hint_y:0.1 #y軸のサイズ
#第四階層(Slider)
Slider:
id :slider_1
size_hint_x :0.8 #x軸のサイズ
max :100 #カーソルの最大値
min :0 #カーソルの最小値
cursor_width :16 #カーソルのサイズ(幅)
cursor_height :16 #カーソルのサイズ(高)
on_touch_move :root.slider_move() #カーソルを動かしたときの動作
on_touch_up :root.slider_move() #カーソルをタッチした時の動作
#第四階層(Slider_text)
TextInput:
id:slider_text
size_hint_x:0.2 #x軸のサイズ
text: ''
font_size:15
#第三階層(Spinner)
BoxLayout:
orientation:'horizontal'
size_hint_y:0.1 #y軸のサイズ
#第四階層(Spinner)
Spinner:
id :spinner_1
size_hint_x :0.8 #x軸のサイズ
text:'spinner' # Spinnerの初期値
values: '1','2','3','4' # Spinnerの選択できる値
on_text:root.spinner_select() # Spinnerからアイテムを選択されたとき
on_press:root.spinner_push() # Spinnerを押されたとき
#第四階層(Spinner_text)
TextInput:
id:spinner_text
size_hint_x:0.2 #x軸のサイズ
text: ''
font_size:15
#Tab_B=====================================
TabbedPanelItem:
text:'Tab_B'
background_color:(1,0,0,1)
#第二階層
BoxLayout:
orientation:'vertical'
#背景設定
canvas:
Color:
rgba: 0.3,0.3,0.3,1
Rectangle:
size: self.size
pos: self.pos
orientation:'vertical'
#Listボタン
Button:
id:Btn_list
text:'List'
font_size:20
size_hint_y:0.1
on_press:root.btn_list_on()
#Iconボタン
Button:
id:Btn_icon
text:'Icon'
font_size:20
size_hint_y:0.1
on_press:root.btn_icon_on()
#Selectボタン
Button:
id:Btn_select
text:'Select'
font_size:20
size_hint_y:0.1
on_press:root.btn_select_on(FileCh.path,FileCh.selection)
#Text
TextInput:
id:TextBox
text: ''
font_size:20
size_hint_y:1
#ファイルダイアログ
FileChooser:
id:FileCh
path:'./'
filters:['*.py']
FileChooserIconLayout
FileChooserListLayout
その他いろいろ作ってみた例
その他作成したら追加