LoginSignup
6
12

More than 1 year has passed since last update.

Tkinter CookBook

Last updated at Posted at 2018-10-13

Tkinter CookBook1

前提

1. 実行環境

本ページで記載しているプログラムコードはWindows10, python3.6で実行できることを確認しています。

2. 必要なpythonモジュール

下記のモジュールを別途インストールしておく必要があります。

  • numpy
  • matplotlib

3. 本投稿の目的

CookBook第一弾として家計簿を作成しながら、Tkinterについて説明をします。

4. 進捗状況

step1 投稿中
step2 投稿中
step3 投稿中
step4 投稿中
step5 投稿中 githubで公開中
step6 投稿中 githubで公開中
step7 ...
...

それでは、はじめましょう。

step1

step1.py
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk
import datetime

##############################################################################
#フレーム
class Use_Frame(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
##############################################################################
#ラベル
class Use_Label(tk.Label):
    def __init__(self, master=None):
        tk.Label.__init__(self, master)
###############################################################################
#エントリー
class Use_Entry(tk.Entry):
    def __init__(self, master=None):
        tk.Entry.__init__(self, master)
##############################################################################

root = tk.Tk()
root.title("家計簿")

#****************************************************************************#
#日付の取得
day = datetime.datetime.now()
day = day.strftime("%Y/%m/%d")

#****************************************************************************#
#Frame_date(日付の入力)
#ラベル、エントリーの作成
Frame_date = Use_Frame(root)
Frame_date.grid(column=0, row=0)

Label_date_1 = Use_Label(Frame_date)
Label_date_1.grid(column=0, row=0)
Label_date_1["text"] = "日付の入力"

Entry_date = Use_Entry(Frame_date)
Entry_date.grid(column=1, row=0)
Entry_date.insert(0, day)
#****************************************************************************#
root.mainloop()

上記のプログラムを実行すると、下のような画面が表示されます。

step1.png

このプログラムが行っていることは

  1. "画面"を作る
  2. "日付の入力"という文字を画面に乗せる
  3. "Entry"というユーザーが文字を入力できる部品を画面に乗せる
  4. "Entry"にあらかじめ現在の年月日を入れておく

です。

1. 画面の作成

画面の作成
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk

root = tk.Tk()
root.title("家計簿")

root.mainloop()

まず、importしているモジュールですが、TkinterはGuiを作成するために使用されるものです。
Pythonをインストールした際に、一緒にインストールされるため非常に使いやすく、簡単にGui作成を
作成することが出来ます。

1行目の 「from tkinter import *」 はtkinterモジュールに含まれているすべてのオブジェクトを「import」する命令です。
一般的に、このような呼び出し方は不適切ですがTkinterを使う場合はよくこのように書きます。

2行目の 「import tkinter as tk」 はコードを書く際に「tkinter」と記述する部分を「tk」と短くすることが出来るために打つ命令です。

3行目の 「import tkinter.ttk as ttk」「tkinter」だけではできないことをするために「ttk」と呼ばれるモジュールを使うために書いておきます。

さて、ここまではプログラム本文とはほとんど関係ありませんでした。

「root = tk.Tk()」という処理が、画面の作成に関わってきます。
「tk.Tk()」「tkinter」モジュールの中にある「Tk()」というメソッドを呼び出します。
このメソッドの中身を知る必要はなく、実行すると画面を作成してくれるということだけ覚えておきましょう。
一般的に、「tk.Tk()」を実行し返ってくるオブジェクトは、「root」という名前の変数に格納されます。

※Pythonを使うのが初めての人に対してあらかじめ述べさせてもらえば、変数名はかなり大きな自由度を持っています。
 ただし、読みやすいコードを書くためにPythonを使っているのであれば、意地を張らずに「root」を使いましょう。

画面にタイトルをつけたい場合は「root.title("文字列")」とします。
これは、「root」の中に画面のオブジェクトが格納されており、画面のオブジェクトが所有しているメソッド「title()」を呼び出しているのです。

最後の「root.mainloop()」も同様の考え方が出来ます。
画面のオブジェクトを想像してみましょう。画面のオブジェクトは実行した直後に処理を終了してしまうでしょうか。
大多数の人は画面のオブジェクトは実行後もモニターに残り、マウスで操作することが出来ると思うのではないでしょうか。
その通りで、画面のオブジェクトには残ってもらわないと困ります。
画面にオブジェクトを残し、ユーザーの入力を受け付けられるようにするのが、最後の命令を書く目的です。

step1_1.png

実行すると上のような画面オブジェクトが出来ます。

2. 日付の入力という文字を画面に乗せる

"日付の入力"を表示
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk

##############################################################################
#フレーム
class Use_Frame(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
##############################################################################
#ラベル
class Use_Label(tk.Label):
    def __init__(self, master=None):
        tk.Label.__init__(self, master)
###############################################################################

root = tk.Tk()
root.title("家計簿")

Frame_date = Use_Frame(root)
Frame_date.grid(column=0, row=0)

Label_date_1 = Use_Label(Frame_date)
Label_date_1.grid(column=0, row=0)
Label_date_1["text"] = "日付の入力"

root.mainloop()

まず、「import」から「root = tk.Tk()」の間に「class」という表記があることに注目しましょう。
この表記はクラスオブジェクトを定義するためのものです。
思い出してみましょう。
「root = tk.Tk()」という命令では画面のオブジェクトが「root」に格納されました。

画面のオブジェクトだけでも、確かに私たちが普段見ているようなGuiなオブジェクトを作ることができました。
一方で、文字を表示したり、ボタンを作ったりするためには別のオブジェクトを作る必要があります。

ここでは文字を表示したいという事なので、「Label」オブジェクトを作成することにします。
そして、このプログラムではラベルを単に画面のオブジェクト(つまり「root」)に乗せるのではなく、「Frame」オブジェクトを間に挟みます。

ここで疑問に思うこととしては、どうしてわざわざクラスオブジェクトを作成しているのか、という点です。
つまり、Tkinterというモジュールは画面のオブジェクトを作成することは出来ても、「Frame」オブジェクトを作成するためのツールは用意していないのか、という事です。

もちろん、そんなことはありません。
「Frame」オブジェクトは「tk.Frame」「Label」オブジェクトは「tk.Label」で作成することが出来ます。

ここで、わざわざ「class Use_Frame」「class Use_Label」を記述し、クラスオブジェクトを定義しているのは後々の利便性を意識しているためです。

定義したクラスオブジェクトはそれぞれ「tk.Frame」「tk.Label」を継承しています。
したがって、定義したクラスオブジェクトは「Frame」オブジェクトや「Label」オブジェクトを作成するための最低限の機能を持っているということです。

コードを見ればわかるように、クラスオブジェクト内にはほとんど記述がありません。
今はこのクラスオブジェクトとTkinterが提供するオブジェクトにほとんど差はありません。
※ほとんどといったのは、一応名前が異なるためです。

さて、「root」に画面のオブジェクトを格納した後に「Frame_date = Use_Frame(root)」という記述をしています。
「Use_Frame」「tk.Frame」を継承しているため、「Frame」オブジェクトを作成することが出来ます。
よく見ると、「Use_Frame(root)」と書かれています。ここに「root」が書かれているのは、このオブジェクトを乗せる土台となるオブジェクトを指定するためです。

よくよく考えてみれば簡単なことであり、「root」は一番下の土台となるオブジェクトですが、「root」を一つだけしか作れないというわけではありません。「root1」「root2」のように複数の画面のオブジェクトを定義することは容易です。
このような時にあたらしく「Frame」オブジェクトを作成した場合、このオブジェクトはどこへ乗せればよいのでしょうか。
もちろん、コンピュータが勝手に考えてくれるわけではなく、私たちが明示的に指定する必要があります。

次に「Frame_date = Use_Frame(root)」配置します。
配置を忘れるという事は、プログラミングの最中によくあることで、気を付けなければいけないポイントです。
先ほど、土台は決めましたがオブジェクトを乗せる土台上の位置も明示的に指定する必要があります。

配置するために使用するのは「Frame_date.grid(column=0, row=0)」です。
「grid()」はtkinterのほとんどのオブジェクトが持つ機能です。

tkinterを使う場合、オブジェクトが持つべき機能を考えることをおすすめします。
「grid()」について言えば、どのようなオブジェクトも画面のオブジェクトという土台に配置される必要があるのだから、配置される機能を持っているべきだと考えるのです。

「grid()」「column」「row」を指定する必要があります。英語の辞書を引けばわかるように「column」「縦の列」「row」「列」です。

- 0 1
0 column = 0, row = 0 column = 1, row = 0
1 column = 0, row = 1 column = 1, row = 1

「column」「row」は二つ以上のオブジェクトがある場合、上図のように機能します。
オブジェクトが一つだけの場合、どのような値を設定しても、ただオブジェクトが配置されるだけになります。
「grid()」はそれぞれのオブジェクトの相対的な位置を、表のように定義するのです。

さあこれで、「Frame」オブジェクトを「root」の上に配置することが出来ました。

次に「Frame」オブジェクトを土台として、「Label」オブジェクトを配置しましょう。
「Label_date_1 = Use_Label(Frame_date)」と記述することで、「Frame_date」オブジェクトを土台として「Label」オブジェクトを作成しているのです。

作成が終わったら「grid()」で配置します。
ここで、先ほどの話を思い出してほしいです。「Label」オブジェクトは一体何のためにあるオブジェクトなのかということです。今回は文字を表示するために使用しているわけですから、文字を表示する機能を持っているわけです。

ここでは機能というよりも装飾という方がいいかもしれません。ある文字が彫られている「Label」オブジェクトというわけです。

装飾するためにはいくつかの方法がありますが、ここではオブジェクトの持つディクショナリへのアクセスを試みます。
「Label_date_1["text"] = "日付の入力"」という記述は、オブジェクトが持つディクショナリのうち、「text」というキーにアクセスしています。このディクショナリはオブジェクトの見た目や大きさを管理しているのです。

たとえばオブジェクトの色を変えたければ"color"、幅を変えたければ"width"といったように細かい設定を行うことが出来ます。"text"は表示する文字を設定しています。

step1_2.png

実行すると、上図のような画面が現れます。

3. Entryオブジェクトの作成

Entryオブジェクトの作成
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk

##############################################################################
#フレーム
class Use_Frame(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
##############################################################################
#ラベル
class Use_Label(tk.Label):
    def __init__(self, master=None):
        tk.Label.__init__(self, master)
###############################################################################
#エントリー
class Use_Entry(tk.Entry):
    def __init__(self, master=None):
        tk.Entry.__init__(self, master)
##############################################################################

root = tk.Tk()
root.title("家計簿")

Frame_date = Use_Frame(root)
Frame_date.grid(column=0, row=0)

Label_date_1 = Use_Label(Frame_date)
Label_date_1.grid(column=0, row=0)
Label_date_1["text"] = "日付の入力"

Entry_date = Use_Entry(Frame_date)
Entry_date.grid(column=1, row=0)

root.mainloop()

「class Use_Entry」「Entry」オブジェクトを再定義しています。

再定義したクラスを用いて「Entry_date = Use_Entry(Frame_date)」「Entry」オブジェクトを作成します。そして、「grid()」で配置します。「column = 1」なので先ほど作成した「Label」の右側にこのオブジェクトは配置されます。

step1_3.png

実行すると上図のようになります。

4. Entryにあらかじめ現在の年月日を入れる

Entryに文字を入力しておく
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk
import datetime

##############################################################################
#フレーム
class Use_Frame(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
##############################################################################
#ラベル
class Use_Label(tk.Label):
    def __init__(self, master=None):
        tk.Label.__init__(self, master)
###############################################################################
#エントリー
class Use_Entry(tk.Entry):
    def __init__(self, master=None):
        tk.Entry.__init__(self, master)
##############################################################################

root = tk.Tk()
root.title("家計簿")

#****************************************************************************#
#日付の取得
day = datetime.datetime.now()
day = day.strftime("%Y/%m/%d")

#****************************************************************************#
#Frame_date(日付の入力)
#ラベル、エントリーの作成
Frame_date = Use_Frame(root)
Frame_date.grid(column=0, row=0)

Label_date_1 = Use_Label(Frame_date)
Label_date_1.grid(column=0, row=0)
Label_date_1["text"] = "日付の入力"

Entry_date = Use_Entry(Frame_date)
Entry_date.grid(column=1, row=0)
Entry_date.insert(0, day)
#****************************************************************************#
root.mainloop()

現在の日付を取得するためには、新しく「datetime」というモジュールをimportする必要があります。
「datetime」モジュールが持つ「datetime.now()」という関数で、現在の日付を得ます。
その後に「day = day.strftime("%Y/%m/%d")」としているのは、「2018/9/10」のような形に整形したいからです。
※ここはtkinterの本質とはあまり関わり合いがありません。

「Entry」オブジェクトにあらかじめ値を入力しておきたい場合、「Entry_date.insert(0, day)」とします。
「0」が入力位置の指定、「day」が入力する文字列です。

step1_4.png

実行すると上図のようになります。

step2

step2.py
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk
import datetime

##############################################################################
#フレーム
class Use_Frame(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
##############################################################################
#ラベル
class Use_Label(tk.Label):
    def __init__(self, master=None):
        tk.Label.__init__(self, master)
###############################################################################
#エントリー
class Use_Entry(tk.Entry):
    def __init__(self, master=None):
        tk.Entry.__init__(self, master)
##############################################################################
#ラジオボタン(step2.pyで追加)
class Use_Radio(tk.Radiobutton):
    def __init__(self, master=None):
        tk.Radiobutton.__init__(self, master)
##############################################################################

root = tk.Tk()
root.title("家計簿")

#****************************************************************************#
#日付の取得
day = datetime.datetime.now()
day = day.strftime("%Y/%m/%d")

#****************************************************************************#
#Frame_date(日付の入力)
#ラベル、エントリーの作成
Frame_date = Use_Frame(root)
Frame_date.grid(column=0, row=0)

Label_date_1 = Use_Label(Frame_date)
Label_date_1.grid(column=0, row=0)
Label_date_1["text"] = "日付の入力"

Entry_date = Use_Entry(Frame_date)
Entry_date.grid(column=1, row=0)
Entry_date.insert(0, day)
#****************************************************************************#
#Frame_item(品目の選択)(step2.pyで追加)
#ラジオボタンの作成
Frame_item = Use_Frame(root)
Frame_item.grid(column=0, row=1)

Label_radio = Use_Label(Frame_item)
Label_radio.grid(column=0, row=0)
Label_radio["text"] = "品目を選択"

Dict_item = {"1": "娯楽費 ", "2": "食費  ", "3": "交通費 ", "4": "日用品費"}

for i in range(len(Dict_item)):
    Radio = Use_Radio(Frame_item)
    Radio.grid(column=0, row=i + 1)
    Radio["text"] = Dict_item[str(i + 1)]
#****************************************************************************#

root.mainloop()

上記のプログラムを実行すると、下のような画面が表示されます。

step2.png

このstep2.pyで追加した記述が行っていることは

  1. 新しく「Frame」オブジェクトを作成
  2. "品目を選択 "という「Label」オブジェクトを作成し、新しい「Frame」オブジェクトの上に配置
  3. "4つのラジオボタン"を作成し、新しい「Frame」オブジェクトの上に配置

です。

1. 新しい「Frame」オブジェクトを作成

新しい「Frame」オブジェクトの作成
#Frame_item(品目の選択)(step2.pyで追加)
#ラジオボタンの作成
Frame_item = Use_Frame(root)
Frame_item.grid(column=0, row=1)

「root」つまり画面のオブジェクト上に直接「Label」オブジェクトや「Entry」オブジェクトを配置しない理由は様々でしょう。このコードでそのようにしない理由は後々の配置換えを容易にするためです。

直接「root」上にオブジェクトを乗せ、「grid()」で配置する方法はオブジェクト数が少なければ非常に有効です。
ただし、オブジェクトの数が多くなると相対的な位置関係は複雑になります。また、あるオブジェクトAをあるオブジェクトBのとなりに配置したくなった場合、オブジェクト数が多いとほとんどすべてのオブジェクトの配置を見直す必要が出て来ます。

これは面倒なので、関連するオブジェクトを「Frame」オブジェクト上にまとめてしまい、「root」の上に乗るオブジェクトは「Frame」オブジェクトのみとするのです。このようにすることで、後々オブジェクトを追加したい場合はそのオブジェクトを配置するのに適切な「Frame」オブジェクトを見つけ、その「Frame」オブジェクト上に乗っているオブジェクトのみ配置を変えればよいのです。

この考え方は私たちが普段から何気なく行っていることなのです。WindowsでもLinuxでもファイルを作成した後は、適切なフォルダへと配置するでしょう。あらゆるファイルを一つのフォルダへ放り込んでいる人は少ないのではないでしょうか。

したがってここでも、ラジオボタンを乗せるために「Frame_item = Use_Frame(root)」で新しい「Frame」オブジェクトを作成します。そして「grid()」によってstep1で作成した「Frame_date」オブジェクトの下に配置します。

step2_1.png

上図は「Frame_item」を作成し、配置したものです。特に画面上の変化は見られません。

2. 「Label」オブジェクトを作成し、新しい「Frame」オブジェクトの上に配置

「Label」オブジェクトを「Frame_item」上に配置
Label_radio = Use_Label(Frame_item)
Label_radio.grid(column=0, row=0)
Label_radio["text"] = "品目を選択"

ここではstep1で行ったのと同様に、「Use_Label」を用いて新しく「Label」オブジェクトを作成しています。ただし、「Frame_item」上に配置します。

ここで注目することは、「grid(column=0, row=0)」です。
「grid()」は各オブジェクトの相対的な位置関係を表しています。この各オブジェクトというのは、土台が同じオブジェクトたちという意味です。

「grid()」の有効範囲は、よく親オブジェクトが同じオブジェクト、という表現をします。「Label_radio」にとって、親は「Frame_item」です。
「Frame_item」にとって、親は「root」です。したがって「root」子オブジェクトである「Frame_date」子オブジェクトのことを気にする必要はないのです。

したがって、「Label_radio」「Frame_item」上のオブジェクトとの相対的な位置に配置されるのです。もちろん「Frame_item」上にオブジェクトはないので、ただ配置されることになります。

step2_2.png

上図は「Frame_item」の上に「Label_radio」を配置したものです。注目するべきなのは"品目を選択"という文字列が画面の中央に配置されている点です。これはつまり、上の"日付の入力"「Entry」オブジェクトの位置にとらわれていないという事です。

3. 「Radio」オブジェクトを作成し、新しい「Frame」オブジェクトの上に配置

「radio」オブジェクトの作成
Dict_item = {"1": "娯楽費 ", "2": "食費  ", "3": "交通費 ", "4": "日用品費"}

for i in range(len(Dict_item)):
    Radio = Use_Radio(Frame_item)
    Radio.grid(column=0, row=i + 1)
    Radio["text"] = Dict_item[str(i + 1)]

最初の「Dict_item」は家計簿の品目を定義しています。
なんでディクショナリにしているのか、という疑問がわいてきますね。私もです。

「radio」オブジェクトの作成、配置はfor文の中で行われています。これは今までのオブジェクトと同様です。
「Radio["text"]」「radio」オブジェクトが示す選択肢の名前を定義しています。

forwhileの中でオブジェクトを作成することはよくよくあります。ここで気を付けるべきなのは、ループ毎に変数を上書きしているはずなのにオブジェクトが複数できているという点です。これはおそらく、tkinterオブジェクトは明示的な破棄を行わない限り、消されないという振る舞いが定義されているためです。

step2.png

実行すると上図のような画面を作成できます。最も、この画面はどこか不自然ですね。まあいいでしょう。次へ行きます。

step3

step3.py
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk
import datetime

##############################################################################
#フレーム
class Use_Frame(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
##############################################################################
#ラベル
class Use_Label(tk.Label):
    def __init__(self, master=None):
        tk.Label.__init__(self, master)
###############################################################################
#エントリー
class Use_Entry(tk.Entry):
    def __init__(self, master=None):
        tk.Entry.__init__(self, master)
##############################################################################
#ラジオボタン
class Use_Radio(tk.Radiobutton):
    def __init__(self, master=None):
        tk.Radiobutton.__init__(self, master)
##############################################################################
#ボタン(step3.pyで追加)
class Use_Button(tk.Button):
    def __init__(self, master=None):
        tk.Button.__init__(self, master)
##############################################################################

root = tk.Tk()
root.title("家計簿")

#****************************************************************************#
#日付の取得
day = datetime.datetime.now()
day = day.strftime("%Y/%m/%d")

#****************************************************************************#
#Frame_date(日付の入力)
#ラベル、エントリーの作成
Frame_date = Use_Frame(root)
Frame_date.grid(column=0, row=0)

Label_date_1 = Use_Label(Frame_date)
Label_date_1.grid(column=0, row=0)
Label_date_1["text"] = "日付の入力"

Entry_date = Use_Entry(Frame_date)
Entry_date.grid(column=1, row=0)
Entry_date.insert(0, day)
#****************************************************************************#
#Frame_item(品目の選択)
#ラジオボタンの作成

Frame_item = Use_Frame(root)
Frame_item.grid(column=0, row=1)

Label_radio = Use_Label(Frame_item)
Label_radio.grid(column=0, row=0)
Label_radio["text"] = "品目を選択"

Dict_item = {"1": "娯楽費 ", "2": "食費  ", "3": "交通費 ", \
             "4": "日用品費"}

for i in range(len(Dict_item)):
    Radio = Use_Radio(Frame_item)
    Radio.grid(column=0, row=i + 1)
    Radio["text"] = Dict_item[str(i + 1)]

#****************************************************************************#
#Frame_cash(金額入力)(step3.pyで追加)

Frame_cash = Use_Frame(root)
Frame_cash.grid(column=0, row=2)

Label_cash = Use_Label(Frame_cash)
Label_cash.grid(column=0, row=0)
Label_cash["text"] = "金額の入力"

Entry_cash = Use_Entry(Frame_cash)
Entry_cash.grid(column=1, row=0)

#****************************************************************************#
#root(送信ボタン)(step3.pyで追加)
Button_display = Use_Button(root)
Button_display.grid(column=0, row=3)
Button_display["text"] = "送信"

root.mainloop()

上記のプログラムを実行すると、下のような画面が表示されます。

step3.png

このstep3.pyで追加した記述が行っていることは、

  1. 新しい「Frame」オブジェクトを作成
  2. "金額の入力"という「Label」オブジェクトを新しい「Frame」オブジェクトに配置
  3. 「Entry」オブジェクトを新しい「Frame」オブジェクトに配置
  4. 「Button」オブジェクトを「root」に配置

です。

1から3はこれまでのstepで行ったことと同じですね。4はボタンオブジェクトを配置しています。とはいえこちらも
今までのオブジェクト配置と同様の方法を用いているので簡単に理解できると思います。

「Label」オブジェクトと同様に["text"]「Button」オブジェクト上の文字を設定しています。

step4

step4.py
from tkinter import *
import tkinter as tk
import tkinter.ttk as ttk
import datetime

##############################################################################
#フレーム
class Use_Frame(tk.Frame):
    def __init__(self, master=None):
        tk.Frame.__init__(self, master)
        #step4.pyで追加
        self["width"] = 600
        self["height"] = 600
        self["padx"] = 20
        self["pady"] = 20
##############################################################################
#ラベル
class Use_Label(tk.Label):
    def __init__(self, master=None):
        tk.Label.__init__(self, master)
        #step4.pyで追加
        self["font"] = ("Helvetica", 20)
        self["width"] = 10
        self["anchor"] = "e"
        self["padx"] = 10
        self["pady"] = 10
###############################################################################
#エントリー
class Use_Entry(tk.Entry):
    def __init__(self, master=None):
        tk.Entry.__init__(self, master)
        #step4.pyで追加
        self["font"] = ("Helvetica", 20)
        self["width"] = 10

##############################################################################
#ラジオボタン
class Use_Radio(tk.Radiobutton):
    def __init__(self, master=None):
        tk.Radiobutton.__init__(self, master)
        #step4.pyで追加
        self["font"] = ("Helvetica", 20)
##############################################################################
#ボタン
class Use_Button(tk.Button):
    def __init__(self, master=None):
        tk.Button.__init__(self, master)
        #step4.pyで追加
        self["height"] = 2
        self["width"] = 20
        self["font"] = ("Helvetica", 15)
##############################################################################

root = tk.Tk()
root.title("家計簿")

#****************************************************************************#
#日付の取得
day = datetime.datetime.now()
day = day.strftime("%Y/%m/%d")

#****************************************************************************#
#Frame_date(日付の入力)
#ラベル、エントリーの作成
Frame_date = Use_Frame(root)
Frame_date.grid(column=0, row=0)

Label_date_1 = Use_Label(Frame_date)
Label_date_1.grid(column=0, row=0)
Label_date_1["text"] = "日付の入力"

Entry_date = Use_Entry(Frame_date)
Entry_date.grid(column=1, row=0)
Entry_date.insert(0, day)
#****************************************************************************#
#Frame_item(品目の選択)
#ラジオボタンの作成

Frame_item = Use_Frame(root)
Frame_item.grid(column=0, row=1)

Label_radio = Use_Label(Frame_item)
Label_radio.grid(column=0, row=0)
Label_radio["text"] = "品目を選択"

Dict_item = {"1": "娯楽費 ", "2": "食費  ", "3": "交通費 ", \
             "4": "日用品費"}

for i in range(len(Dict_item)):
    Radio = Use_Radio(Frame_item)
    Radio.grid(column=0, row=i + 1)
    Radio["text"] = Dict_item[str(i + 1)]

#****************************************************************************#
#Frame_cash(金額入力)

Frame_cash = Use_Frame(root)
Frame_cash.grid(column=0, row=2)

Label_cash = Use_Label(Frame_cash)
Label_cash.grid(column=0, row=0)
Label_cash["text"] = "金額の入力"

Entry_cash = Use_Entry(Frame_cash)
Entry_cash.grid(column=1, row=0)

#****************************************************************************#
#root(送信ボタン)
Button_display = Use_Button(root)
Button_display.grid(column=0, row=3)
Button_display["text"] = "送信"

root.mainloop()

上記のプログラムを実行すると、下のような画面が表示されます。

step4.png

このstep4.pyで追加した記述が行っていることは

  1. オブジェクトの大きさを設定
  2. 文字列のフォントを設定
  3. オブジェクトの周囲に作る余白の設定

です。

1. オブジェクトの大きさを設定

オブジェクトの大きさを設定
self["width"] = 600
self["height"] = 600

基本的には上の二つ、「width」「height」を設定します。
オブジェクトの幅と高さの設定を行います。

今まではインスタンス変数["text"]のような形だったと思います。
これはインスタンスオブジェクトに対して個別に値を設定したいときに使用します。
同じクラスから作成されたインスタンスでも、それぞれ固有の設定値を持つことが出来ます。
したがって、オブジェクトによって柔軟な設定を行うことが出来ます。

一方でself["属性名"]とする場合、クラスオブジェクトに対しての設定となります。
したがって、このクラスから作成されたインスタンスは、明示的に設定をしない限りクラスオブジェクトの
設定を引き継ぐことになります。

クラスオブジェクトに対しての設定は画面に配置するオブジェクトに対して統一感を与えることが出来ます。

つまり、統一する必要のない設定値、例えば"text"などはオブジェクトごとに設定し、統一したい設定値は大元のクラスオブジェクトで設定してしまうのです。

この幅と高さの設定に関して気を付けるべき点は、各オブジェクトによって単位が異なるということです。
何が言いたいかと言えば「Frame」オブジェクトの["width"] = 600「Label」オブジェクトの["width"] = 600は異なるということです。

何度も配置して好みの大きさにしましょう。

2. 文字列のフォントを設定

文字列のフォントを設定
self["font"] = ("Helvetica", 15)

上記の設定で文字形式と文字サイズを設定できます。
フォントの設定は文字列を表示するオブジェクトに対して設定します。

フォントのタイプにこだわりがある? それならば、tkinter.fontモジュールをimportしましょう。詳しい方法はこちらで。

3. オブジェクトの周囲に作る余白の設定

余白の作成
self["padx"] = 10
self["pady"] = 10

上記の設定でオブジェクトの周囲に作る余白を設定できます。
オブジェクト同士がくっつきすぎて嫌な場合は使用しましょう。見た目がすっきりするかもです。
また、この["padx(y)"]はほとんどのオブジェクトで設定できるはずです。つまり、ほとんどのオブジェクトは周囲の余白を調整する機能を持っているべきだという事です。

step5

グラフ部分の作成

...
import numpy as np

#step5で追加
##############################################################################
#ノートブック
class Use_NoteBook(ttk.Notebook):
    """Textを管理するNoteBookウィジェット"""

    def __init__(self, master=None):
        ttk.Notebook.__init__(self, master)

##############################################################################

...

Main = Use_Frame(root)
Main["width"] = 1200
Main["height"] = 1200
Main.grid(column=0, row=0)

Main_left = Use_Frame(Main)
Main_left.grid(column=0, row=0)

note = Use_NoteBook(Main)
note.grid(column=1, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))

Main_right = Use_Frame(note)
Main_right.grid(column=0, row=0)
note.add(Main_right, text="送信")

###############################################################################
#****************************************************************************#
#日付の取得
Time = StringVar()
...

#****************************************************************************#
#Frame_date(日付の入力)
#ラベル、エントリーの作成
Frame_date = Use_Frame(Main_left)
...

#****************************************************************************#
#Frame_item(品目の選択)
#ラジオボタンの作成

Frame_item = Use_Frame(Main_left)
...

#****************************************************************************#
#Frame_cash(金額入力)

Frame_cash = Use_Frame(Main_left)
...

#****************************************************************************#
Button_display = Use_Button(Main_left)
...

step4.pyとの差分だけ表示しています。

上記のプログラムを実行すると以下のような画面が表示されます。

step5.png

このstep5.pyで追加した記述が行っていることは

  1. 新しい「Notebook」オブジェクトを作成
  2. 新しい「Frame」オブジェクト「Main_left」と「Main_right」を作成
  3. step4.pyまでに作ったオブジェクトを「root」から「Main_left」へ移動
  4. 「Notebook」を「Main_right」へ配置

です。

1. 新しい「Notebook」オブジェクトを作成

#ノートブック
class Use_NoteBook(ttk.Notebook):
    """Textを管理するNoteBookウィジェット"""

    def __init__(self, master=None):
        ttk.Notebook.__init__(self, master)

Notebookオブジェクトというのは、いわゆるタブです。

今回は、家計簿に追加する品物と金額のリストを表示するため、および、グラフを表示するために使用します。

今はまだ最小限の記載しかありません。

2. 新しい「Frame」オブジェクト「Main_left」と「Main_right」を作成


Main = Use_Frame(root)
Main["width"] = 1200
Main["height"] = 1200
Main.grid(column=0, row=0)

Main_left = Use_Frame(Main)
Main_left.grid(column=0, row=0)

Main_right = Use_Frame(note)
Main_right.grid(column=0, row=0)

MainというFrameを作成し、その上にMain_leftMain_rightを乗せます。

これは上で説明したように、このFrameに乗っかるオブジェクトの配置を容易にするためです。

3. step4.pyまでに作ったオブジェクトを「root」から「Main_left」へ移動

Frame_date = Use_Frame(Main_left)

Frame_item = Use_Frame(Main_left)

Frame_cash = Use_Frame(Main_left)

Button_display = Use_Button(Main_left)

いくつかのオブジェクトは親オブジェクトがrootであるため、これをMain_leftへ変更します。

4. 「Notebook」を「Main_right」へ配置

note = Use_NoteBook(Main)
note.grid(column=1, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))

note.add(Main_right, text="送信")

NotebookオブジェクトをMain_rightへ配置します。

sticky=(tk.N, tk.S, tk.E, tk.W)Notebookを全体に引き伸ばすために使用します。

note.add(Main_right, text="送信")は作成したNotebookオブジェクトへ、送信という名前のタブを作成します。

step6

ノートブック部分の作成

#ボタン
class Use_Button(tk.Button):
    def __init__(self, master=None):
        ...

    # step6で追加
    def Insert(self, event=None):
        entry_date = Entry_date.get()
        entry_cash = Entry_cash.get()

        Val_Radio = str(val.get() + 1)

        List_display.insert(END, "  ".join([entry_date, Dict_item[Val_Radio], entry_cash]))

...

#****************************************************************************#
#Frame_item(品目の選択)
#ラジオボタンの作成

...

Dict_item = {"1": "娯楽費 ", "2": "食費  ", "3": "交通費 ", \
             "4": "日用品費"}

# 以下をstep6で追加

val = IntVar()
val.set(1)

for i in range(len(Dict_item)):
    Radio = Use_Radio(Frame_item)
    Radio.grid(column=0, row=i + 1)
    Radio["text"] = Dict_item[str(i + 1)]
    Radio["value"] = i
    Radio["variable"] = val

...

#****************************************************************************#
#Frame_Listbox(entryの取得、表示)
#値の取得

Frame_Listbox = Use_Frame(Main_right)
Frame_Listbox.grid(column=0, row=0)

scrollbar = Scrollbar(Frame_Listbox)
scrollbar.pack(side = RIGHT, fill = Y)

List_display = Listbox(Frame_Listbox, yscrollcommand = scrollbar.set, selectmode=EXTENDED)
List_display.pack(side = LEFT, fill = BOTH)
List_display["width"] = 30
List_display["height"] = 10
List_display["font"] = ("Helvetica", 20)

scrollbar["command"] = List_display.yview

Button_display = Use_Button(Main_left)
Button_display.grid(column=0, row=3)
Button_display["text"] = "送信"
Button_display["command"] = Button_display.Insert

step5.pyとの差分だけ表示しています。

上記のプログラムを実行すると以下のような画面が表示されます。

step6_1.png

step6_2.png

このstep6.pyで追加した記述が行っているのは

  1. Listboxの配置
  2. 入力した値とラジオボタンの値を表示するメソッドの作成
  3. 送信ボタンとメソッドを紐付け

です。

Listboxの配置


#Frame_Listbox(entryの取得、表示)
#値の取得

Frame_Listbox = Use_Frame(Main_right)
Frame_Listbox.grid(column=0, row=0)

scrollbar = Scrollbar(Frame_Listbox)
scrollbar.pack(side = RIGHT, fill = Y)

List_display = Listbox(Frame_Listbox, yscrollcommand = scrollbar.set, selectmode=EXTENDED)
List_display.pack(side = LEFT, fill = BOTH)
List_display["width"] = 30
List_display["height"] = 10
List_display["font"] = ("Helvetica", 20)

scrollbar["command"] = List_display.yview

まず、Listboxを配置するためのFrameを作成します。ここでは、Frame_Listboxですね。このようにFrameを作成する理由は何度も述べるように下のFrameListbox無関係(疎結合)にするためです。

次にScrollbarFrame_Listboxへスクロールバーを作成しています。これはListboxへたくさんの値を表示する際に、下へスクロールさせるために設定しています。packで配置を行っています。

List_displayからListboxの配置になります。高さ、幅、フォントなどの設定を行います。先に作ったScrollbarとの連携もここで行っています。

入力した値とラジオボタンの値を表示するメソッドの作成

class Use_Button(tk.Button):
    ...

    def Insert(self, event=None):
        entry_date = Entry_date.get()
        entry_cash = Entry_cash.get()

        Val_Radio = str(val.get() + 1)

        List_display.insert(END, "  ".join([entry_date, Dict_item[Val_Radio], entry_cash]))

Use_ButtonクラスにInsertメソッドを定義しています。これは画像の送信ボタンが押されたときに実行するためのメソッドです。

Entryオブジェクトからデータを取り出すためにEntry.getを使用して、日付(entry_date)と金額(entry_cash)を取得しています。

また、現在選択されているラジオボタンの値を取得するためにval.get()を使用しています。

Listboxへ値を書き込むためにはinsertメソッドを使用します。insert(END, "文字列")とすることで、Listboxの一番下へ文字列を挿入します。ここではjoinメソッドを使用し、日付(entry_data)ラジオボタンの値(Dict_item[Val_Radio])金額(entry_cash)半角スペース(" ")で結合しています。

送信ボタンとメソッドを紐付け


Button_display["command"] = Button_display.Insert

Buttonクラスのcommandへ先程作成したメソッドを指定します。

その他


val = IntVar()
val.set(1)

for i in range(len(Dict_item)):
    Radio = Use_Radio(Frame_item)
    Radio.grid(column=0, row=i + 1)
    Radio["text"] = Dict_item[str(i + 1)]
    Radio["value"] = i
    Radio["variable"] = val

この部分はラジオボタンへ値を割り振るために今回追加しました。val変数へIntVarというtkinterで使用するint型変数を格納します。ここでvar.set(1)としているのは、このアプリケーションを起動したときにデフォルトで1という値を持つラジオボタンが選択された状態にしたかったためです。

for文の中で実施しているのはRadio["value"]で各ラジオボタンへ数字を割り当てることと、Radio["variable"]へ上で作ったvalを割り当てることです。

こうすることで、各ラジオボタンは一意の数字をもち、かつval.get()でその数字を取得することができるようになります。

6
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
12