"""
XMLファイルを読み込み、TkinterのGUIツリービューで表示するスクリプト。
概要:
このスクリプトは、指定されたXMLファイルの内容を読み込み、
それをグラフィカルユーザーインターフェース(GUI)のツリービュー形式で表示します。
ユーザーはXMLファイルの構造を視覚的に探索できます。
詳細説明:
- コマンドライン引数またはTOML形式のINIファイルから、XMLファイルのパスとウィンドウサイズを読み込みます。
- `xml.etree.ElementTree` モジュールを使用してXMLファイルを解析し、その構造を再帰的に走査します。
- Tkinterとttk(themed Tkinter)を使用して、解析されたXML要素を階層的なTreeviewウィジェットに表示します。
- ウィンドウを閉じる際に、現在のウィンドウのジオメトリをINIファイルに保存する機能を提供し、
次回の起動時に前回の設定を復元できるようにします。
使用方法:
python xml_viewer.py [XMLファイルパス] [ウィンドウサイズ (例: 800x600)]
関連リンク:
:doc:`XMLView_usage`
"""
import os
import sys
import toml
import xml.etree.ElementTree as ET
import tkinter as tk
from tkinter import ttk
if len(sys.argv) <= 1:
print("Usage: python xml_viewer.py infile window_size")
sys.exit(1)
infile = '../XML/vasprun.xml'
geometry = '800x600'
inifile = os.path.splitext(sys.argv[0])[0] + ".ini"
if os.path.isfile(inifile):
geometry = toml.load(inifile).get("geometry", geometry)
nargs = len(sys.argv)
if nargs >= 2:
infile = sys.argv[1]
if nargs >= 3:
geometry = sys.argv[2]
[ドキュメント]
class XMLViewer:
"""
XMLデータをTkinterのTreeviewウィジェットに表示するためのビューアクラス。
概要:
XMLファイルを読み込み、その内容をTkinterのTreeviewウィジェットに階層的に表示します。
詳細説明:
このクラスは、Tkinterのルートウィンドウを受け取り、XMLファイルのパスと初期ウィンドウサイズに基づいて、
Treeviewウィジェットを作成し、XML構造を解析して表示します。
XML要素はタグ、テキスト、属性に基づいてTreeviewノードとして追加されます。
"""
def __init__(self, root: tk.Tk, infile: str, geometry: str = '800x600', tree_open: bool = True):
"""
XMLViewerクラスのコンストラクタ。GUIの初期設定とXMLファイルの読み込みを行います。
概要:
Tkinterのルートウィンドウを設定し、Treeviewウィジェットを作成。
指定されたXMLファイルを読み込み、その内容をTreeviewに表示します。
:param root: tk.Tk - Tkinterのルートウィンドウオブジェクト。
:param infile: str - 読み込むXMLファイルのパス。
:param geometry: str - ウィンドウの初期サイズと位置を指定する文字列(例: '800x600')。デフォルトは '800x600'。
:param tree_open: bool - Treeviewの初期状態でノードを開くかどうか。Trueの場合、特定のレベルまで開く。
"""
self.tree_open = tree_open
self.root = root
self.root.title(f"XML Viewer - {infile}")
self.root.geometry(geometry)
self.treeview = ttk.Treeview(self.root)
self.treeview.heading('#0', text = infile)
self.treeview.pack(fill = 'both', expand = True)
self.read_xml(infile)
[ドキュメント]
def read_xml(self, infile: str):
"""
指定されたXMLファイルを解析し、Treeviewに表示するための準備を行う。
概要:
`xml.etree.ElementTree` を使用してXMLファイルを解析し、ルート要素を取得。
その後、`display_element` メソッドを呼び出してTreeviewに内容を再帰的に表示します。
詳細説明:
ファイルが存在しない場合やXML解析エラーが発生した場合は、
エラーメッセージをコンソールとTreeviewに表示し、アプリケーションを終了します。
正常に解析された場合、XMLのルート要素から表示を開始します。
:param infile: str - 読み込むXMLファイルのパス。
:returns: None
"""
if not os.path.isfile(infile):
print(f"\nError: Can not find [{infile}]")
input("Press ENTER to terminate>>\n")
exit()
try:
self.etree = ET.parse(infile)
self.root_element = self.etree.getroot()
self.display_element(self.root_element, '')
except ET.ParseError as e:
self.treeview.insert("", "end", text="Error", values = (str(e)))
[ドキュメント]
def display_element(self, element: ET.Element, item_id: str = '', level: int = 0):
"""
XML要素を再帰的にTkinterのTreeviewウィジェットに表示する。
概要:
指定されたXML要素のタグ、テキスト、属性をTreeviewのノードとして追加し、
子要素があればそれらも再帰的に表示します。
詳細説明:
ノードの表示テキストは、`name` 属性が存在すればそれを優先し、次に要素のテキスト、
最後にタグ名を使用します。
レベル1までのノードは初期状態で開き、それ以降のレベルは閉じられた状態で挿入されます。
各要素に対して、そのタグ、テキスト、属性が詳細として子ノードに追加されます。
属性はさらに個別のキーと値のペアとして表示されます。
:param element: ET.Element - 表示するXML要素。
:param item_id: str - 親となるTreeviewアイテムのID。ルート要素の場合は空文字列。
:param level: int - 現在の要素の深さ(ネストレベル)。
:returns: None
"""
if level <= 1:
tree_open = self.tree_open
else:
tree_open = False
text = None
if element.attrib.get('name', None) is not None:
text = element.attrib["name"]
if element.text is not None:
if text is None:
text = element.text.strip()
else:
text += f":{element.text.strip()}"
if text is None or text == '':
text = element.tag.strip()
if text is None or text == '':
text = 'root'
child_item = self.treeview.insert(item_id, "end", text = text, open = tree_open)
self.treeview.insert(child_item, "end", text = f"tag: {element.tag.strip()}", open = tree_open)
if element.text is not None:
self.treeview.insert(child_item, "end", text = f"text: {element.text.strip()}")
self.treeview.insert(child_item, "end", text = f"attrib: {element.attrib}", open = tree_open)
for key, value in element.attrib.items():
self.treeview.insert(child_item, "end", text = f"{key}: {value}", open = tree_open)
for child in element.findall('*'):
self.display_element(child, child_item, level = level + 1)
[ドキュメント]
def window_close(root: tk.Tk):
"""
ウィンドウが閉じられたときに呼び出され、現在のウィンドウのジオメトリをINIファイルに保存する。
概要:
Tkinterウィンドウが閉じられる際に実行され、現在のウィンドウのサイズと位置をTOML形式のINIファイルに保存します。
詳細説明:
この関数は、`WM_DELETE_WINDOW` プロトコルハンドラとして設定されます。
ウィンドウの現在のジオメトリ情報を取得し、`inifile` で指定されたパスにあるINIファイルに
`geometry` キーとして書き込みます。その後、アプリケーションを終了します。
:param root: tk.Tk - Tkinterのルートウィンドウオブジェクト。
:returns: None
"""
with open(inifile, "w") as file:
toml.dump({"geometry": root.geometry()}, file)
root.quit()
[ドキュメント]
def main():
"""
アプリケーションのエントリポイント。TkinterウィンドウとXMLViewerインスタンスを作成し、GUIイベントループを開始する。
概要:
XMLビューアアプリケーションを初期化し、実行します。
詳細説明:
Tkinterのルートウィンドウを初期化し、コマンドライン引数やINIファイルから取得した設定(XMLファイルパス、ウィンドウサイズ)に基づいて
`XMLViewer` インスタンスを作成します。
ウィンドウが閉じられる際のプロトコルハンドラとして `window_close` 関数を設定し、
GUIイベントループを開始してアプリケーションをユーザー操作可能にします。
:returns: None
"""
root = tk.Tk()
app = XMLViewer(root, infile, geometry, tree_open = True)
root.protocol("WM_DELETE_WINDOW", lambda: window_close(root))
root.mainloop()
if __name__ == "__main__":
main()