"""
XMLファイルを閲覧・編集するためのTkinterアプリケーションを提供します。
このスクリプトは、指定されたXMLファイルをGUIで表示し、
ユーザーが内容を編集して保存できる機能を提供します。
VASPのvasprun.xmlのような構造化されたXMLファイルの視覚的な確認や修正に適しています。
:doc:`XML_Edit_usage`
"""
import sys
import tkinter as tk
from tkinter import ttk, messagebox
import xml.etree.ElementTree as ET
file_path = '../XML/vasprun.xml'
window_size = '800x600'
nargs = len(sys.argv)
if nargs >= 2:
file_path = sys.argv[1]
if nargs >= 3:
window_size = sys.argv[2]
[ドキュメント]
class XMLViewerEditor:
"""
XMLファイルをGUIで表示し、編集機能を提供するアプリケーションクラスです。
Tkinterを使用してXMLドキュメントのツリー構造を表示し、
ユーザーがその内容をテキストエリアで直接編集し、変更を適用できるようにします。
"""
def __init__(self, root, file_path, window_size):
"""
XMLViewerEditorクラスの新しいインスタンスを初期化します。
GUIのルートウィンドウ、Treeview、テキストエディタ、およびボタンを設定し、
指定されたXMLファイルをロードして表示します。
:param root: Tkinterアプリケーションのルートウィンドウインスタンス。
:type root: tk.Tk
:param file_path: ロードするXMLファイルのパス。
:type file_path: str
:param window_size: GUIウィンドウの初期サイズ(例: '800x600')。
:type window_size: str
"""
self.root = root
self.root.title("XML Viewer and Editor")
self.root.geometry(window_size)
self.tree = ttk.Treeview(self.root)
self.tree.heading('#0', text='Element')
self.tree.pack(fill='both', expand=True)
self.load_xml(file_path)
self.text_editor = tk.Text(self.root, wrap="none")
self.text_editor.pack(fill="both", expand=True)
self.text_editor.grid_remove()
self.edit_button = tk.Button(self.root, text="Edit XML", command=self.toggle_edit_mode)
self.edit_button.pack()
[ドキュメント]
def load_xml(self, file_path):
"""
指定されたXMLファイルを解析し、Treeviewに表示するための準備を行います。
ファイルが見つからない、またはXMLとして不正な場合にエラーメッセージを表示します。
:param file_path: ロードするXMLファイルのパス。
:type file_path: str
"""
try:
tree = ET.parse(file_path)
self.root_element = tree.getroot()
self.tree.delete(*self.tree.get_children()) # 既存のTreeviewの内容をクリア
self.display_element(self.root_element, "")
except ET.ParseError as e:
messagebox.showerror("Error", f"XMLファイルの解析エラー: {e}")
except FileNotFoundError:
messagebox.showerror("Error", f"ファイルが見つかりません: {file_path}")
except Exception as e:
messagebox.showerror("Error", f"予期せぬエラーが発生しました: {e}")
[ドキュメント]
def display_element(self, element, parent_id):
"""
XML要素とその子要素を再帰的にTreeviewウィジェットに表示します。
要素のタグと属性がTreeviewのアイテムとして挿入されます。
:param element: 表示するXML要素オブジェクト。
:type element: xml.etree.ElementTree.Element
:param parent_id: Treeview内で現在の要素の親となるアイテムのID。
:type parent_id: str
"""
item = self.tree.insert(parent_id, "end", text=element.tag)
for key, value in element.attrib.items():
self.tree.insert(item, "end", text=f"{key}: {value}")
if element.text and element.text.strip():
self.tree.insert(item, "end", text=f"Text: {element.text.strip()}")
for child in element:
self.display_element(child, item)
[ドキュメント]
def toggle_edit_mode(self):
"""
XML表示モードと編集モードを切り替えます。
現在編集モードの場合、テキストエディタの内容を解析してXMLツリーを更新し、
表示モードに戻ります。XMLが不正な場合はエラーを表示します。
現在表示モードの場合、現在のXMLツリーを文字列としてテキストエディタに表示し、
編集モードに切り替えます。
ボタンのテキストも適宜「Edit XML」と「Save XML」に切り替わります。
"""
if self.text_editor.winfo_viewable():
# 編集モードから表示モードへ切り替え
edited_xml = self.text_editor.get("1.0", "end-1c") # 最後の改行文字を除外
try:
new_root = ET.fromstring(edited_xml)
self.root_element = new_root
self.tree.delete(*self.tree.get_children())
self.display_element(self.root_element, "")
self.text_editor.grid_remove()
self.edit_button.config(text="Edit XML")
except ET.ParseError as e:
messagebox.showerror("Error", f"XML形式が不正です: {e}")
else:
# 表示モードから編集モードへ切り替え
# Pretty printにする場合は ET.tostring() に encoding='unicode', pretty_print=True を指定
# しかし、ElementTreeのtostringにはpretty_print引数はないため、手動で整形するか別のライブラリを使う
# ここでは元のコードの挙動を維持し、整形せずにそのまま表示する
xml_string = ET.tostring(self.root_element, encoding='utf-8').decode('utf-8')
self.text_editor.delete("1.0", "end")
self.text_editor.insert("1.0", xml_string)
self.text_editor.grid()
self.edit_button.config(text="Save XML")
if __name__ == "__main__":
root = tk.Tk()
app = XMLViewerEditor(root, file_path, window_size)
root.mainloop()