takuroooのブログ

勉強したこととか

QTreeWidgetを使ってExifViewerを作る

前回紹介したQt for PythonのQTreeWidgetを使って簡単なExifViewerを作ってみる。
コード全体(60行くらい)は最後に記載。

takuroooooo.hatenablog.com

目次

全体構成

構成は下記図のような感じ。
入力はJPEGファイルのみ対応。

f:id:takuroooooo:20200113230959p:plain

ExifViewer完成画面

今回作るExifViewerの画面。
GUIはQt for Pythonで作る。

f:id:takuroooooo:20200113224124p:plain

f:id:takuroooooo:20200113224003p:plain

f:id:takuroooooo:20200113223957p:plain

以下コードの簡単な説明をしていく。

処理手順

1.JPEGからExifを取得する

まずPILを使ってJPEGの中にあるExif情報を取り出す。
PILはpip install pillowでインストールできる。

Exif情報は_getexif()を使うとExif情報が辞書形式で取得できる。

from PIL import Image
exif = Image.open(img_path)._getexif()

exif変数は辞書になっていて、{tag_id: value}の形式になっている。exifはどんな情報なのかを識別するためのtag_idがあらかじめ用意されている。tag_idは16バイトの数字で定義されている。

www.vieas.com

viewerで表示するときはtag_idだと何の情報なのかよくわからないので、人間が分かるtag_nameに変換する。
これはfrom PIL.ExifTags import TAGS, GPSTAGSを使うと簡単に変換できる。

下記コードはtag_idとtag_nameとtagの値を出力するコード。

from PIL.ExifTags import TAGS, GPSTAGS
for tag_id, value in exif.items():
    tag_name = TAGS[tag_id]
    print(tag_id, tag_name, value)

これを実行すると以下のような出力が得られる。

36864 ExifVersion b'0220'
37121 ComponentsConfiguration b'\x01\x02\x03\x00'
40960 FlashPixVersion b'0100'
36867 DateTimeOriginal 2008:10:22 16:28:39
36868 DateTimeDigitized 2008:10:22 16:28:39
・
・
34853 GPSInfo {1: 'N', 2: ((43, 1), (28, 1), ....
・
・

GPSTAGSExif情報の中にあるGPS情報に対して使う。GPS情報はtag_nameがGPSInfoの中にある。GPSInfo{tag_id: value}という形式の値を持っているので、同じようにGPSTAGS[tag_id]とすればtag_nameが取り出せる。

今回はExif情報とGPS情報を親ツリーとして表示したいので、まずはExif情報からGPS情報だけを取り出す関数をつくる。こうしておくことで後々のコードをわかりやすく書ける。

def extract_gps_info(exif):
    for exif_id, exif_value in exif.items():
        exif_tag = TAGS.get(exif_id, str(exif_id))
        if exif_tag == 'GPSInfo':
            del exif[exif_id] # Exif情報からGPS情報を削除する.
            return exif_value # GPS情報をリターンする.
    return None

exif = Image.open(img_path)._getexif()
gps = extract_gps_info(exif)

2.Exif情報を使ってTreeを生成する

{tag_id: value}形式のExif情報とGPS情報が取得できたので、次にこの情報を使ってTreeを作成する。
Treeの生成はQTreeWidget()QTreeWidgetItemを使う。 今回はcreate_top_level_itemという関数を作って、Exif情報のツリーとGPS情報のツリーを生成する。
この関数でリターンしたQTreeWidgetItemQTreeWidget.addTopLevelItem()の引数にすることでTreeに登録している。

def create_top_level_item(top_name, id_val, id_to_name):
    top_level_item = QTreeWidgetItem([top_name])  # 親ツリーアイテム生成

    for tag_id, val in id_val.items():
        tag_name = id_to_name.get(tag_id, str(tag_id))
        child_item = QTreeWidgetItem([tag_name, str(val)])  # 子ツリーアイテム生成
        top_level_item.addChild(child_item)  # 親に子を追加

    return top_level_item

qw_tree = QTreeWidget()
qw_tree.resize(500, 500)
qw_tree.setAlternatingRowColors(True)
qw_tree.setHeaderLabels(["name", "val"])

# Exif情報のツリーをつくる.
top_level_item = create_top_level_item('exif', exif, TAGS)
qw_tree.addTopLevelItem(top_level_item)

# GPS情報のツリーをつくる.
if gps is not None:
    top_level_item = create_top_level_item('gps', gps, GPSTAGS)
    qw_tree.addTopLevelItem(top_level_item)

全体コード

import sys
from PySide2.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS


def extract_gps_info(exif):
    for exif_id, exif_value in exif.items():
        exif_tag = TAGS.get(exif_id, str(exif_id))
        if exif_tag == 'GPSInfo':
            del exif[exif_id]  # Exif情報からGPS情報を削除する.
            return exif_value  # GPS情報をリターンする.
    return None


def create_top_level_item(top_name, id_val, id_to_name):
    top_level_item = QTreeWidgetItem([top_name])  # 親ツリーアイテム生成

    for tag_id, val in id_val.items():
        tag_name = id_to_name.get(tag_id, str(tag_id))
        child_item = QTreeWidgetItem([tag_name, str(val)])  # 子ツリーアイテム生成
        top_level_item.addChild(child_item)  # 親に子を追加

    return top_level_item


def main(img_path):
    app = QApplication([])

    exif = Image.open(img_path)._getexif()
    # for tag_id, value in exif.items():
    #     print(tag_id, TAGS[tag_id], value)
    gps = extract_gps_info(exif)

    qw_tree = QTreeWidget()
    qw_tree.resize(500, 500)
    qw_tree.setAlternatingRowColors(True)
    qw_tree.setHeaderLabels(["name", "val"])
    
    # Exif情報のツリーをつくる.
    top_level_item = create_top_level_item('exif', exif, TAGS)
    qw_tree.addTopLevelItem(top_level_item)

    # GPS情報のツリーをつくる.
    if gps is not None:
        top_level_item = create_top_level_item('gps', gps, GPSTAGS)
        qw_tree.addTopLevelItem(top_level_item)

    # qw_tree.expandAll()
    qw_tree.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    if len(sys.argv) != 2:
        sys.exit()
    img_path = sys.argv[1]
    main(img_path)