見出し画像

python:三角グラフ (3元系状態図) の作成・点のプロット・パワーポイントへの出力を行うプログラム

初めまして!
大学で材料系の研究をしている神崎です。

「3元系状態図を作成し、適切な位置に点をプロットする」
私はこれを進捗報告スライドの作成時や、組成の分析時に行っています。
が、この作業面倒くさくないですか?

この記事は、そんな面倒くさい作業を効率的に行うためのプログラム紹介です。


状態図の作成と適当な点のプロット / パワーポイントへの出力をするプログラム

このプログラムは三角グラフを作成し、任意の点へプロットを行った後にPowerPointへ編集可能な図形として出力するツールです。
このプログラムを使用することにより、主軸や副軸の間隔, 表示範囲, プロットする点の色や形, 大きさなどを簡単に設定・効率化を図れます。

図1. プログラムの起動画面
図2. プロット時のプレビュー画面(複数操作後)

主な機能(図2参照)

  1. 3元系状態図の作成
    3軸の最大値-最小値を設定し、表示範囲を変更できます。

  2. 主軸/副軸の間隔設定
    主軸/副軸の間隔を変更できます。これにより、10刻みでメモリを振ったり、2刻みで振ったりなど自由に変更可能です。

  3. プロット点の設定
    プロットする点を系列6まで設定可能です。また、各系列は色, 形, 大きさを自由に変更できる為、自分の好みに作成することができます。

  4. プロット点をExcelから読み取り
    Excelから3要素の構成比を読み取る形式な為、値の入力時にExcelの機能(オートフィルなど)を使用可能です。

  5. 編集可能な図形として出力
    線, 点, 文字をPowerPointの図形として出力します。これにより、スライドに使用する際は線の太さや点の色, 大きさ、フォント、入力されている文字などを細かく調整可能です。

  6. その他図形の微調整
    三角形の頂点を任意の座標で切り落とす、軸ラベルの変更、ワンボタンでプロット範囲の最大化、などが可能です。

特徴

  • まとまった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のような画面が出ますので、各軸の最小-最大値, 主軸-副軸の分割数を入力してください。

図3. 試供品の入力例
図4. 出力される図形のイメージ

ここまで読んでくれてありがとうございました。
気に入ったらぜひ完全版をご購入ください。
※Windows以外での動作は保証できません

*以下は完全版(有料)のソースコード及び詳細な使用方法です。


ここから先は

53,389字 / 5画像

¥ 300

この記事が気に入ったらチップで応援してみませんか?