使用Python从JSON创建Word(docx)


自动创建Word(docx)

  • Word(docx)自动生成

    • 介绍
    • 目的
    • 相关文章
    • 执行环境
    • 源代码
    • UI实施
    • docx创建的实现

简介

这是一篇有关Mac环境的文章,但步骤与Windows环境相同。请阅读并尝试与环境有关的部分。

目的

阅读完本文后,您将能够:

  • 实施桌面应用
  • 实现自动创建Word(docx)

アプリ

呈现并输出JSON数据和Word(docx)模板。页面上输出多个数据项。

スクリーンショット 2019-06-07 14.16.32.png

JSONデータ

sample_data.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
    {
        "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)テンプレート

スクリーンショット 2019-06-07 17.12.37.png

実行結果

第1/2页

スクリーンショット 2019-06-07 17.26.42.png

第2/2页

スクリーンショット 2019-06-07 17.26.53.png

相关文章

  • tkinter-Tcl / Tk的Python接口
  • docxtpl --PyPI

执行环境

<表格>

环境

版本


<身体>

macOS Mojave

10.14.5

Python

3.7.3

tkinter

8.5

doxtpl

0.6.1


源代码

我认为,如果您在实际阅读实现内容和源代码的同时进行阅读,将会加深理解。请一定使用。

GitHub

UI实施

UI在Tkinter中实现。

Tkinter是适用于Windows,MacOS和Linux的跨平台GUI库。

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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的创建是由docx tpl实现的。

docxtpl是一个呈现JSON数据和Word(docx)模板的库。

模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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.')