python:三角グラフ (3元系状態図) の作成・点のプロット・パワーポイントへの出力を行うプログラム
初めまして!
大学で材料系の研究をしている神崎です。
「3元系状態図を作成し、適切な位置に点をプロットする」
私はこれを進捗報告スライドの作成時や、組成の分析時に行っています。
が、この作業面倒くさくないですか?
この記事は、そんな面倒くさい作業を効率的に行うためのプログラム紹介です。
状態図の作成と適当な点のプロット / パワーポイントへの出力をするプログラム
このプログラムは三角グラフを作成し、任意の点へプロットを行った後にPowerPointへ編集可能な図形として出力するツールです。
このプログラムを使用することにより、主軸や副軸の間隔, 表示範囲, プロットする点の色や形, 大きさなどを簡単に設定・効率化を図れます。
主な機能(図2参照)
3元系状態図の作成:
3軸の最大値-最小値を設定し、表示範囲を変更できます。主軸/副軸の間隔設定:
主軸/副軸の間隔を変更できます。これにより、10刻みでメモリを振ったり、2刻みで振ったりなど自由に変更可能です。プロット点の設定:
プロットする点を系列6まで設定可能です。また、各系列は色, 形, 大きさを自由に変更できる為、自分の好みに作成することができます。プロット点をExcelから読み取り:
Excelから3要素の構成比を読み取る形式な為、値の入力時にExcelの機能(オートフィルなど)を使用可能です。編集可能な図形として出力:
線, 点, 文字をPowerPointの図形として出力します。これにより、スライドに使用する際は線の太さや点の色, 大きさ、フォント、入力されている文字などを細かく調整可能です。その他図形の微調整:
三角形の頂点を任意の座標で切り落とす、軸ラベルの変更、ワンボタンでプロット範囲の最大化、などが可能です。
特徴
まとまったGUI設計:
主に使用する設定を1つの画面にまとめることで、直感的な操作が可能。柔軟なファイル管理:
読み込むExcel、出力するPowerPointは名前, 保存場所を自由に設定可能。PowerPointとして出力:
しつこいようですがこれが一番の特徴。他のソフト/コードではプロットは出来ても、編集可能な図形としての出力はできないでしょう。
このプログラムは研究者(主に学生)など、3軸グラフを作成する必要がある方々に特に有用です。プロットプレビューやPowerPointでの出力を活用することで、図形作成にかけていた時間と労力を大幅に削減できます。
試供品のコード
以下のコードは試供品です。
状態図をPowerPointへ出力する部分のみ実装されています。
このコードにて当コンテンツ最大の特徴である「編集可能な図形を出力」の部分を体験し、魅力的でしたら有料版の完全なコードをご購入ください。
※このNote上のコードは全てpython ver3.11.1にて開発しています。
Excel、PowerPointはMicrosoft 365のものを使用しています。
Windows以外での動作は保証できません
1.python-pptxをインストールしてください。
pip install python-pptx
2.pythonで任意のファイル名(例:状態図.py)に以下のソースコードを貼り付けてください。
import tkinter as tk
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.shapes import MSO_CONNECTOR
from pptx.dml.color import RGBColor
from pptx.enum.dml import MSO_LINE_DASH_STYLE
# 軸の設定
def add_line(slide, start_x, start_y, end_x, end_y, color=RGBColor(200, 200, 200), width=Pt(0.5), dash_style=MSO_LINE_DASH_STYLE.DASH):
line = slide.shapes.add_connector(
MSO_CONNECTOR.STRAIGHT,
Inches(start_x), Inches(start_y),
Inches(end_x), Inches(end_y)
)
line.line.color.rgb = color
line.line.width = width
line.line.dash_style = dash_style
line.shadow.inherit = False
return line
# 文字の設定
def add_text_box(slide, x, y, text, font_size=20, rotation=0):
txBox = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(1), Inches(0.3))
tf = txBox.text_frame
tf.text = text
tf.paragraphs[0].font.size = Pt(font_size)
txBox.rotation = rotation
# 状態図の作成
def create_ternary_diagram(y_min, y_max, x_min, x_max, z_min, z_max, main_steps, sub_steps):
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
# 三角形の頂点座標(インチ単位)
top = (5, 1)
left = (1, 8)
right = (9, 8)
# 三角形の外枠を描画
add_line(slide, *left, *right, color=RGBColor(0, 0, 0), width=Pt(2), dash_style=MSO_LINE_DASH_STYLE.SOLID)
add_line(slide, *left, *top, color=RGBColor(0, 0, 0), width=Pt(2), dash_style=MSO_LINE_DASH_STYLE.SOLID)
add_line(slide, *right, *top, color=RGBColor(0, 0, 0), width=Pt(2), dash_style=MSO_LINE_DASH_STYLE.SOLID)
# 副軸を描画
grid_color = RGBColor(200, 200, 200)
grid_width = Pt(0.5)
grid_dash_style = MSO_LINE_DASH_STYLE.DASH
for i in range(1, sub_steps):
# 左から右へ
start = (left[0] + (right[0] - left[0]) * i / (sub_steps), left[1])
end = (top[0] - (top[0] - left[0]) * (sub_steps - i) / (sub_steps),
top[1] + (left[1] - top[1]) * (sub_steps - i) / (sub_steps))
add_line(slide, *start, *end, color=grid_color, width=grid_width, dash_style=grid_dash_style)
# 右から左へ
start = (right[0] - (right[0] - left[0]) * i / (sub_steps), right[1])
end = (top[0] + (right[0] - top[0]) * (sub_steps - i) / (sub_steps),
top[1] + (left[1] - top[1]) * (sub_steps - i) / (sub_steps))
add_line(slide, *start, *end, color=grid_color, width=grid_width, dash_style=grid_dash_style)
# 底辺に平行
start = (left[0] + (top[0] - left[0]) * i / (sub_steps),
left[1] + (top[1] - left[1]) * i / (sub_steps))
end = (right[0] - (right[0] - top[0]) * i / (sub_steps),
right[1] + (top[1] - right[1]) * i / (sub_steps))
add_line(slide, *start, *end, color=grid_color, width=grid_width, dash_style=grid_dash_style)
# 主軸を描画
grid_color = RGBColor(100, 100, 100)
grid_width = Pt(1)
grid_dash_style = MSO_LINE_DASH_STYLE.DASH
for i in range(1, main_steps):
# 左から右へ
start = (left[0] + (right[0] - left[0]) * i / main_steps, left[1])
end = (top[0] - (top[0] - left[0]) * (main_steps - i) / main_steps,
top[1] + (left[1] - top[1]) * (main_steps - i) / main_steps)
add_line(slide, *start, *end, color=grid_color, width=grid_width, dash_style=grid_dash_style)
# 右から左へ
start = (right[0] - (right[0] - left[0]) * i / main_steps, right[1])
end = (top[0] + (right[0] - top[0]) * (main_steps - i) / main_steps,
top[1] + (left[1] - top[1]) * (main_steps - i) / main_steps)
add_line(slide, *start, *end, color=grid_color, width=grid_width, dash_style=grid_dash_style)
# 底辺に平行
start = (left[0] + (top[0] - left[0]) * i / main_steps,
left[1] + (top[1] - left[1]) * i / main_steps)
end = (right[0] - (right[0] - top[0]) * i / main_steps,
right[1] + (top[1] - right[1]) * i / main_steps)
add_line(slide, *start, *end, color=grid_color, width=grid_width, dash_style=grid_dash_style)
# 目盛りを追加
for i in range(0, main_steps + 1):
ratio = i / main_steps
y_value = y_max - (y_max - y_min) * ratio
x_value = x_min + (x_max - x_min) * ratio
z_value = z_min + (z_max - z_min) * ratio
# 左軸 (Y軸)
x = left[0] + (top[0] - left[0]) * ratio - 0.6
y = left[1] + (top[1] - left[1]) * ratio - 0.5
add_text_box(slide, x, y, f"{y_value:.1f}", rotation=300)
# 右軸 (X軸)
x = right[0] + (top[0] - right[0]) * ratio - 0.1
y = right[1] + (top[1] - right[1]) * ratio
add_text_box(slide, x, y, f"{x_value:.1f}", rotation=60)
# 底辺 (Z軸)
x = left[0] + (right[0] - left[0]) * ratio - 0.2
y = left[1] + 0.1
add_text_box(slide, x, y, f"{z_value:.1f}")
prs.save('状態図.pptx')
def show_gui():
def on_submit():
y_min = float(y_min_entry.get())
y_max = float(y_max_entry.get())
x_min = float(x_min_entry.get())
x_max = float(x_max_entry.get())
z_min = float(z_min_entry.get())
z_max = float(z_max_entry.get())
main_steps = int(main_steps_entry.get())
sub_steps = int(sub_steps_entry.get())
create_ternary_diagram(y_min, y_max, x_min, x_max, z_min, z_max, main_steps, sub_steps)
root = tk.Tk()
root.title("Ternary Diagram Range Input")
tk.Label(root, text="左軸最小値:").grid(row=0, column=0)
y_min_entry = tk.Entry(root)
y_min_entry.grid(row=0, column=1)
tk.Label(root, text="左軸最大値:").grid(row=1, column=0)
y_max_entry = tk.Entry(root)
y_max_entry.grid(row=1, column=1)
tk.Label(root, text="右軸最小値:").grid(row=2, column=0)
x_min_entry = tk.Entry(root)
x_min_entry.grid(row=2, column=1)
tk.Label(root, text="右軸最大値:").grid(row=3, column=0)
x_max_entry = tk.Entry(root)
x_max_entry.grid(row=3, column=1)
tk.Label(root, text="底軸最小値:").grid(row=4, column=0)
z_min_entry = tk.Entry(root)
z_min_entry.grid(row=4, column=1)
tk.Label(root, text="底軸最大値:").grid(row=5, column=0)
z_max_entry = tk.Entry(root)
z_max_entry.grid(row=5, column=1)
tk.Label(root, text="主軸の分割数:").grid(row=6, column=0)
main_steps_entry = tk.Entry(root)
main_steps_entry.grid(row=6, column=1)
tk.Label(root, text="副軸の分割数:").grid(row=7, column=0)
sub_steps_entry = tk.Entry(root)
sub_steps_entry.grid(row=7, column=1)
submit_button = tk.Button(root, text="出力", command=on_submit)
submit_button.grid(row=8, columnspan=2)
root.mainloop()
show_gui()
3.実際に動かすと図3のような画面が出ますので、各軸の最小-最大値, 主軸-副軸の分割数を入力してください。
ここまで読んでくれてありがとうございました。
気に入ったらぜひ完全版をご購入ください。
※Windows以外での動作は保証できません
*以下は完全版(有料)のソースコード及び詳細な使用方法です。
ここから先は
¥ 300
この記事が気に入ったらチップで応援してみませんか?