TeX で作っている報告書類が 20 個ほどあるのだが、修正を行うたびにガチャガチャやるのが面倒なこともあり、このアプリを作ろうと思い立った。
TeX はコンパイルする手間が必要だが、慣れてしまえば Word より入力は楽だし、このような自動化にも馴染むので、手放せない。
なお、使用マシンは、M1 Pro MacBook Pro 14" である。
ボタンの機能は以下の通り。
- TeX pdf;選択したファイルの pdf を作成
- Clear all:チェックボックスの選択を全解除
- Select all:チェックボックスを全選択
- View:選択した pdf を preview で表示
- Exit:アプリ終了
- Clear:(Treated Files) の下に表示された pdf 作成完了のファイル名をクリア
機能としては、チェックボックスで選択したTeX ファイルから pdf を作成する、また選択したファイルを preview で表示するというもの。管理するファイルと報告書タイトルは別途テキストファイルに書き込んであり、これを読み込む。また TeX のプレアンブルもテキストファイルにしてあり、各報告書共通で使うようにしている。
ファイル管理用テキストファイルの中身は以下の画像の通り。新しい報告書を追加する場合は、ここにファイル名=ディレクトリ名とタイトルを追加すれば GUI は自動的に更新される。

TeXのプレアンブルは以下の画像の通り。数式はamsmathであり、\renewcommand{\familydefault}{\sfdefault}により、本文フォントは sans-serif にしている。(本文は英語です)
amsmathと sans-serif の組み合わせが組版的に正しいかはわからないが、少なくとも本文のsansーserif は、自分的には読みやすい。
大した処理をしているわけではないが、ここで、少しプログラミング上のTipsを述べておく。
チェックボックスの作成
var =[tk.BooleanVar(
value=False) for i in range(n)]
cbox=[ctk.CTkCheckBox(
frame1,
width=200,
height=30,
text=sfile[i],
variable=var[i]
) for i in range(n)]
for i in range(n):
ir,ic=divmod(i,3)
cbox[i].grid(row=ir,column=ic)
チェックボックスは、別途テキストファイルに記載された報告書の数だけ自動的に生成される。チェックボックスのオン・オフは、varという変数を定義しこれの値(True or False)により把握する。
チェックボックスの配置はgridで行っているが、列数を3に固定し、ir,ic=divmod(i,3)で表示順を列数で割った商と余りを行番号と列番号に当てはめている。
アプリを立ち上げた状態では、チェックボックスは、全解除(False)の状態である。
pdf 作成制御
def get_value():
tbox.delete('0.0','end')
app.update()
for i in range(n):
if var[i].get()==True:
dlist=pre_org.copy()
dirn=sfile[i]
if i==0:
cmd='python py_toc.py'
subprocess.run(cmd,shell=True)
tex_pre_toc(dlist)
else:
tstr=stitle[i]
tex_pre(dirn,tstr,dlist)
tex_pdf(dirn)
tbox.insert('end',dirn+'\n')
tbox.see('end')
app.update()
messagebox.showinfo('showinfo','Work has been completed')
TeX pdfのボタンを押すと、チェックボックスで選択されているファイルの pdf を作成する。pdf 作成処理が完了すると、ボタン群の下に配置したテキストボックスに逐次処理完了ファイル名を出力していく。TeX による pdf 作成そのものは、関数tex_pdf内で、platexおよびdvipdfmxにより行っている。
上記リスト中、python の外部プログラム py_toc.pyを実行しているが、これは、ファイルリストを読み込み、これを表形式にした画像ファイルを作成するもの。TeX の中でこれを読み込み、報告書全体のカバーページを作成している。
pdf 作成済みファイルの逐次出力は、テキストボックスにinsertで追加するのだが、スクロールバーを自動でスクロールさせる、すなわち最下段のテキストを表示するように.see('end')を入れる。またファイル名を追加したらGUI表示を更新するためapp.update()を入れる。この処理を取り出したコードは以下の通り。
tbox.insert('end',dirn+'\n')
tbox.see('end')
app.update()
pdf 表示
def view():
for i in range(n):
if var[i].get()==True:
dd=sfile[i]
cmd=f'open ./0_pdf_doc/{dd}.pdf'
subprocess.run(cmd,shell=True)
messagebox.showinfo('showinfo','Work has been completed')
チェックボックスで選択されている pdf ファイルを preview で表示する。私の環境では、初めの数個は、独立したウインドウで表示され、その後はタブ内に表示される。これは preview の設定によるようである。
プログラム全文
import customtkinter as ctk
import tkinter as tk
import numpy as np
import subprocess
import os
import shutil
from tkinter import messagebox
def inp():
fnameR='_flist.txt'
f=open(fnameR,'r')
datalist=f.readlines()
f.close()
sfile=[]
stitle=[]
for text in datalist:
text=text.strip()
text=text.split(',')
sfile.append(text[0])
stitle.append(text[1])
f = open('_tex_pre.txt', 'r')
data = f.read()
f.close()
pre_org=data.split('\n')
return sfile,stitle,pre_org
def tex_pdf(dd):
dir1=f'./{dd}/tex'
os.chdir(dir1)
cmd1='platex tex_test'
cmd2='dvipdfmx tex_test'
subprocess.run(cmd1,shell=True)
subprocess.run(cmd1,shell=True)
subprocess.run(cmd2,shell=True)
os.chdir('../../')
file1=f'{dir1}/tex_test.pdf'
file2=f'./0_pdf_doc/{dd}.pdf'
shutil.copy(file1,file2)
def tex_pre_toc(dlist):
tstr=r'Sugoi Project\\Design Calculation Report of Power Station Civil Structure\\during Construction Stage'
author='Yamada Tarou (x-company)'
date=r'\today'
ss=[]
for text in dlist:
if 0<=text.find('xxxxx'):
text='\\title{'+tstr+'}'
if 0<=text.find('author{}'):
text='\\author{'+author+'}'
if 0<=text.find('date{}'):
text='\\date{'+date+'}'
if 0<=text.find('thispagestyle{empty}'): continue
if 0<=text.find('addtocounter{page}{-1}'): continue
if 0<=text.find('tableofcontents'): continue
if 0<=text.find('pagebreak'): continue
ss.append(text)
s='\n'.join(ss)
dirn='./000_cover_toc'
fnameW=f'./{dirn}/tex/tex_test.tex'
f=open(fnameW,'w')
print(s,file=f)
f.close()
def tex_pre(dirn,tstr,dlist):
ss=[]
for text in dlist:
if 0<=text.find('xxxxx'):
text='\\title{'+tstr+'}'
ss.append(text)
s='\n'.join(ss)
fnameW=f'./{dirn}/tex/tex_test.tex'
f=open(fnameW,'w')
print(s,file=f)
f.close()
def main():
sfile,stitle,pre_org=inp()
n=len(sfile)
# Set appearance mode and default color theme
ctk.set_appearance_mode('dark')
ctk.set_default_color_theme('blue')
# Create the main window
app = ctk.CTk()
app.title('TeX pdf')
fon,fsz='Calibri',20
col='transparent'
frame1=ctk.CTkFrame(app,fg_color=col)
var =[tk.BooleanVar(
value=False) for i in range(n)]
cbox=[ctk.CTkCheckBox(
frame1,
width=200,
height=30,
text=sfile[i],
variable=var[i]
) for i in range(n)]
for i in range(n):
ir,ic=divmod(i,3)
cbox[i].grid(row=ir,column=ic)
frame1.pack(padx=10,pady=10)
def get_value():
tbox.delete('0.0','end')
app.update()
for i in range(n):
if var[i].get()==True:
dlist=pre_org.copy()
dirn=sfile[i]
if i==0:
cmd='python py_toc.py'
subprocess.run(cmd,shell=True)
tex_pre_toc(dlist)
else:
tstr=stitle[i]
tex_pre(dirn,tstr,dlist)
tex_pdf(dirn)
tbox.insert('end',dirn+'\n')
tbox.see('end')
app.update()
messagebox.showinfo('showinfo','Work has been completed')
def can_all():
for i in range(n):
var[i].set(False)
def set_all():
for i in range(n):
var[i].set(True)
def view():
for i in range(n):
if var[i].get()==True:
dd=sfile[i]
cmd=f'open ./0_pdf_doc/{dd}.pdf'
subprocess.run(cmd,shell=True)
messagebox.showinfo('showinfo','Work has been completed')
def close_app():
exit(0)
frame2=ctk.CTkFrame(app,fg_color=col)
button1 = ctk.CTkButton(frame2,text='TeX pdf', width=100,font=(fon,fsz),command=get_value)
button2 = ctk.CTkButton(frame2,text='Clear all',width=100,font=(fon,fsz),command=can_all)
button3 = ctk.CTkButton(frame2,text='Select all',width=100,font=(fon,fsz),command=set_all)
button4 = ctk.CTkButton(frame2,text='View', width=100,font=(fon,fsz),command=view)
button5 = ctk.CTkButton(frame2,text='Exit',width=100,font=(fon,fsz),command=close_app)
button1.grid(row=0,column=0,padx=5, pady=10)
button2.grid(row=0,column=1,padx=5, pady=10)
button3.grid(row=0,column=2,padx=5, pady=10)
button4.grid(row=0,column=3,padx=5, pady=10)
button5.grid(row=0,column=4,padx=5, pady=10)
frame2.pack(fill=ctk.X,padx=10)
def cls():
tbox.delete('0.0','end')
app.update()
frame3=ctk.CTkFrame(app,fg_color=col)
tcol='#aaaaaa'
lb=ctk.CTkLabel(
frame3,
text='(Treated Files)',
font=(fon,fsz),
text_color=tcol,
anchor=ctk.W
)
bt=ctk.CTkButton(
frame3,
text='Clear',
width=100,
font=(fon,fsz),
text_color=tcol,
command=cls
)
ww=300
tbox=ctk.CTkTextbox(
frame3,
width=ww,
height=0.3*ww,
font=('Courier',fsz-2)
)
lb.grid(row=0,column=0)
bt.grid(row=0,column=1)
tbox.grid(row=1,column=0,columnspan=2)
frame3.pack(fill=ctk.X,padx=10,pady=10)
# Run the application
app.mainloop()
#---------------
# Execute
#---------------
if __name__ == '__main__': main()
以 上

