Word(docx)の自動作成
はじめに
Mac環境の記事ですが、Windows環境も同じ手順になります。環境依存の部分は読み替えてお試しください。
目的
この記事を最後まで読むと、次のことができるようになります。
- デスクトップアプリを実装する
- Word(docx)の自動作成を実装する
アプリ
JSONデータとWord(docx)テンプレートをレンダリングして出力する。複数件のデータはページに分けて出力する。

JSONデータ
sample_data.json
[
{
"create_date": "令和元年 5月 1日",
"to_company_name": "カレンダー株式会社",
"to_company_department": "イヤホン本部",
"relocation_date": "令和元年 7月 28日",
"post_code": "123-4567",
"new_address": "東京都港区サンプル1-2-3 ビルディング 45F",
"new_phone_number": "1234-56-7890",
"new_fax_number": "1234-56-7890"
},
{
"create_date": "2019年 5月 1日",
"to_company_name": "冷暖房リモコン株式会社",
"to_company_department": "ティッシュケース本部",
"relocation_date": "2019年 7月 28日",
"post_code": "987-6543",
"new_address": "東京都港区サンプル9-8-7 ビルディング 65F",
"new_phone_number": "0987-65-4321",
"new_fax_number": "0987-65-4321"
}
]
Word(docx)テンプレート

実行結果
page 1/2

page 2/2

関連する記事
実行環境
環境 | Ver. |
---|---|
macOS Mojave | 10.14.5 |
Python | 3.7.3 |
tkinter | 8.5 |
docxtpl | 0.6.1 |
ソースコード
実際に実装内容やソースコードを追いながら読むとより理解が深まるかと思います。是非ご活用ください。
UIの実装
UIはTkinterで実装します。
Tkinterとは、Windows、MacOS、Linuxに対応するクロスプラットフォームなGUIライブラリです。
app.py
import os
import tkinter as tk
from tkinter import filedialog as fdialog
from tkinter import messagebox as mdialog
from model import Docx
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.create_widgets()
def set_title(self):
self.master.title('Create Docx')
def set_menu_bar(self):
self.menu_bar = tk.Menu(self.master)
self.master.config(menu=self.menu_bar)
file_menu = tk.Menu(self.menu_bar)
file_menu.add_command(label='Exit', command=self.master.quit)
self.menu_bar.add_cascade(label='File', menu=file_menu)
def select_file(self, entry):
entry.delete(0, tk.END)
entry.insert(0, fdialog.askopenfilename(initialdir=os.getcwd()))
def create_docx(self, json_url, template_url):
if not os.path.exists(json_url) or not os.path.exists(template_url):
mdialog.showerror('Error', 'Please select JSON and Template.')
return
docx = Docx(json_url=json_url, template_url=template_url)
docx.render()
def set_body(self):
tk.Label(self.master, text='JSON:').grid(row=0, column=0)
entry_json = tk.Entry(self.master)
entry_json.grid(row=0, column=1, pady=5)
tk.Button(self.master, text='Select...',
command=lambda: self.select_file(entry_json)).grid(row=0, column=2)
tk.Label(self.master, text='Template:').grid(row=1, column=0)
entry_template = tk.Entry(self.master)
entry_template.grid(row=1, column=1, pady=5)
tk.Button(self.master, text='Select...',
command=lambda: self.select_file(entry_template)).grid(row=1, column=2)
tk.Button(self.master, text='Create', width=30,
command=lambda: self.create_docx(entry_json.get(), entry_template.get())).grid(row=2, column=0, columnspan=3)
def create_widgets(self):
self.master.geometry()
self.entry = tk.Entry(self.master)
self.set_title()
self.set_menu_bar()
self.set_body()
# fix tkinter bug start
def fix_bug():
width_height = root.winfo_geometry().split('+')[0].split('x')
width = int(width_height[0])
height = int(width_height[1])
root.geometry('{}x{}'.format(width+1, height+1))
# fix tkinter bug end
if __name__ == '__main__':
root = tk.Tk()
app = Application(master=root)
# fix tkinter bug start
root.update()
root.after(0, fix_bug)
# fix tkinter bug end
app.mainloop()
※ tkinterで白画面になる不具合がありサイズを1プラスすることで改修
docx作成の実装
docxの作成はdocxtplで実装します。
docxtplとは、JSONデータとWord(docx)テンプレートをレンダリングするライブラリです。
model.py
import cgi
import json
import os.path
import re
import sys
import uuid
from tkinter import messagebox as mdialog
from docxtpl import DocxTemplate
_illegal_unichrs = [(0x00, 0x08), (0x0B, 0x0C), (0x0E, 0x1F),
(0x7F, 0x84), (0x86, 0x9F),
(0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF)]
if sys.maxunicode >= 0x10000: # not narrow build
_illegal_unichrs.extend([(0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF),
(0x3FFFE, 0x3FFFF), (0x4FFFE, 0x4FFFF),
(0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF),
(0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF),
(0x9FFFE, 0x9FFFF), (0xAFFFE, 0xAFFFF),
(0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF),
(0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF),
(0xFFFFE, 0xFFFFF), (0x10FFFE, 0x10FFFF)])
_illegal_ranges = ['%s-%s' % (chr(low), chr(high))
for (low, high) in _illegal_unichrs]
_illegal_xml_chars_RE = re.compile(u'[%s]' % u''.join(_illegal_ranges))
class Docx(object):
def __init__(self, json_url, template_url):
self.json_url = json_url
self.template_url = template_url
def read_data(self):
with open(self.json_url, 'r') as f:
load_data = json.load(f)
json_data = json.dumps(load_data)
json_data = cgi.escape(json_data)
json_data = json_data.replace('\n', '\\n')
dict_data = json.loads(json_data)
for d in dict_data:
for k in d.keys():
try:
d[k] = _illegal_xml_chars_RE.sub('', d[k])
except TypeError:
pass
return dict_data
def render(self):
dict_data = self.read_data()
docx = DocxTemplate(self.template_url)
docx.render({'applications': dict_data})
file_name = '{}.{}'.format(str(uuid.uuid4()), 'docx')
save_dir = os.path.join(os.path.curdir, 'output')
if not os.path.exists(save_dir):
os.makedirs(save_dir)
docx.save(os.path.join(save_dir, file_name))
mdialog.showinfo('Successful', 'Please check the output folder.')