Tkinterの使い方:フレームウィジェット(Frame)の使い方

フレームウィジェットの作り方の解説ページアイキャッチ

このページでは、Tkinter における「フレーム」ウィジェットの使い方やフレームを利用するメリット、設定方法について説明します。

フレームとは

フレームは、一言でいうと複数のウィジェットをグループ化する「枠」です。

フレームも他のウィジェット同様に、メインウィンドウ上に作成・配置するウィジェットになります。

Frameをメインウィンドウ上に作成・配置する様子

ただし、フレームは作成・配置しただけではあまり意味がなく、他のウィジェットをグループ化することで効果を発揮します。

メインウィンドウ上にウィジェットの作成・配置を行うことができることは皆さんもご存知だと思いますが、tkinter では、”フレーム自体にも” 他のウィジェットの作成・配置を行うことができます。

そして、同じフレーム上に作成・配置されたウィジェットを同じグループのウィジェットとして扱うことができます。つまり、フレームごとにグループが存在することになります。

各種ウィジェットをFrame上に作成・配置する様子

さらに、フレームは名前の通り「枠」で、フレーム上のウィジェットは全てその枠の中に配置されることになります。また、それぞれのグループ間で独立してウィジェットの配置が行われます。それぞれで独立しているので、各グループをアプリの部品として扱いやすいです。

親ウィジェットのFrame内に各種ウィジェットが配置される様子

このフレームを用いるメリットは下記の3つです。

  • レイアウトが楽になる
  • グループ単位で一括で処理を実行することができる
  • アプリの部品化がしやすい

この辺りのメリットについては、フレームウィジェットを利用するメリットで解説させていきたいと思います。

フレームウィジェットの作り方・使い方

フレームウィジェットは下記を実行することで生成することができます。

  • Tkinter の Frame クラスのインスタンスを生成する

フレームウィジェットを生成するスクリプト例は下記のようになります。

フレームウィジェットの作成1
# -*- coding:utf-8 -*-
import tkinter

# メインウィンドウを作成
app = tkinter.Tk()

# メインウィンドウ上にフレームを作成
frame1 = tkinter.Frame(
	app,
	width=300,
	height=200
)

# フレームを配置
frame1.grid(column=0,row=0)

# メインループ
app.mainloop()

実行すると下の図のような画面のアプリが起動します。

フレームを作成したけどフレームが表示されない様子

ってメインウィンドウ上に何も見えませんね…。

フレームとは基本的にはそういうものです。ウィジェットをグループ化するのがメインの機能なので、ユーザーからは見えないようになっています(設定を変更することで見えるようにすることもできます)。

ただ、上記のスクリプトだとフレームの良さやフレームを使う意味が分からないと思いますので、次はその辺りが分かりやすいようなスクリプトを紹介したいと思います。

フレームウィジェットの作成2
# -*- coding:utf-8 -*-
import tkinter

# メインウィンドウを作成
app = tkinter.Tk()
app.geometry("400x350")

# メインウィンドウ上にフレームを作成
frame1 = tkinter.Frame(
	app,
)

frame2 = tkinter.Frame(
	app,
)

# frame1上にウィジェットを作成
canvas = tkinter.Canvas(
	frame1,
	width=300,
	height=300,
	bg="blue"
)
canvas.pack()

button1 = tkinter.Button(
	frame1,
	text="ボタン1",
)
button1.pack()

# frame2上にウィジェットを作成
label = tkinter.Label(
	frame2,
	text="ラベル"
)
label.pack()

button2 = tkinter.Button(
	frame2,
	text="ボタン2"
)
button2.pack()

# フレームを配置
frame1.grid(column=0,row=0)
frame2.grid(column=1,row=0)
# メインループ
app.mainloop()

実行すると下の図のような画面のアプリが起動します。

フレームの作成例2

今回はなんとなーくフレームの効果が確認できるのではないかと思います。

ということで、このスクリプトを用いてフレームの作り方について解説していきたいと思います。

スポンサーリンク

メインウィンドウの作成とメインループ

まずスクリプトの下記部分ではメインウィンドウ作成とメインループの実行を行なっています。

メインウィンドウの作成とメインループ
# メインウィンドウを作成
app = tkinter.Tk()
app.geometry("400x350")

# 〜略〜

# メインループ
app.mainloop()

この辺りは他のウィジェットと同様ですね!ここでは詳しく解説しませんが、メインウィンドウについては下記で、

メインウィンドウ作成解説ページのアイキャッチTkinterの使い方:メインウィンドウを作成する

メインループについては下記で解説していますので詳しく知りたい方はこれらのページを参考にしていただければと思います。

tkinterのmainloopの解説ページのアイキャッチ【Python】tkinterのmainloopについて解説

フレームウィジェットの作成(Frame()

フレームウィジェットを実際に作成しているのは下記になります。tkinter.Frame クラスのインスタンスを生成しており、これによりフレームウィジェットが作成されます。

フレームウィジェットの作成
# メインウィンドウ上にフレームを作成
frame1 = tkinter.Frame(
	app,
)

frame2 = tkinter.Frame(
	app,
)

第1引数に指定するのは、フレームウィジェットの親ウィジェットになります。

今回はメインウィンドウのオブジェクトである app を指定していますが、他のフレームウィジェットを指定するようなことも可能です。

つまりフレームウィジェットは重ねて配置するようなことが可能です。

フレームの中にフレームを作成・配置する様子

また、今回は Frame には第1引数しか指定していませんが、キーワード引数でウィジェットの設定(オプション)を指定することも可能です。

具体的な設定可能なキーワード引数は後述のフレームウィジェットの設定で解説します。

フレーム上へのウィジェットの作成と配置

ここからが “フレームウィジェットならでは” の処理になってきます。

各ウィジェット作成時には、コンストラクタの第1引数に親ウィジェットを指定しますが、この指定が意味するのは、ウィジェットの作成先です。

つまり、ウィジェットは第1引数に指定したウィジェット上に作成・配置されることになります。

コンストラクタの第1引数で指定したウィジェット上にウィジェットが作成・配置される様子

さらに、同じウィジェット上に作成されたウィジェットは同じグループのウィジェットとして扱われます。

親ウィジェットが同じウィジェエットが同じグループのウィジェットとして扱われる様子

で、フレームのメイン機能はこのグループ化なので、フレームはただ単に作成して配置しただけではあまり意味がなく、そのフレーム上にウィジェットを配置し、ウィジェットをグループ化したときに効果を発揮します。

なので、フレームを利用する際には、そのフレーム上に他のウィジェットを作成・配置する処理も合わせて記述する必要があります。

このフレーム上へのウィジェットの作成と配置を行なっているのが下記部分になります。

フレーム上へのウィジェットの作成と配置
# frame1上にウィジェットを作成
canvas = tkinter.Canvas(
	frame1,
	width=300,
	height=300,
	bg="blue"
)
canvas.pack()

button1 = tkinter.Button(
	frame1,
	text="ボタン1",
	command=removeFrame
)
button1.pack()

# frame2上にウィジェットを作成
label = tkinter.Label(
	frame2,
	text="ラベル"
)
label.pack()

button2 = tkinter.Button(
	frame2,
	text="ボタン2"
)
button2.pack()

各フレームに対して下記のようにウィジェットの作成と配置を行なっています。

  • frame1
    • canvasbutton1
  • frame2
    • labelbutton2

ただし、フレーム上にウィジェットを作成して配置したとしても、この段階ではまだ画面には表示されません。

これはフレーム自体の配置をまだ行なっていないからです。

フレーム上に作成・配置されたウィジェットは、そのフレームが配置されて初めて画面に表示されることになります。

ということで次はフレームの配置を行なっていきましょう!

スポンサーリンク

フレームウィジェットの配置

下記ではフレームウィジェットの配置を行なっています。

フレームウィジェットの配置
# フレームを配置
frame1.grid(column=0,row=0)
frame2.grid(column=1,row=0)

上記では、grid メソッドを利用して、2つのフレームを横に並べる形で配置しています。

フレームウィジェットにおいても、他のウィジェット同様に、配置には下記の3つのメソッドを利用することが可能です。

  • pack
  • grid
  • place

これらのメソッドについては下記ページで解説していますので、詳しく知りたい方はコチラを読んでいただければと思います。

ウィジェット配置方法解説ページのアイキャッチTkinterの使い方:ウィジェットの配置(pack・grid・place)

前述の通り、このフレームの配置を行なった時に、フレーム上に作成・配置したウィジェットが画面に表示されることになります(厳密には配置した後に mainloop を実行することで表示される)。

フレーム上に配置されたウィジェットがフレームの配置時に表示されるようになる様子

ちなみに、ウィジェットを配置していないフレームを配置した場合は画面上には見えません(フレームウィジェットの設定で説明する設定により見えるようにすることも可能)。

フレームウィジェットを利用するメリット

フレームウィジェットを利用する目的は「複数のウィジェットをグループ化する」ことです。

では、このグループ化することでどのようなメリットがあるのでしょうか?

前述でも述べましたが、このメリットは下記の3つになります。

  • レイアウトが楽になる
  • グループ単位で一括で処理を実行することができる
  • アプリの部品化がしやすい

レイアウトが楽になる

まずウィジェットのレイアウトが楽になります。特にウィジェットの数が多い場合に便利です。

前述の通り、フレームはメインウィンドウ(や他のフレーム)上に配置可能な枠です。

また、フレームには他のウィジェットを packgridplace で配置することが可能であり、それらのウィジェットはフレーム内に配置されることになります。

フレーム内にウィジェットを配置する様子

フレーム毎に個別の独立した空間が用意され、フレーム上のウィジェットはそれぞれの空間に配置される感じです。

さらに、このフレーム自体も他のウィジェット同様にメインウィンドウ上に packgridplace メソッドを用いて配置することが可能です。

フレーム自体を配置する様子

これらを利用すれば、フレームを利用することで下記のように2段階的にアプリ内のウィジェットのレイアウトを行うことができるようになります。

  1. まずフレームを配置してアプリのレイアウトの大枠を構成する
  2. 各フレームに対してウィジェットを配置してアプリの詳細なレイアウトを構成する

アプリ全体で全てのウィジェットの配置を細かく決めるのは、特にウィジェットの数が多いと大変です。

ですが、フレームを利用すれば、1. でアプリのレイアウトの大枠さえ構成してやれば、あとは 2. でフレーム単位でウィジェットの配置をすれば良いだけになるので、複雑なレイアウトでも簡単に行うことができます。

アプリのレイアウトを2段階的に構成する流れ

また、tkinter には、同じグループのウィジェット(親ウィジェットが同じウィジェット)の中では pack での配置と grid での配置を混在させることができないという決まりがあります。混在させると実行時にエラーになります。

ですが、グループ間では同じアプリ内であっても packgrid を混在させることが可能です。つまり、フレームを複数用意して複数のグループを用意してやれば、同じアプリ内で packgrid を混在させることができます。

フレームを分けることでpackとgridをアプリ内で混在させる様子

この packgrid の混在により、より複雑なレイアウトを place なしで実現することができます。

スポンサーリンク

グループ単位で一括で処理を実行することができる

さらに、フレームウィジェットの作り方・使い方でも少し説明しましたが、フレーム上に配置したウィジェットは、フレームを配置した時に初めて画面に表示されることになります。

これを利用して、同じグループのウィジェット全ての表示・非表示を、フレームの表示・非表示のみで切り替えるようなこともできます。

この利用例が下記のスクリプトになります。

フレームの表示と非表示
# -*- coding:utf-8 -*-
import tkinter

# フレーム2のremove中フラグ
is_removed = False

def removeFrame():
	
	global frame2
	global button2
	global is_removed
	
	if is_removed:
		# remove中であれば再度gridで配置
		frame2.grid()
		is_removed = False
	else:
		# remove中でないならばremove
		frame2.grid_remove()
		is_removed = True

# メインウィンドウを作成
app = tkinter.Tk()
app.geometry("400x350")

# メインウィンドウ上にフレームを作成
frame1 = tkinter.Frame(
	app,
)

frame2 = tkinter.Frame(
	app,
)

# frame1上にウィジェットを作成
canvas = tkinter.Canvas(
	frame1,
	width=300,
	height=300,
	bg="blue"
)
canvas.pack()

button1 = tkinter.Button(
	frame1,
	text="ボタン1",
	command=removeFrame
)
button1.pack()

# frame2上にウィジェットを作成
label = tkinter.Label(
	frame2,
	text="ラベル"
)
label.pack()

button2 = tkinter.Button(
	frame2,
	text="ボタン2"
)
button2.pack()

# フレームを配置
frame1.grid(column=0,row=0)
frame2.grid(column=1,row=0)

# メインループ
app.mainloop()

このスクリプトを実行してボタン1をクリックすると、ラベルとボタン2の表示と非表示が切り替わることが確認できると思います。

フレーム上のウィジェットが一括で表示・非表示が切り替えられる様子

上記スクリプトでは、ボタン1がクリックされた時に removeFrame が実行されるようにしており、その removeFrame では frame2 のみの表示と非表示の切り替えを行なっています(gridgrid_remove を交互に実行)。

つまり、フレームの表示と非表示の切り替えを行うだけで、そのフレーム上の全てのウィジェットの表示と非表示の切り替えを一括して行うことができています。

処理を行う対象のウィジェットが1つだけになるので、プログラミングが楽になります。

こんな感じで、複数のウィジェットに対して一括で処理を行いたいような場合にも、フレームによるグループ化が便利です。

アプリの部品化がしやすい

また、フレームを利用することでアプリの部品化がしやすいというメリットがあります。

例えば下の図のようなフレーム(とフレーム上のウィジェット)があるとしましょう。

アプリの部品化の説明図2

さらに、例えばフレーム上のボタンが下記を行うとしましょう(かなり大雑把に書いてます)。

  • frame1
    • 数字:押された数字を記憶する
    • 演算子:記憶した数字と押された演算子で計算を行う
  • frame2
    • 表示:画像を Canvas に描画する
  • frame3
    • 表示:文字列を Textbox に表示する
  • frame4
    • 開く:ファイルを開く
    • 保存:ファイルに保存する

これらの4つのフレームの組み合わせやメインウィンドウ上への配置位置を変更することで、さまざまなアプリを作成することができます。

例えば frame2frame4 をメインウィンドウ上に作成・配置してやれば、下の図のような画像ビューワーのようなアプリに仕立てるようなことができます。

アプリの部品化の説明図2

また frame3 と frame4 をメインウィンドウ上に作成をメインウィンドウ上に作成・配置してやれば、下の図のようなテキストエディタのようなアプリに仕立てるようなことができます。

アプリの部品化の説明図3

さらに frame3frame1 をメインウィンドウ上に作成・配置してやれば、下の図のような電卓のようなアプリに仕立てるようなことができます。

アプリの部品化の説明図4

こんな感じで、フレーム単位でアプリの一部を作成していくことで、フレーム上のウィジェットのグループを部品として他のアプリに再利用したり、その部品を組み合わせることで新たなアプリを作成したりすることができます。

もちろん再利用したり組み合わせたりする際に各フレームやフレーム上のウィジェットを変更をする必要はあるかもしれませんが、それでもゼロベースでアプリを作成していくよりも効率は向上すると思います。

また、ウィジェット単体もアプリの部品と考えることができますが、複数のウィジェットをフレームでグループ化した状態で部品化できるので、ウィジェット単体よりも再利用時の開発効率が上がります。

特に、グループ単位でクラス化しておけば、あとはそのクラスのオブジェクトをメインウィンドウ上に配置するだけでアプリを作っていくことができるので便利です。クラス化はグループ単位で行いたいので、ウィジェットをグループ化することができる tkinter.Frame を親クラスとしたクラスとして作成する必要があります。

例えばフレームウィジェットの作り方・使い方で紹介したスクリプトを、グループ単位でクラス化したスクリプトは下記のようになります。

# -*- coding:utf-8 -*-
import tkinter

class Frame1(tkinter.Frame):
	def __init__(self, master):
		super().__init__(master)
		
		canvas = tkinter.Canvas(
			self,
			width=300,
			height=300,
			bg="blue"
		)
		canvas.pack()

		button = tkinter.Button(
			self,
			text="ボタン1",
		)
		button.pack()

class Frame2(tkinter.Frame):
	def __init__(self, master):
		super().__init__(master)

		label = tkinter.Label(
			self,
			text="ラベル"
		)
		label.pack()

		button = tkinter.Button(
			self,
			text="ボタン2"
		)
		button.pack()

# メインウィンドウを作成
app = tkinter.Tk()

# 各部品オブジェクトを作成
frame1 = Frame1(app)
frame2 = Frame2(app)

# 部品を配置
frame1.grid(column=0,row=0)
frame2.grid(column=1,row=0)

# メインループ
app.mainloop()

Frame1 が元々のスクリプトにおける frame1 及びそのグループに属するウィジェットをまとめて部品化したクラスになります。Frame2 も同様です。

こんな感じでグループを部品化したクラスを用意していくことで、今後のアプリ開発が楽になります。

フレームウィジェットの設定

最後に、ウィジェットの作成時(tkinter.Frame() 実行時)や config メソッド実行時にキーワード引数で指定するフレームウィジェットの設定について解説していきます。

設定できる全てのキーワードは下記により確認することができます。

設定可能なキーワード
# frameはFrameのインスタンス
print(frame.keys())

ここでは私がよく使うもの・動作を理解しているものをピックアップして説明していきたいと思います。

MEMO

私の下記環境での実行結果をもとに説明していますが、環境によっては動きが異なるかもしれません

実際にご自身の環境で実行結果を確認していただくと、より確実に設定の効果を理解することができると思います

  • OS:macOS Catalina
  • Python:3.8
  • Tkinter:8.6

スポンサーリンク

widthheight

widthheight を指定することでフレームウィジェットの幅と高さを設定することができます。

widthとheight設定の説明図

この widthheight には整数を指定します。単位はピクセルになります。

フレームの widthheight をそれぞれ 300 px と 200 px に設定する例は下記のようになります。

widthとheightの設定例
frame = tkinter.Frame(
	app,
	width=300, # widthの設定
	height=200, # heightの設定
)

ただし、私の環境では、フレーム上のウィジェットの配置に packgrid を利用した場合は widthheight 設定が無視され、配置したウィジェットのサイズによって自動的に再設定されるようでした。

一方で、フレーム上のウィジェットの配置を place で行う場合は、この widthheight を設定しないと上手く配置できませんでした。

また place 実行時には、その width height に収まるように xy を設定する必要があります(外側に配置しても表示されない)。

上記の制限があるので、実質的に同じ Frame 内では packplacegridplace での配置の混在ができないことになります。

環境によって動作が異なるかもしれませんが、私の環境と同じ方(特に MacOSX を利用されている方)は上記の点に気をつけて widthheight を指定していただければと思います。

bg(or background)

bg もしくは backgroud を指定することでフレームの背景色を設定することができます。

bg設定の説明図

これらには色名の文字列やカラーコード等を指定することができます。

フレームに対する bg の設定例は下記のようになります。

bgの設定例
frame = tkinter.Frame(
	app,
	width=200,
	height=200,
	bg="green"
)

メインウィンドウの色と異なる色を設定することで、フレーム自体が目で確認できるようにすることができます。

bd(or borderwidth)

bd もしくは borderwidth を指定することでフレームの枠線の太さを設定することが可能です。

bd設定の説明図

bd もしくは borderwidth には整数を指定します(単位はピクセル)。

フレームに対して bd10 px に設定する例は下記のようになります。

bdの設定例
frame = tkinter.Frame(
	app,
	width=200,
	height=200,
	bd=10, # bdの設定
)

ただ私の環境では枠線の太さを変更しても、そもそも枠線の色が背景の色と同じになっていて違いが確認できませんでした…。

次の relief の設定を行うと bd による枠線の太さの変化が分かりやすいと思います。

スポンサーリンク

relief

relief を指定することでフレームの見た目を設定することができます。

relief設定の説明図

relief に指定可能なパラメータは下記の6つになります。

  • tkinter.RAISED
  • tkinter.SUNKEN
  • tkinter.FLAT
  • tkinter.RIDGE
  • tkinter.GROOVE
  • tkinter.SOLID

フレームに対する relief の設定例は下記のようになります。

reliefの設定例
frame = tkinter.Frame(
	app,
	width=100,
	height=100,
	bd=20,
	bd="gray",
	relief=tkinter.RAISED, # reliefの設定
)

relief の指定によって、フレームの見た目は下の図のように変わります。

reliefの指定によるフレームの見た目の違い

reliefは枠線の描画方法を変更することでフレームの見た目を変更するものですので、枠線がないような場合は relief を指定しても見た目が変わらないので注意してください。

padxpady

padxpadyの指定により、フレーム内の余白サイズを設定することができます。

  • padx:左右の余白量(ピクセル単位)
  • pady:上下の余白量(ピクセル単位)

padxとpady設定の説明図

padxpady の設定例は下記のようになります。

padxとpadyの設定例
frame = tkinter.Frame(
	app,
	bd=10,
	relief=tkinter.RAISED,
	padx=100, # padxの設定
	pady=30, # padyの設定
)

この frame にボタンウィジェットを pack で配置した場合のフレームの見た目は下の図のようになります。ボタンとフレームの枠線の間に左右上下方向に余白があることが確認できると思います。

padxとpadyの設定例

ちなみに padxpady を設定しなかった場合は下の図のようになります。

padxとpadyを設定しなかった時の例

この2つの違いからも padxpady の効果が確認できると思います。

cursor

cursor を指定することでフレーム上にマウスカーソルが入ったときのカーソルの見た目(アイコン)を設定することができます。

cursor設定の説明図

例えば cursor には下記のような値を設定することが可能です。

  • "hand"
  • "ibeam"
  • "wait"
  • "poof"

OS 毎に指定可能な値が異なる可能性が高いので注意してください。

フレームに対する cursor の設定例は下記のようになります。

cursorの設定例
frame = tkinter.Frame(
	app,
	width=200,
	height=200,
	relief=tkinter.RAISED,
	bd=10,
	cursor="hand" # cursorの設定
)

これにより、フレーム上ではマウスカーソルが手の形に変化します。

cursorの設定例

スポンサーリンク

takefocus

takefocus の指定により、タブキーによるフォーカスの有効無効を設定することができます。

ただ、フレームに対してフォーカスをあてる効果は正直よく分かりません…。

例えばスクロールバーであれば、フォーカスをあてれば方向キーでスライダーを移動させることができたりするのですが…

とりあえず解説を進めると、このフォーカスをあてられるかどうかを、takefocus に下記のように指定すること設定することができます。

  • takefocus=0:フォーカスがあてられない
  • takefocus=1:フォーカスがあてられる

デフォルト設定は takefocus=0 になります。

takefocusの設定例
frame = tkinter.Frame(
	app,
	width=200,
	height=200,
	takefocus=1, # takefocusの設定
)

highlightcolorhighlightbackgroundhighlightthickness

highlightcolorhighlightbackgroundhighlightthickness の指定により、フレームにフォーカスがあてられた時&フォーカスが外れた時のフレームの囲い線の設定が可能です。

  • highlightcolor:フォーカスがあてられた時の囲い線の色
  • highlightbackground:フォーカスが外れた時の囲い線の色
  • highlightthickness:囲い線の太さ(px)

下記はこれらのキーワードを指定するスクリプトの例になります。

highlight関連の設定例
frame = tkinter.Frame(
	app,
	width=200,
	height=200,
	highlightthickness=10, # highlightthicknessの設定
	highlightbackground="blue", # highlightbackgroundの設定
	highlightcolor="red", # highlightcolorの設定
	takefocus=1
)

上記のフレームを配置したスクリプトを実行すると下の図のようなアプリが起動します。

highlight関連の設定例1

青い線がフォーカスに対するフレームの囲い線になります。この囲い線の太さが、highlightthickness で指定している 10 px になります。

また、現状このフレームにフォーカスが当たっていないので、この囲い線の色は highlightbackground で指定している "blue" になります。

この状態でタブキーを押してフレームにフォーカスをあてると、今度はこの囲い線の色が highlightcolor で指定している "red" に変化します。

highlight関連の設定例2

まとめ

このページでは、tkinter でのフレームの作り方・使い方について解説し、その後フレームを利用するメリットとフレームウィジェットの設定について解説しました。

フレームを利用することで最初に感じるメリットはウィジェットの配置です。フレームを利用することで簡単に複雑なレイアウトのアプリを実現することができるようになります。

また、フレームを利用することでアプリを部品単位で扱いやすくなります。これによりアプリの開発効率を向上することができます。

ソフトウェアの部品化は、tkinter だけでなくさまざまな分野のソフトウェア開発に用いられる考え方なので、フレームの使い方だけでなく、このソフトウェアの部品化の考え方についても、この機会にしっかり理解しておきましょう!

オススメ参考書

Tkinter に興味がある方には下記のPythonでつくる ゲーム開発 入門講座がオススメです。

Tkinter をゲーム開発を通して「楽しく学ぶ」ことができます。Python 入門者、Tkinter 入門者の方にオススメです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です