(2024.10.29)コードを修正しました。
(2024.10.31)結局こんな感じになりました、
(2024.11.03)修正版
はじめに
最近PythonのTkinterの使い方を勉強している。そんな中、作ってみたいアプリがあったので、作ってみることにした。
当方の環境は以下の通り。
M1 Pro MacBook Pro 14"
macOS Sequoia 15.0.1
Python 3.13.0
Tcl/Tk 8.6.15
動作写真
実行している画面は、以下の写真の通り。Navy色のセルには初期値を入れてあるが、修正して再計算が可能。所定の数値を入力し、左上のボタンを押すと、別プログラムで計算して出力された jpg 画像が、右側の Sub window に表示される。Sub window に表示されたグラフは名前を付けて保存が可能(実際には別プログラムで出力してある jpg 画像を別名で保存している)。新たにデータを入力してグラフを表示するには、Sub window を閉じる必要がある。
アプリ概要
アプリ構成の概要は以下の通り。
py_rebar_inp.py
とpy_rebar_cal.py
という2つの Python コードから成り立っている。
やっていることは以下の通り。
-
py_rebar_inp.py
を起動し、画面より必要データを入力し、これを csv ファイルとして出力。 -
py_rebar_inp.py
からsubprocess.run
によりpy_rebar_cal.py
を実行。 -
py_rebar_cal.py
では、出力された csv ファイルを読み込み計算処理を行い、jpg イメージのグラフを出力。 - 再び
py_rebar_inp.py
に制御を戻し、sub window で出力された jpg イメージを表示。 -
py_rebar_inp.py
の sub window から画像の保存ができるようにした。filedialog で保存ディレクトリ・ファイル名を指定し、保存が終わったら messagebox で通知する。
プログラムが動くようになったら、Mac の automator で app 化する。
automator は、「automator 起動 => Application 選択 => run shell script 選択 => shell script 記載 => 保存」というステップで app が作成できる。
automator での app 化では、以下に注意。
- 記載するシェルスクリプトの Python のありか、実行するコード共フルパスで記載。
- subprocess で実行するプログラム(
py_rebar_cal.py
)、入出力用の csv ファイル名、jpg 画像ファイル名も、全てコード内にフルパスで記載。
py_rebar_cal.py
でやっている計算はマニアックなものなので説明は省略。
py_rebar_inp.py
の画面作成について、少し解説しておく。画面イメージは以下の通り。
Label および Entry の配置が格子状になっているのが特徴。これをいかに手を抜いて作成するかを少しだけ考えた。方法としては、numpy 配列の dtype=object
を使うことである。
Label 配置領域は、lab
という1次元の numpy 配列を定義し、要素として Label を指定し、grid を用いて row と column を指定して配置する、
Entry 配置領域は、arc
という2次元の numpy 配列を定義し、要素として Entry を指定し、grid を用いて row と column を指定して配置する。Entry を配置しないところには文字列 'na' を入れておき、grid で配置するときは 'na' が入っている要素はなにもせず、for ループを回していく。
arc
配列の4行目は、本来 Label を使うところであるが、ここでは Entry を使い、文字列記入後、readonly
にしている。
なお、jpg を表示するときは、PIL で処理する必要があるらしい。
ちなみに、ここでは、jpg 画像は、Label 上に表示している。
コード
以下に、py_rebar_inp.py
のコードを示す、
import tkinter as tk
import tkinter.font
from tkinter import filedialog
from tkinter import messagebox
from PIL import ImageTk, Image
import numpy as np
import subprocess
def end_q():
quit()
def out_data(dir,arc):
# create data file (csv)
n,m=arc.shape
fcu=float(arc[0,0].get())
fy= float(arc[1,0].get())
bb,hh=float(arc[2,0].get()),float(arc[2,1].get())
tt=[]; nn=[]; cc=[]
for i in range(0,m):
t=float(arc[4,i].get())
n=float(arc[5,i].get())
c=float(arc[6,i].get())
tt.append(t)
nn.append(n)
cc.append(c)
ax,mo=float(arc[7,0].get()),float(arc[7,1].get())
s1='{0:6.0f}'.format(fcu)
s2='{0:6.0f}'.format(fy)
s3='{0:6.0f},{1:6.0f}'.format(bb,hh)
s4='{0:6.0f},{1:6.0f},{2:6.0f},{3:6.0f}'.format(tt[0],tt[1],tt[2],tt[3])
s5='{0:6.0f},{1:6.0f},{2:6.0f},{3:6.0f}'.format(nn[0],nn[1],nn[2],nn[3])
s6='{0:6.0f},{1:6.0f},{2:6.0f},{3:6.0f}'.format(cc[0],cc[1],cc[2],cc[3])
s7='{0:6.1f},{1:6.1f}'.format(ax,mo)
ss=s1+'\n'+s2+'\n'+s3+'\n'+s4+'\n'+s5+'\n'+s6+'\n'+s7
fnameW=dir+'_rebar_inp.csv'
f=open(fnameW,'w')
print(ss,file=f)
f.close()
def calc(dir):
# calculation and drawing
p1='/Users/kk/.pyenv/shims/python'
p2=dir+'py_rebar_cal.py'
cmd='{0} {1}'.format(p1,p2)
subprocess.run(cmd,shell=True)
def show_jpg(sub_win,dir):
def save_image(sub_win,img_org):
save_path=filedialog.asksaveasfilename(initialfile='fig_rebar_xxx.jpg')
if save_path!='': # condition of not cancel
img_org.save(save_path)
s='Image was saved.\n'+save_path+'\nDo you want to close this window?'
if messagebox.askyesno('Close',s):sub_win.destroy()
if sub_win==None or not sub_win.winfo_exists(): # one sub-window
fnameF=dir+'_fig_rebar_tk.jpg'
im_org=Image.open(fnameF)
w,h=im_org.size
px=int(400)
py=int(px*h/w)
im_resize=im_org.resize((px,py))
img = ImageTk.PhotoImage(im_resize)
sub_win=tk.Toplevel()
sxy='{0}x{1}'.format(px,py)
sub_win.title('M-N diagram ({0})'.format(sxy))
label_i=tk.Label(sub_win,image=img)
label_i.pack()
save_button=tk.Button(sub_win,relief='raised',text='Save As...',command=lambda:save_image(sub_win,im_org))
save_button.pack()
sub_win.mainloop()
def b1_click(sub_win,arc):
dir='/Users/kk/stream_deck/'
out_data(dir,arc) # create data file (csv)
calc(dir) # calculation and drawing (jpg)
show_jpg(sub_win,dir) # show jpg image
def ival(arc):
n,m=arc.shape
# set initial values
val=np.array([
[ 30,np.nan,np.nan,np.nan],
[ 460,np.nan,np.nan,np.nan],
[ 1000, 500,np.nan,np.nan],
[np.nan,np.nan,np.nan,np.nan],
[ 25, 0, 0, 25],
[ 5, 0, 0, 5],
[ 100, 300, 300, 100],
[ 0, 255.2,np.nan,np.nan]
],dtype=np.float64)
for i in range(0,n):
for j in range(0,m):
if np.isnan(val[i,j]): continue
arc[i,j].delete(0,tk.END)
if i<n-1: arc[i,j].insert(tk.END,'{0:.0f}'.format(val[i,j]))
else: arc[i,j].insert(tk.END,'{0:.1f}'.format(val[i,j]))
def main():
sub_win=None # difinition of sub_window
root = tk.Tk()
root.title('RC calc (BS 8110-1)')
fn1,fn2='Menlo','Courier new'
fontf=tkinter.font.Font(root,family=fn1,size=14,weight='bold',underline=True)
font1=tkinter.font.Font(root,family=fn1,size=14)
font2=tkinter.font.Font(root,family=fn2,size=17,weight='bold')
frame1=tk.LabelFrame(root, text='(Execution Button)',relief='flat',font=fontf)
button1=tk.Button(frame1,relief='raised',text='Calculation',command=lambda:b1_click(sub_win,arc))
button2=tk.Button(frame1,relief='raised',text='(Initial values)',comman=lambda:ival(arc))
button1.grid(row=0,column=0)
button2.grid(row=0,column=1)
frame1.pack(fill=tk.X)
frame2 = tk.LabelFrame(root,text='(Data Input)',relief='flat',font=fontf)
ss=['Con fcu (MPa)',
'Rebar fy (MPa)',
'b (mm) x h (mm)',
'Reinforcement',
'Bar dia (mm)',
'Bar num (nos)',
'Cover (mm)',
'Design N (kN),M (kN-m)']
lab=np.empty(len(ss),dtype=object)
for i in range(0,len(ss)):
lab[i]=tk.Label(frame2,text=ss[i],font=font2)
lab[i].grid(row=i,column=0)
arc=np.array([
[ 1, 'na', 'na', 'na'],
[ 1, 'na', 'na', 'na'],
[ 1, 1, 'na', 'na'],
['Tens-1','Tens-2','Comp-2','Comp-1'],
[ 1, 1, 1, 1],
[ 1, 1, 1, 1],
[ 1, 1, 1, 1],
[ 1, 1, 'na', 'na']
],dtype=object)
n,m=arc.shape
for i in range(0,n):
for j in range(0,m):
if arc[i,j]=='na': continue
s=arc[i,j]
arc[i,j]=tk.Entry(frame2,justify=tk.CENTER,width=7,bg='#000080',font=font2)
if s!=1:
arc[i,j].insert(tk.END,s)
arc[i,j].configure(state='readonly',bg='#000000')
arc[i,j].grid(row=i,column=j+1)
ival(arc) # set initial values
frame2.pack(fill=tk.BOTH)
frame3 = tk.LabelFrame(root,text='(Note)',relief='flat',font=fontf)
s1='+ Close M-N diagram for next calculation.'
s2='+ Tens-1 & Tens-2 are tensile bars.'
s3='+ Comp-1 & Comp-2 are compressive bars.'
s4='+ Cover is distance from edge to rebar center.'
ss=s1+'\n'+s2+'\n'+s3+'\n'+s4
label1=tk.Label(frame3,text=ss,justify=tk.LEFT,font=font1)
label1.grid(columnspan=2,row=1,column=0)
frame3.pack(fill=tk.X)
q_button=tk.Button(root,text='Quit',command=end_q)
q_button.pack()
root.mainloop()
if __name__ == '__main__': main()
以 上