
PythonでChatGPTと一緒に住所録を作ろう:データの読み込み
上の記事での画面の作成に引き続き、住所録のデータを読み込む方法を学びます。
JSONというファイルフォーマットで書かれた住所のデータをデータベースの代わりに読み込みます。
この記事を読むことによって、データベースなどからQTableWidgetへデータを読み込む方法が理解できます。
また、汎用的に使われる軽量なデータフォーマットJSONの読み書きが理解できます。
JSONで取り込んだデータを住所録で扱う方法を学ぶことでデータベースのデータを扱う方法の基礎となります。
表記について
ChatGPTの記述部分への切り替わり
"ChatGPT#####"もしくは "G : "
筆者(ごろう)の記述部分への切り替わり
”ごろう#####”もしくは"ご : "
1.JSONとは
早速始めましょう。
ChatGPTに、「プログラミンでよく使われるJSONとは何ですか」と送信します。
ChatGPT#####(重要部分のみの抜き出し)
JSON(JavaScript Object Notation)は、データを構造化して保存・送受信するための軽量なデータフォーマットです。
もともとはJavaScriptから派生しましたが、現在ではほとんどのプログラミング言語でサポートされています。
主にAPI通信や設定ファイルとして使われることが多いです。
中略…
JSONの基本構造
JSONは、以下の2つのデータ構造を基本にしています:
オブジェクト(key-valueのペア)
波括弧 {} で囲まれ、キーと値がコロン : で区切られます。配列(順序付きの値のリスト)
角括弧 [] で囲まれ、値はカンマ , で区切られます。
ごろう#####
上記のような説明が返ってきました。
だいたいの雰囲気がつかめたでしょうか?
百聞は一見に如かずでサンプルを見れば一発ですね。
[
{
"name": "山田太郎",
"age": 25,
"email": "yamada@example.com",
"skills": ["Python", "JavaScript", "HTML"],
"isEmployed": true,
"address": {
"city": "東京",
"postalCode": "100-0001"
}
},
{
データ2...
},
{
データ3...
}
]
ファイルで保存される場合は、上記のような内容でテキストファイルとして保存されます。
オブジェクト
オブジェクトとは、{} で囲まれたデータで
データは、"名前 : 値" と書かれます。
つまりオブジェクトとは、一塊のデータの集まりですってことです。
データの入れ物と考えることもできます。
値には、文字列、数値、真偽値、配列、オブジェクトが割り当てられます。
配列
さらにオブジェクトなどをまとめる入れ物として配列 [] がありますってことですね。
配列には、オブジェクトや基本データ型(数値や文字列)を格納できます。
データベースとして使う
なので、今回の住所録では一人分のデータをオブジェクトとして
人数分のオブジェクトを配列に格納してデータベースとして使います。
2.JSONデータの読み込み
ChatGPTに「PythonでJSONデータをファイルから読み込む方法を教えて」とプロンプトを送信します。
ChatGPT#####(必要な部分のみ)
PythonでJSONデータをファイルから読み込むには、標準ライブラリのjsonモジュールを使用します。以下に手順を説明します。
JSONデータのファイル作成
まず、以下のようなJSONデータを含むファイル(例: data.json)を作成します。
[
{
"name": "山田太郎",
"age": 25,
"email": "yamada@example.com",
"skills": ["Python", "JavaScript", "HTML"],
"isEmployed": true,
"address": {
"city": "東京",
"postalCode": "100-0001"
}
},
{
"name": "鈴木花子",
"age": 28,
"email": "hanako@example.com",
"skills": ["Ruby", "CSS"],
"isEmployed": false,
"address": {
"city": "大阪",
"postalCode": "530-0001"
}
}
]
PythonでJSONデータを読み込む方法
jsonモジュールを使ってファイルを開き、データを読み込みます。
try:
with open('data.json', 'r', encoding='utf-8') as file:
data = json.load(file)
for person in data:
print(f"名前: {person['name']}, 年齢: {person['age']}")
except FileNotFoundError:
print("エラー: data.json ファイルが見つかりません。")
except json.JSONDecodeError:
print("エラー: JSONデータが正しくありません。")
ごろう#####
上記のコードとサンプルデータが返ってきましたのでテストしてみましょう。
data.jsonと言うファイルを作って、JSONデータのサンプルを張り付けて保存します。
json_test.pyと言うファイルを作ってコードを保存して実行してみましょう。
二つのファイルは、同じフォルダに保存して実行してください。
python json_test.py
結果
名前: 山田太郎, 年齢: 25
名前: 鈴木花子, 年齢: 28
うまく結果は、表示されましたか?
分からないところは、ChatGPTに聞いてみてください。
念のために解説しますと
# JSONファイルを開く
with open('data.json', 'r', encoding='utf-8') as file:
data = json.load(file) # ファイルからJSONを読み込む
data = json.load(file) でJSONデータの内容を読み込んで
JSONオブジェクトを作成しています。
with とは、何なのか。
file とは、なにか。
'r' のほかに何が指定できるか。
encoding='utf-8'とはなにか。
try: ~ except: とはなにか。(ここには、ありませんが上記のコードの外側のコード部分です。)
確認すると良いでしょう。
3.データの構造
本来、仕様として記事の最初に説明すべきでした。
データの項目についてここで説明します。
データの項目は
姓
姓フリガナ
名
名フリガナ
郵便番号
住所
電話番号
備考
とします。
データベースとして考えるとこれにIDがあったほうが良いのですが
今回は、練習用のアプリケーションという事で使わないことにします。
これをJSONで書くと
[
{
"姓": "鈴木",
"姓フリガナ": "スズキ",
"名": "稔",
"名フリガナ": "ミノル",
"郵便番号": "844-9047",
"住所": "香川県君津市台東24丁目6番16号",
"電話番号": "45-7065-8608",
"備考": "感謝芸術家族平和友情"
},
{
"姓": "藤田",
"姓フリガナ": "フジタ",
"名": "淳",
"名フリガナ": "アツシ",
"郵便番号": "160-8650",
"住所": "神奈川県足立区大中32丁目14番6号 所野コーポ882",
"電話番号": "090-8162-3050",
"備考": "夢平和幸福平和安心"
},
中略...
]
となります。
テスト用のデータ
100件分のデータを作りましたのでテスト用に使ってください。
Faker(と言うライブラリを使って)で自動生成したものです。
4.住所録に組み込む
住所録コード(前回の記事で使用した住所録コード)
import sys
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton,
QTableWidget, QTableWidgetItem, QHeaderView, QWidget, QLabel, QDialog, QFormLayout
)
class EditDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("編集画面")
self.setGeometry(200, 200, 400, 300)
layout = QFormLayout()
self.last_name_input = QLineEdit()
self.first_name_input = QLineEdit()
self.last_name_kana_input = QLineEdit()
self.first_name_kana_input = QLineEdit()
self.postal_code_input = QLineEdit()
self.address_input = QLineEdit()
self.phone_number_input = QLineEdit()
self.notes_input = QLineEdit()
layout.addRow("姓", self.last_name_input)
layout.addRow("名", self.first_name_input)
layout.addRow("姓フリガナ", self.last_name_kana_input)
layout.addRow("名フリガナ", self.first_name_kana_input)
layout.addRow("郵便番号", self.postal_code_input)
layout.addRow("住所", self.address_input)
layout.addRow("電話番号", self.phone_number_input)
layout.addRow("備考", self.notes_input)
self.save_button = QPushButton("保存")
self.cancel_button = QPushButton("キャンセル")
button_layout = QHBoxLayout()
button_layout.addWidget(self.save_button)
button_layout.addWidget(self.cancel_button)
layout.addRow(button_layout)
self.setLayout(layout)
class AddressBookApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("住所録")
self.setGeometry(100, 100, 800, 600)
# メインウィジェットとレイアウト
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QVBoxLayout()
main_widget.setLayout(main_layout)
# フィルター入力と実行ボタン
filter_layout = QHBoxLayout()
self.filter_input = QLineEdit()
self.filter_input.setPlaceholderText("フィルターを入力")
self.filter_button = QPushButton("実行")
filter_layout.addWidget(self.filter_input)
filter_layout.addWidget(self.filter_button)
main_layout.addLayout(filter_layout)
# 一覧表
self.table = QTableWidget()
self.table.setColumnCount(8)
self.table.setHorizontalHeaderLabels([
"姓", "姓フリガナ", "名", "名フリガナ", "郵便番号", "住所", "電話番号", "備考"
])
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
main_layout.addWidget(self.table)
# ボタン群
button_layout = QHBoxLayout()
self.new_button = QPushButton("新規作成")
self.edit_button = QPushButton("編集")
self.delete_button = QPushButton("削除")
self.save_button = QPushButton("保存")
self.load_button = QPushButton("読み込み")
button_layout.addWidget(self.new_button)
button_layout.addWidget(self.edit_button)
button_layout.addWidget(self.delete_button)
button_layout.addWidget(self.save_button)
button_layout.addWidget(self.load_button)
main_layout.addLayout(button_layout)
# ボタンの動作
self.new_button.clicked.connect(self.open_edit_dialog)
self.edit_button.clicked.connect(self.open_edit_dialog)
def open_edit_dialog(self):
dialog = EditDialog(self)
dialog.exec()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = AddressBookApp()
window.show()
sys.exit(app.exec())
クラスAddressBookAppのコンストラクタで
JSONデータを読み込むのが適切ですね。
ちょっと疑問が湧いたのでChatGPTにプロンプトを送ります。
「(ソースを与えて)これは住所録のコードです。
住所録のデータとしてJSONデータを読み込むのは、
GUIを組み立てた前と後どちらが妥当ですか?」
ChatGPT#####
JSONデータを読み込む処理を「GUIを組み立てた後」に実行するのが適切です。以下に理由を説明します:
理由 1: GUIが組み上がっていないとデータを反映できない
JSONデータを読み込むと、そのデータをテーブルなどのGUI要素に反映する必要があります。
GUIがまだ組み立てられていない段階でデータを読み込もうとすると、テーブルなどのUI要素が存在しないため、エラーが発生します。
理由 2: 分かりやすいコード構造
GUIの構築をまず行い、その後にデータの読み込みや初期化処理を行うという順序は、アプリケーションの初期化処理を整理しやすくします。
「GUI構築」と「データのロード」を別々に記述することで、後から修正・拡張する際の可読性も向上します。
ごろう#####
大分省略してますが上記の様に返ってきました。
それでは、JSONの読み込み部分を load_data_from_json()という関数にして
コンストラクタの最下行に追加します。
おまけでコンストラクタが長くなってきたので(見通しが悪い)ので
setup_ui()という、UI組み立て用の専用関数に分けて作ってわかりやすくします。
尚、データはテスト用の"address_book.json"を使っています。
import json # ほかのimport文とまとめる
class AddressBookApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("住所録")
self.setGeometry(100, 100, 800, 600)
# GUIを構築
self.setup_ui()
# データを読み込む
self.load_data_from_json("address_book.json") # データ読み込み関数
def setup_ui(self):
"""GUIを構築するメソッド"""
# メインウィジェットとレイアウト
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QVBoxLayout()
main_widget.setLayout(main_layout)
# フィルター入力と実行ボタン
filter_layout = QHBoxLayout()
self.filter_input = QLineEdit()
self.filter_input.setPlaceholderText("フィルターを入力")
self.filter_button = QPushButton("実行")
filter_layout.addWidget(self.filter_input)
filter_layout.addWidget(self.filter_button)
main_layout.addLayout(filter_layout)
# 一覧表
self.table = QTableWidget()
self.table.setColumnCount(8)
self.table.setHorizontalHeaderLabels([
"姓", "姓フリガナ", "名", "名フリガナ", "郵便番号", "住所", "電話番号", "備考"
])
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
main_layout.addWidget(self.table)
# ボタン群
button_layout = QHBoxLayout()
self.new_button = QPushButton("新規作成")
self.edit_button = QPushButton("編集")
self.delete_button = QPushButton("削除")
self.save_button = QPushButton("保存")
self.load_button = QPushButton("読み込み")
button_layout.addWidget(self.new_button)
button_layout.addWidget(self.edit_button)
button_layout.addWidget(self.delete_button)
button_layout.addWidget(self.save_button)
button_layout.addWidget(self.load_button)
main_layout.addLayout(button_layout)
# ボタンの動作
self.new_button.clicked.connect(self.open_edit_dialog)
self.edit_button.clicked.connect(self.open_edit_dialog)
def load_data_from_json(self, file_path): # テスト読み込み関数
"""JSONデータを読み込んでテーブルに反映するメソッド"""
# テスト読み込み
try:
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
print(data) # デバッグprint
except FileNotFoundError:
print("エラー: data.json ファイルが見つかりません。")
except json.JSONDecodeError:
print("エラー: JSONデータが正しくありません。")
def open_edit_dialog(self):
dialog = EditDialog(self)
dialog.exec()
クラスAddressBookApp()を先ほどのテストコードに従って上記の様に書き換えました。
この段階では、load_data_from_json()はテスト用にしか作っていません。
本来は、QTableWidgetへのデータの渡し方を考えて作るのですがまず
ここで確実にデータを読み取れているかをprint()で確かめます。
書き換えたコードを実行してみます。
コンソールにJSONデータがそのまま表示できています。
これでJSONデータを住所録に持ち込むことが出来ました。
5.QTableWidgetに読み込む
まず、ChatGPTにQTableWidgetの使い方を教えてもらいましょう。
「QTableWidgetの使い方を教えて」などとするとサンプルコードとともに基本的な使い方を解説してくれます。
ヘッダーはすでに追加されていますね。
行は、追加することが出来るのでデータを追加するときに行を増やせば良さそうです。
table.setItem(y, x, QTableWidgetItem(データ))とすればデータを追加出来ることが分かりました。
新しいチャットを立ち上げて、address_book.pyの内容を一部コピーしてChatGPTのプロンプトに張り付け以下のプロンプトを送ります。
新しいチャットを立ち上げるのは、返答が今までの内容に合わせてしまい
純粋な質問内容とは違う答えが返ってくるからです。
[
{
"姓": "鈴木",
"姓フリガナ": "スズキ",
"名": "稔",
"名フリガナ": "ミノル",
"郵便番号": "844-9047",
"住所": "香川県君津市台東24丁目6番16号",
"電話番号": "45-7065-8608",
"備考": "感謝芸術家族平和友情"
},
{
"姓": "藤田",
"姓フリガナ": "フジタ",
"名": "淳",
"名フリガナ": "アツシ",
"郵便番号": "160-8650",
"住所": "神奈川県足立区大中32丁目14番6号 所野コーポ882",
"電話番号": "090-8162-3050",
"備考": "夢平和幸福平和安心"
},
]
address_book.json ファイルの内容の一部です。
self.table = QTableWidget()
self.table.setColumnCount(8)
self.table.setHorizontalHeaderLabels([
"姓", "姓フリガナ", "名", "名フリガナ", "郵便番号", "住所", "電話番号", "備考"
])
これを上のコードようなQTableWidgetに取り込むにはどんなコードを書けばいいですか。
そうすると次のような回答が返ってきました。
ChatGPT#####
def load_data_from_json(self, file_path):
# JSONデータの読み込み
with open(file_path, "r", encoding="utf-8") as file:
address_book = json.load(file)
# データをテーブルに追加
self.table.setRowCount(len(address_book)) # 行数を設定
for row, entry in enumerate(address_book):
self.table.setItem(row, 0, QTableWidgetItem(entry.get("姓", "")))
self.table.setItem(row, 1, QTableWidgetItem(entry.get("姓フリガナ", "")))
self.table.setItem(row, 2, QTableWidgetItem(entry.get("名", "")))
self.table.setItem(row, 3, QTableWidgetItem(entry.get("名フリガナ", "")))
self.table.setItem(row, 4, QTableWidgetItem(entry.get("郵便番号", "")))
self.table.setItem(row, 5, QTableWidgetItem(entry.get("住所", "")))
self.table.setItem(row, 6, QTableWidgetItem(entry.get("電話番号", "")))
self.table.setItem(row, 7, QTableWidgetItem(entry.get("備考", "")))
ご#####
うまく行きそうですが後ろのfor文以降が見た目が同じことの繰り返しです。
省略できそうですね。
ファイルが無い場合などのERRORハンドリングも書いておきます。
def load_data_from_json(self, file_path):
try:
# JSONデータの読み込み
with open(file_path, "r", encoding="utf-8") as file:
address_book = json.load(file)
# データをテーブルに追加
self.table.setRowCount(len(address_book)) # 行数を設定
for row, entry in enumerate(address_book):
for col, item in enumerate(["姓", "姓フリガナ", "名", "名フリガナ",
"郵便番号","住所", "電話番号", "備考"]):
cell = QTableWidgetItem(entry.get(item, ""))
self.table.setItem(row, col, cell)
except FileNotFoundError:
print("ファイルが見つかりません")
except json.JSONDecodeError:
print("JSONデータが壊れています")
self.table.setRowCount()でQTableWidgetの行数を設定します。
行数はlen(address_book)、つまりデータの長さを設定します。
enumerateは、for文と一緒に使います。 リストなどがあったときにループの回数に合わせてタプルを作ります。
['a', 'b', 'c'] こんなリストがあったとき、
for t enumerate(['a', 'b', 'c']):
print(t)
# 結果
# (0, 'a')
# (1, 'b')
# (2, 'c')
このようにタプルを返します。
タプルとは、(x, y) のような2要素の組のデータ構造です。
それでは、先ほどaddress_book.jsonから取得したデータを
仮にprint()で出力していた部分を
load_data_from_json()で置き換えましょう。
これでデータをQTableWidgetに読み込めるようになりました。
6.全コード
import sys
import json
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton,
QTableWidget, QTableWidgetItem, QHeaderView, QWidget, QLabel, QDialog, QFormLayout
)
class EditDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("編集画面")
self.setGeometry(200, 200, 400, 300)
layout = QFormLayout()
self.last_name_input = QLineEdit()
self.first_name_input = QLineEdit()
self.last_name_kana_input = QLineEdit()
self.first_name_kana_input = QLineEdit()
self.postal_code_input = QLineEdit()
self.address_input = QLineEdit()
self.phone_number_input = QLineEdit()
self.notes_input = QLineEdit()
layout.addRow("姓", self.last_name_input)
layout.addRow("名", self.first_name_input)
layout.addRow("姓フリガナ", self.last_name_kana_input)
layout.addRow("名フリガナ", self.first_name_kana_input)
layout.addRow("郵便番号", self.postal_code_input)
layout.addRow("住所", self.address_input)
layout.addRow("電話番号", self.phone_number_input)
layout.addRow("備考", self.notes_input)
self.save_button = QPushButton("保存")
self.cancel_button = QPushButton("キャンセル")
button_layout = QHBoxLayout()
button_layout.addWidget(self.save_button)
button_layout.addWidget(self.cancel_button)
layout.addRow(button_layout)
self.setLayout(layout)
class AddressBookApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("住所録")
self.setGeometry(100, 100, 800, 600)
# GUIを構築
self.setup_ui()
# データを読み込む
self.load_data_from_json("address_book.json")
def setup_ui(self):
"""GUIを構築するメソッド"""
# メインウィジェットとレイアウト
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QVBoxLayout()
main_widget.setLayout(main_layout)
# フィルター入力と実行ボタン
filter_layout = QHBoxLayout()
self.filter_input = QLineEdit()
self.filter_input.setPlaceholderText("フィルターを入力")
self.filter_button = QPushButton("実行")
filter_layout.addWidget(self.filter_input)
filter_layout.addWidget(self.filter_button)
main_layout.addLayout(filter_layout)
# 一覧表
self.table = QTableWidget()
self.table.setColumnCount(8)
self.table.setHorizontalHeaderLabels([
"姓", "姓フリガナ", "名", "名フリガナ", "郵便番号", "住所", "電話番号", "備考"
])
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
main_layout.addWidget(self.table)
# ボタン群
button_layout = QHBoxLayout()
self.new_button = QPushButton("新規作成")
self.edit_button = QPushButton("編集")
self.delete_button = QPushButton("削除")
self.save_button = QPushButton("保存")
self.load_button = QPushButton("読み込み")
button_layout.addWidget(self.new_button)
button_layout.addWidget(self.edit_button)
button_layout.addWidget(self.delete_button)
button_layout.addWidget(self.save_button)
button_layout.addWidget(self.load_button)
main_layout.addLayout(button_layout)
# ボタンの動作
self.new_button.clicked.connect(self.open_edit_dialog)
self.edit_button.clicked.connect(self.open_edit_dialog)
def load_data_from_json(self, file_path):
try:
# JSONデータの読み込み
with open(file_path, "r", encoding="utf-8") as file:
address_book = json.load(file)
# データをテーブルに追加
self.table.setRowCount(len(address_book)) # 行数を設定
for row, entry in enumerate(address_book):
for col, item in enumerate(["姓", "姓フリガナ", "名", "名フリガナ",
"郵便番号","住所", "電話番号", "備考"]):
cell = QTableWidgetItem(entry.get(item, ""))
self.table.setItem(row, col, cell)
except FileNotFoundError:
print("ファイルが見つかりません")
except json.JSONDecodeError:
print("JSONデータが壊れています")
def open_edit_dialog(self):
dialog = EditDialog(self)
dialog.exec()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = AddressBookApp()
window.show()
sys.exit(app.exec())
テーブルにデータが読み込まれました。

7.まとめ
以上で住所録にJSONのデータが読み込まれました。
次回は、新規追加や編集などの機能を追加していきます。
お疲れさまでした。