こどものために計算ドリルを作ってみたpython編 ②Tkinterとreportlabを用いてPDF作成

# お子様がいるプログラマーのみなさん

はい。では[前回作成](https://qiita.com/miyama_daily/items/56b65c255c4e5c504093)した関数を使って計算プリントのPDFファイルをバシバシ出力していきましょう。

https://qiita.com/miyama_daily/items/56b65c255c4e5c504093



# Tkinterとreportlabを使います

ここに一番時間がかかったのですが、正直ほかの記事を参照した方がいいと思います。

参考にした記事
* Tkinterで数値のみの入力制限をする方法
https://kuroro.blog/python/YaHEdMd4ScGvrU44zdT6/#google_vignette

* Tkinterのドキュメント
https://docs.python.org/ja/3/library/tkinter.html
* reportlabのドキュメント
https://docs.reportlab.com/


今回の記事ではとりあえず実践例を載せちゃいます！！！

**以下をコピペするだけで計算ドリルのPDF作成できちゃいます！！！**

# 構成

parts.py           ・・・　問題作成関数とPDF作成関数をまとめたpy file
addition.py        ・・・　足し算を出力するpy file
subtraction.py     ・・・　引き算を出力するpy file
multiplication.py  ・・・　掛け算を出力するpy file
division.py        ・・・　割り算を出力するpy file

# parts.py

前回作成した問題作成関数と、今回初出のPDF作成関数をまとめたものです。

<details><summary>parts.py</summary>

```py:parts.py

import random
import os

from datetime import datetime

from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.pagesizes import A4, mm


# 繰り上がり判定機　
def k_up(N, M):
    d_N = len(str(N))
    d_M = len(str(M))
    
    if d_N > d_M: 
        l = d_M
    else: 
        l = d_N

    for i in range(1, l+1):
        if int(str(N)[-i]) + int(str(M)[-i]) >= 10:
            return False
        else:
            continue
    
    # 繰り上がりありならFalse、なしならTrueを返す
    return True

def generate_temp_addition(N, M):
    retern random.randint(10**(N-1), 10**(N)-1), random.randint(10**(M-1), 10**(M)-1)

def generate_addition(N, M, k):    
    # 繰り上がりなし
    if k is False:
        # Trueとなれば抜けられるようにする
        while k is False:
            n_N, n_M = generate_temp_addition(int(N), int(M))
            k = k_up(n_N, n_M)

    # 繰り上がりあり
    else:
        # Falseとなれば抜けられるようにする
        while k is True:
            n_N, n_M = generate_temp_addition(int(N), int(M))
            k = k_up(n_N, n_M)

    return {"N": n_N, "M": n_M, "ans": {"value": n_N+n_M}}


def k_down(N, M):

    for i in range(1, len(str(M))+1):
        if int(str(N)[-i]) < int(str(M)[-i]):
            return False
        else:
            continue

def generate_temp_asubtraction(N, M):
    retern random.randint(10**(N-1), 10**(N)-1), random.randint(10**(M-1), n_N)

def generate_subtraction(N, M, k):  
    # 繰り上がりなし
    if k is False:
        # Trueとなれば抜けられるようにする
        while k is False:
            n_N, n_M = generate_temp_subtraction(int(N), int(M))
            k = k_down(n_N, n_M)

    # 繰り上がりあり
    else:
        # Falseとなれば抜けられるようにする
        while k is True:
            n_N, n_M = generate_temp_subtraction(int(N), int(M))
            k = k_down(n_N, n_M)

    return {"N": n_N, "M": n_M, "ans": {"value": n_N-n_M}}
    

def generate_multiplication(N, M):

    n_N = random.randint(10**(N-1), 10**(N)-1)
    n_M = random.randint(10**(M-1), 10**(M)-1)

    return {"N": n_N, "M": n_M, "ans": {"value": n_N*n_M}}


def make_divisors(n):
    lower_divisors , upper_divisors = [], []
    i = 1
    while i*i <= n:
        if n % i == 0:
            lower_divisors.append(i)
            if i != n // i:
                upper_divisors.append(n//i)
        i += 1
    return lower_divisors + upper_divisors[::-1]


def generate_division(N, M, s, a):

    if a is False:

        p_divisors = []

        while len(p_divisors) == 0:
            n_N = random.randint(10**(N-1), 10**(N)-1)
            divisors = make_divisors(n_N)
            print(divisors)
            if s is False:
                del divisors[0]
                del divisors[-1]
            p_divisors = [i for i in divisors if 10**(M-1) <= i <= 10**(M)-1]

        n_M = random.choice(p_divisors)

    else:
        n_N = random.randint(10**(N-1), 10**(N)-1)
        divisors = make_divisors(n_N)
        if N == M:
            n_M = random.randint(10**(M-1), n_N)
        else:
            n_M = random.randint(10**(M-1), 10**(M)-1)
        while n_M in divisors:
            if N == M:
                n_M = random.randint(10**(M-1), n_N)
            else:
                n_M = random.randint(10**(M-1), 10**(M)-1)

    return {"N": n_N, "M": n_M, "ans": {"value": int(n_N/n_M), "sho": n_N//n_M, "amari":n_N%n_M}}



def Set_calc(c, x, y, problem, ope, ans=False, a=False):
    
    c.setLineWidth(1.5)

    #答えあり
    if ans is True:

        x_offset = 10   
        if ope == "÷" and a is True:
            c.drawString((x+x_offset)*mm, y*mm, f'{problem["N"]} {ope} {problem["M"]} = {problem["ans"]["sho"]}...{problem["ans"]["amari"]}')
        else:
            c.drawString((x+x_offset)*mm, y*mm, f'{problem["N"]} {ope} {problem["M"]} = {problem["ans"]["value"]}')


    # 答えなし
    else:

        if H is False:
            x_offset = 10   
            c.drawString((x+x_offset)*mm, y*mm, f'{problem["N"]} {ope} {problem["M"]} =')

 
# 1ページ分の計算式を生成する関数
# aはあまりあるかどうか
# 答えなしとありをセットで出力します。
def make_page(c, x_list, y_list, list_of_problems, ope, a=False):

    # 答えなし
    # フォントを指定することも可能です
    fontname = "Helvetica"
    c.setFont(fontname,30)
    # ヘッダー部分の文字列出力
    c.drawString(10,800, "  Day:    /          Score:    /          Time:            ")
    
    # Set Value
    n = 0
    for row in y_list:
        for col in x_list:
            n += 1
            Set_calc(c,col,row, list_of_problems[n-1], ope, ans=False, a=a)
    c.showPage()

    # 答えあり
    c.drawString(10,800, "  Day:    /          Score:    /          Time:            ")
    # Set Value
    n = 0
    for row in y_list:
        for col in x_list:
            n += 1
            Set_calc(c,col,row, list_of_problems[n-1], ope, ans=True, a=a)
    c.showPage()
           


def pdf_export(list_of_paper, x_list, y_list, ope, a=False):
    # pathに注意
    # 親ディレクトリにpdfというディレクトリを用意してください。
    c = canvas.Canvas(f"{os.path.dirname(os.path.dirname(os.path.dirname(__file__)))}/pdf/calc_train_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf",pagesize=A4)

    for l in list_of_paper:
        make_page(c, x_list, y_list, l, ope, a)

    c.save()

```

</details>

# 足し算

入力として
N, M, kが必要ですのでそれを取得できるようなTkinterを作成しました。
コードが長いので隠しておきます。

<details><summary>足し算ドリル作成のコード</summary>

```py:addition.py
import random
import re
import tkinter as tk
from tkinter import ttk
import os, sys


from parts.parts import generate_addition, pdf_export


# 値を取得するTkinter
def get_value():

    root = tk.Tk()
    root.title("足し算")
    root.geometry("600x600")

    v1, v2, v3, v4 = None, None, None, None

    # ウィンドウを閉じた際にPythonを終了するように誘導するためにFalseを返す
    def close_window():
        root.quit()
        return False

    # ウィンドウを閉じるイベントの設定
    root.protocol("WM_DELETE_WINDOW", close_window)

    #桁数の選択肢　とりあえず5桁までにしていますが変更可能
    options = [1, 2, 3, 4, 5]

    # 一項目
    label1=tk.Label(root,text="一項目の桁数：")
    label1.pack(padx=10, pady=10)
    value1 = ttk.Combobox(root, values=options, state="readonly")
    value1.pack(padx=10)

    # 二項目
    label2=tk.Label(root,text="二項目の桁数：")
    label2.pack(padx=10, pady=10)
    value2 = ttk.Combobox(root, values=options, state="readonly")
    value2.pack(padx=10)

    # 繰り上がり
    value3 = tk.BooleanVar()
    rb1 = ttk.Radiobutton(root, value=True,variable=value3, text="繰り上がりあり")
    rb2 = ttk.Radiobutton(root, value=False,variable=value3, text="繰り上がりなし")
    rb1.pack(padx=10, pady=10)
    rb2.pack()

    # 枚数
    def invalidText():
        print('半角数字を入力してください。')

    # 1. 入力制限の条件を設けて検証する関数の名前を決める
    # 4. 入力制限の条件を設けて検証する関数を実装する
    def onValidate(S):
        # 入力された文字が半角数字の場合
        if re.match(re.compile('[0-9]+'), S):
            return True
        else:
            # 入力不正のブザーを鳴らす。
            root.bell()
            return False

    # 2. 1で決めた関数名を、register関数を用いて登録する
    # register : 入力制限を行うための関数の登録を行う。パラメータと関数を紐づけるために必要。
    vcmd = root.register(onValidate)

    # validate : 入力制限するオプションの値を設定。
    # validatecommand or vcmd : 入力制限用関数の設定。(3. entryのvalidatecommand option or vcmd optionへ、2の戻り値とパラメータを渡す)
    # invalidcommand : 入力制限により、入力不正が発生した場合に呼ばれる関数の設定。
    
    label4=tk.Label(root,text="プリントの枚数：")
    label4.pack(padx=10, pady=10)
    value4 = tk.Entry(root, width=10, validate="key", validatecommand=(vcmd, '%S'), invalidcommand=invalidText)
    value4.pack(padx=10, pady=10)

    
    # OKボタン押すと値を返す
    def ok_get(event):
        nonlocal v1, v2, v3, v4
        v1, v2, v3, v4 = value1.get(), value2.get(), value3.get(), value4.get()
        root.quit()


    #ボタン
    button1 = tk.Button(text='OK')
    button1.bind("<Button-1>", ok_get)
    button1.pack(padx=10, pady=20)
    
    # Enter でも OK
    root.bind('<Return>', ok_get)
    
    root.mainloop()

    try:
        return [v1, v2, v3, v4]
    except:
        return False



def get_list_problems(d_N, d_M, k, n):

    list_of_problems = []

    while len(list_of_problems) < n:
        list_of_problems.append(generate_addition(d_N, d_M, k))

    return list_of_problems


def main():
    n = 20 # 1枚の問題数
    # 計算式の基本座標
    # 2 * 10
    # A4 210 297
    # 位置のリスト
    x_list = list(range(0, 210, 105))  
    y_list = list(range(260, -10, -27))  


    if n != len(x_list) * len(y_list):
        return False

    lst = get_value()
    if lst is False:
        return None

    list_of_paper = []
    for i in range(int(lst[3])):
        list_of_paper.append(get_list_problems(int(lst[0]), int(lst[1]), lst[2], n))


    # ope is "+"
    pdf_export(list_of_paper, x_list, y_list, "+")


if __name__ == "__main__":
    main()

```
</details>

# 引き算

入力として
N, M, kが必要ですのでそれを取得できるようなTkinterを作成しました。
コードが長いので隠しておきます。

<details><summary>引き算ドリル作成のコード</summary>

```py:subtraction.py
import random
import re
import tkinter as tk
from tkinter import ttk
import os, sys


from parts import generate_subtraction, pdf_export


# 値を取得するTkinter
def get_value():

    root = tk.Tk()
    root.title("引き算")
    root.geometry("600x600")

    v1, v2, v3, v4 = None, None, None, None

    # ウィンドウを閉じた際にPythonを終了するように誘導するためにFalseを返す
    def close_window():
        root.quit()
        return False

    # ウィンドウを閉じるイベントの設定
    root.protocol("WM_DELETE_WINDOW", close_window)

    #pull down
    options = [1, 2, 3, 4, 5]
    options2 = {"1": [1], "2": [1, 2], "3": [1, 2, 3], "4":[1, 2, 3, 4], "5":[1, 2, 3, 4, 5]}

    # コールバック関数
    def update_combo2(event):
        selected = value1.get()
        value2['values'] = options2[str(selected)]
        #combo2.set('')  # 初期値をリセット

    # 一項目
    label1=tk.Label(root,text="一項目の桁数：")
    label1.pack(padx=10, pady=10)
    value1 = ttk.Combobox(root, values=options, state="readonly")
    value1.pack(padx=10)
    value1.bind("<<ComboboxSelected>>", update_combo2)

    # 二項目
    label2=tk.Label(root,text="二項目の桁数：")
    label2.pack(padx=10, pady=10)
    value2 = ttk.Combobox(root, values=options2, state="readonly")
    value2.pack(padx=10)

    # 繰り下がり
    value3 = tk.BooleanVar()
    rb1 = ttk.Radiobutton(root, value=True,variable=value3, text="繰り下がりあり")
    rb2 = ttk.Radiobutton(root, value=False,variable=value3, text="繰り下がりなし")
    rb1.pack(padx=10, pady=10)
    rb2.pack()


    # 枚数

    def invalidText():
        print('半角数字を入力してください。')

    # 1. 入力制限の条件を設けて検証する関数の名前を決める
    # 4. 入力制限の条件を設けて検証する関数を実装する
    def onValidate(S):
        # 入力された文字が半角数字の場合
        if re.match(re.compile('[0-9]+'), S):
            return True
        else:
            # 入力不正のブザーを鳴らす。
            root.bell()
            return False

    # 2. 1で決めた関数名を、register関数を用いて登録する
    # register : 入力制限を行うための関数の登録を行う。パラメータと関数を紐づけるために必要。
    vcmd = root.register(onValidate)

    # validate : 入力制限するオプションの値を設定。
    # validatecommand or vcmd : 入力制限用関数の設定。(3. entryのvalidatecommand option or vcmd optionへ、2の戻り値とパラメータを渡す)
    # invalidcommand : 入力制限により、入力不正が発生した場合に呼ばれる関数の設定。
    
    label4=tk.Label(root,text="プリントの枚数：")
    label4.pack(padx=10, pady=10)
    value4 = tk.Entry(root, width=10, validate="key", validatecommand=(vcmd, '%S'), invalidcommand=invalidText)
    value4.pack(padx=10, pady=10)

    
    # OKボタン押すと値を返す
    def ok_get(event):
        nonlocal v1, v2, v3, v4
        v1, v2, v3, v4 = value1.get(), value2.get(), value3.get(), value4.get()
        root.quit()


    #ボタン
    button1 = tk.Button(text='OK')
    button1.bind("<Button-1>", ok_get)
    button1.pack(padx=10, pady=20)
    
    # Enter でも OK
    root.bind('<Return>', ok_get)
    
    root.mainloop()

    try:
        return [v1, v2, v3, v4]
    except:
        return False


def get_list_problems(d_N, d_M, k, n):

    list_of_problems = []

    while len(list_of_problems) < n:
        list_of_problems.append(generate_subtraction(d_N, d_M, k))

    return list_of_problems


def main():
    n = 20 # 1枚の問題数
    # 計算式の基本座標
    # 2 * 10
    # A4 210 297
    x_list = list(range(0, 210, 105))  
    y_list = list(range(260, -10, -27))  


    if n != len(x_list) * len(y_list):
        return False

    lst = get_value()
    if lst is False:
        return None

    list_of_paper = []
    for i in range(int(lst[3])):
        list_of_paper.append(get_list_problems(int(lst[0]), int(lst[1]), lst[2], n))


    # ope is "-"
    pdf_export(list_of_paper, x_list, y_list, "-")


if __name__ == "__main__":
    main()


```
</details>



コンボボックスの選択肢を動的に変更するのは
[この](URL "https://zenn.dev/nuinui/articles/7e7588fe49db40")記事を参考にしました
https://zenn.dev/nuinui/articles/7e7588fe49db40


# 掛け算

コードが長いので隠しておきます。

<details><summary>掛け算ドリル作成のコード</summary>

```py:multiplication.py
import random
import re
import tkinter as tk
from tkinter import ttk
import os, sys


from parts import generate_multiplication, pdf_export


# 値を取得するTkinter
def get_value():

    root = tk.Tk()
    root.title("掛け算")
    root.geometry("600x600")

    v1, v2, v3 = None, None, None

    # ウィンドウを閉じた際にPythonを終了するように誘導するためにFalseを返す
    def close_window():
        root.quit()
        return False

    # ウィンドウを閉じるイベントの設定
    root.protocol("WM_DELETE_WINDOW", close_window)

    #pull down
    options = [1, 2, 3, 4, 5]
    options2 = [1, 2, 3, 4, 5]

    # 一項目
    label1=tk.Label(root,text="一項目の桁数：")
    label1.pack(padx=10, pady=10)
    value1 = ttk.Combobox(root, values=options, state="readonly")
    value1.pack(padx=10)

    # 二項目
    label2=tk.Label(root,text="二項目の桁数：")
    label2.pack(padx=10, pady=10)
    value2 = ttk.Combobox(root, values=options2, state="readonly")
    value2.pack(padx=10)


    # 枚数

    def invalidText():
        print('半角数字を入力してください。')

    # 1. 入力制限の条件を設けて検証する関数の名前を決める
    # 4. 入力制限の条件を設けて検証する関数を実装する
    def onValidate(S):
        # 入力された文字が半角数字の場合
        if re.match(re.compile('[0-9]+'), S):
            return True
        else:
            # 入力不正のブザーを鳴らす。
            root.bell()
            return False

    # 2. 1で決めた関数名を、register関数を用いて登録する
    # register : 入力制限を行うための関数の登録を行う。パラメータと関数を紐づけるために必要。
    vcmd = root.register(onValidate)

    # validate : 入力制限するオプションの値を設定。
    # validatecommand or vcmd : 入力制限用関数の設定。(3. entryのvalidatecommand option or vcmd optionへ、2の戻り値とパラメータを渡す)
    # invalidcommand : 入力制限により、入力不正が発生した場合に呼ばれる関数の設定。
    
    label3=tk.Label(root,text="プリントの枚数：")
    label3.pack(padx=10, pady=10)
    value3 = tk.Entry(root, width=10, validate="key", validatecommand=(vcmd, '%S'), invalidcommand=invalidText)
    value3.pack(padx=10, pady=10)

    
    # OKボタン押すと値を返す
    def ok_get(event):
        nonlocal v1, v2, v3
        v1, v2, v3 = value1.get(), value2.get(), value3.get()
        root.quit()


    #ボタン
    button1 = tk.Button(text='OK')
    button1.bind("<Button-1>", ok_get)
    button1.pack(padx=10, pady=20)
    
    # Enter でも OK
    root.bind('<Return>', ok_get)
    
    root.mainloop()

    try:
        return [v1, v2, v3]
    except:
        return False




def get_list_problems(d_N, d_M, n):

    list_of_problems = []

    while len(list_of_problems) < n:
        list_of_problems.append(generate_multiplication(d_N, d_M))

    return list_of_problems


def main():
    n = 20 # 1枚の問題数
    # 計算式の基本座標
    # 2 * 10
    # A4 210 297
    x_list = list(range(0, 210, 105))  
    y_list = list(range(260, -10, -27))  


    if n != len(x_list) * len(y_list):
        return False

    lst = get_value()
    if lst is False:
        return None

    list_of_paper = []
    for i in range(int(lst[2])):
        # 掛け算割り算ではkはなし
        list_of_paper.append(get_list_problems(int(lst[0]), int(lst[1]), n))


    # ope is "×"
    pdf_export(list_of_paper, x_list, y_list, "×")



if __name__ == "__main__":
    main()

```
</details>



# 割り算

コードが長いので隠しておきます。

<details><summary>割り算ドリル作成のコード</summary>

```py:multiplication.py
import random
import re
import tkinter as tk
from tkinter import ttk
import os, sys


from parts import generate_division, pdf_export


def get_value():

    root = tk.Tk()
    root.title("割り算")
    root.geometry("600x600")

    v1, v2, v3, v4, v5 = None, None, None, None, None

    # ウィンドウを閉じた際にPythonを終了するように誘導するためにFalseを返す
    def close_window():
        root.quit()
        return False

    # ウィンドウを閉じるイベントの設定
    root.protocol("WM_DELETE_WINDOW", close_window)

    #pull down
    options = [3, 4, 5]
    options2 = {"3": [1, 2], "4":[1, 2, 3], "5":[1, 2, 3, 4]}

    # コールバック関数
    def update_combo2(event):
        selected = value1.get()
        value2['values'] = options2[str(selected)]
        #combo2.set('')  # 初期値をリセット

    # 一項目
    label1=tk.Label(root,text="一項目の桁数：")
    label1.pack(padx=10, pady=10)
    value1 = ttk.Combobox(root, values=options, state="readonly")
    value1.pack(padx=10)
    value1.bind("<<ComboboxSelected>>", update_combo2)

    # 二項目
    label2=tk.Label(root,text="二項目の桁数：")
    label2.pack(padx=10, pady=10)
    value2 = ttk.Combobox(root, values=options2, state="readonly")
    value2.pack(padx=10)

    # N ÷ N, N ÷ 1を含むかどうか
    value3 = tk.BooleanVar()
    rb1 = ttk.Radiobutton(root, value=True,variable=value3, text="n ÷ n や n ÷ 1 を含む")
    rb2 = ttk.Radiobutton(root, value=False,variable=value3, text="含まない")
    rb1.pack(padx=10, pady=10)
    rb2.pack()

    # 余りあるかどうか
    value4 = tk.BooleanVar()
    rb3 = ttk.Radiobutton(root, value=True,variable=value4, text="余りあり")
    rb4 = ttk.Radiobutton(root, value=False,variable=value4, text="余りなし")
    rb3.pack(padx=10, pady=10)
    rb4.pack()


    # 枚数

    def invalidText():
        print('半角数字を入力してください。')

    # 1. 入力制限の条件を設けて検証する関数の名前を決める
    # 4. 入力制限の条件を設けて検証する関数を実装する
    def onValidate(S):
        # 入力された文字が半角数字の場合
        if re.match(re.compile('[0-9]+'), S):
            return True
        else:
            # 入力不正のブザーを鳴らす。
            root.bell()
            return False

    # 2. 1で決めた関数名を、register関数を用いて登録する
    # register : 入力制限を行うための関数の登録を行う。パラメータと関数を紐づけるために必要。
    vcmd = root.register(onValidate)

    # validate : 入力制限するオプションの値を設定。
    # validatecommand or vcmd : 入力制限用関数の設定。(3. entryのvalidatecommand option or vcmd optionへ、2の戻り値とパラメータを渡す)
    # invalidcommand : 入力制限により、入力不正が発生した場合に呼ばれる関数の設定。
    
    label5=tk.Label(root,text="プリントの枚数：")
    label5.pack(padx=10, pady=10)
    value5 = tk.Entry(root, width=10, validate="key", validatecommand=(vcmd, '%S'), invalidcommand=invalidText)
    value5.pack(padx=10, pady=10)

    
    # OKボタン押すと値を返す
    def ok_get(event):
        nonlocal v1, v2, v3, v4, v5
        v1, v2, v3, v4, v5 = value1.get(), value2.get(), value3.get(), value4.get(),  value5.get()
        root.quit()


    #ボタン
    button1 = tk.Button(text='OK')
    button1.bind("<Button-1>", ok_get)
    button1.pack(padx=10, pady=20)
    
    # Enter でも OK
    root.bind('<Return>', ok_get)
    
    root.mainloop()

    try:
        return [v1, v2, v3, v4, v5]
    except:
        return False




def get_list_problems(d_N, d_M, s, a, n):

    list_of_problems = []

    while len(list_of_problems) < n:
        list_of_problems.append(generate_division(d_N, d_M, s, a))

    return list_of_problems


def main():
    n = 20 # 1枚の問題数
    # 計算式の基本座標
    # 2 * 10
    # A4 210 297
    x_list = list(range(0, 210, 105))  
    y_list = list(range(260, -10, -27))  


    if n != len(x_list) * len(y_list):
        return False

    lst = get_value()
    if lst is False:
        return None

    list_of_paper = []
    for i in range(int(lst[4])):
        list_of_paper.append(get_list_problems(int(lst[0]), int(lst[1]), lst[2], lst[3], n))


    # ope is "÷", a is ,,,
    pdf_export(list_of_paper, x_list, y_list, "÷", lst[3])





if __name__ == "__main__":
    main()


```
</details>


# おわり

以上！！！
それぞれのpy fileを起動すればTkinterが起動されて値を入力すればPDFファイルがバシバシ出力されます！！！

こんな感じで入力したら、、、
![スクリーンショット (356).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1221567/50eee7f7-73bf-4908-296d-2a0886ae3112.png)


こんなPDFが出てきました！！！
![スクリーンショット (357).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1221567/2caadea6-59dd-9af3-76e0-facded60cb78.png)

最高ですね！！！