[AI] LlamaIndexアプリ製作 その4 完成 PyQt6

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

LlamaIndexアプリを完成させました。

exeファイルやappファイルには出来ませんでしたが、シェルスクリプトにするとアプリ風になるのでこれでけりを付けました。

ChatGPT APIを使い出して1ヶ月と1週が経過し、私自身だいぶ落ち着きを取り戻しつつあります。

import os, logging, sys
from pathlib import Path
from PyQt6.QtWidgets import QLabel,QWidget,QApplication,QTextEdit,QLineEdit,QPushButton
from PyQt6.QtCore import Qt
from llama_index import download_loader,LLMPredictor, GPTSimpleVectorIndex, ServiceContext
from langchain import OpenAI
import datetime

class LlamaIndex(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("LlamaIndex")
        self.setGeometry(100,100,480,480)
        self.setStyleSheet('background-color: #483D8B')
        self.setAcceptDrops(True)
        
        file = QLabel('FILE',self)
        file.setGeometry(10,15,34,16)
        file.setStyleSheet('color: #FFFFFF; font-size: 14pt;')
        file.setAlignment(Qt.AlignmentFlag.AlignCenter)
        
        self.input= QLineEdit('',self)
        self.input.setGeometry(55,10,355,25)
        self.input.setAcceptDrops(True)
        
        idxBtn = QPushButton('IDX作成',self)
        idxBtn.setGeometry(420,10,50,25)
        idxBtn.setStyleSheet('color: #FFFFFF; font-size: 12pt;')
        idxBtn.released.connect(self.makeIDX)
        
        idx = QLabel('IDX',self)
        idx.setGeometry(10,45,26,16)
        idx.setStyleSheet('color: #FFFFFF; font-size: 14pt;')
        idx.setAlignment(Qt.AlignmentFlag.AlignCenter)
        
        self.input2= QLineEdit('',self)
        self.input2.setGeometry(55,40,355,25)
        self.input2.setAcceptDrops(True)
        
        self.idxBtn2 = QPushButton('IDX読込',self)
        self.idxBtn2.setGeometry(420,40,50,25)
        self.idxBtn2.setStyleSheet('color: #FFFFFF; font-size: 12pt;')
        self.idxBtn2.released.connect(self.loadIDX)
        
        send = QPushButton('送信',self)
        send.setGeometry(420,70,50,25)
        send.setStyleSheet('color: #FFFFFF; font-size: 14pt;')
        send.released.connect(self.sendQuestion)
        
        clear = QPushButton('CL',self)
        clear.setGeometry(420,100,50,25)
        clear.setStyleSheet('color: #FFFFFF; font-size: 14pt;')
        clear.released.connect(self.clear)
            
        self.questionInput = QTextEdit('',self)
        self.questionInput.setGeometry(10,70,400,95)
        self.questionInput.setAcceptDrops(False)
        
        self.output = QTextEdit('',self)
        self.output.setGeometry(10,170,460,305)
        self.output.setAcceptDrops(False)
        
        # APIキーを環境変数から取得
        apiKey = os.getenv("CHATGPT_API_KEY")
        os.environ["OPENAI_API_KEY"] = apiKey

        # ログレベルの設定(DEBUG)
        logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
        logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))

    def makeIDX(self):
        # インデックスの作成および保存
        llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="text-embedding-ada-002"))
        service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)
        
        data_path = self.input.text()
        data_path2 = data_path.replace("'", "") # 拡張子判定用
        data_path3 = Path(data_path2) # loader用

        if data_path2.endswith('.csv'):
            SimpleCSVReader = download_loader("SimpleCSVReader")
            loader = SimpleCSVReader()
        elif data_path2.endswith('.pdf'):
            PDFReader = download_loader("PDFReader")
            loader = PDFReader()
        else:
            print('ファイルがcsv,pdfではありません')
            sys.exit()

        nodes = loader.load_data(file=data_path3)

        index = GPTSimpleVectorIndex.from_documents(nodes, service_context=service_context)
        
        now = datetime.datetime.now()
        formatted_time = now.strftime('%y%m%d_%H%M%S')

        index_file = "/AI/LlamaIndex/index/" + formatted_time + "_index.json"
        index.save_to_disk(index_file)
        
        self.input2.setText(index_file)
        self.idxBtn2.click()

    def loadIDX(self):
        # インデックスの読込
        llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="text-davinci-003", max_tokens=3500))
        service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)
        
        index_path = self.input2.text()

        self.index = GPTSimpleVectorIndex.load_from_disk(save_path= index_path, service_context=service_context)
        self.output.setText("IDX読込完了")
        
    def sendQuestion(self):
        question = self.questionInput.toPlainText()
        response = self.index.query(question)
        
        print(response)
        self.output.setText(str(response))

    def clear(self):
        self.input.clear()
        self.input2.clear()
        self.questionInput.clear()
        self.output.clear()

app = QApplication(sys.argv)
window = LlamaIndex()
window.show()
app.exec()

[AI] LlamaIndexアプリ製作 その3 LlamaIndexライブラリ分析 pdbによるデバッグ

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

py2appによるpyファイルのappファイル化がうまくいかないので、LlamaIndexライブラリの内容を分析し、C++への移植を目指すことにしました。

分析にあたりLlamaIndexライブラリを改変し、デバッグ仕様にしています。

思っていたよりも内容が複雑で何度かくじけかけましたが、ようやく光が見えてきました。

Pythonスクリプトを走らせた時の出力の内容とLlamaIndexライブラリの関連がとても見えにくく、printデバッグで細かくスクリプトやライブラリの動作を追跡しないと何が何だか分かりません。

そんなこんなでCSVファイルをindex化する所までたどり着いたものの、ここでいよいよ分からなくなりPython用のデバッガpdbを使いました。

下図赤枠の所をrコマンド、nコマンド、sコマンドで刻んで進めていきましたが、どこまで行っても目的箇所へたどり着けず300行ぐらいで追いかけるのを断念しました。Pythonの標準ライブラリを多用していますし、とてもじゃないですがC++へ移植など工数がいくつあっても足りないです。

Embbedingについては他のアプローチを模索していきます。

由来が不明だった出力の根拠となるスクリプトをようやく見つけた
pdbによるデバッグ
import os, logging, sys
from pathlib import Path
# llama_index改変版
from llama_index_fork import download_loader,LLMPredictor, GPTSimpleVectorIndex, ServiceContext
from langchain import OpenAI
from mylib import MySimpleCSVReader
import pdb

# APIキーを環境変数から取得
apiKey = os.getenv("CHATGPT_API_KEY")
os.environ["OPENAI_API_KEY"] = apiKey

# ログレベルの設定(DEBUG)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))

# インデックスの作成および保存
llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="text-embedding-ada-002"))
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)

print("ファイルパスを入力して下さい")
file_path = input()
file_path2 = file_path.replace("'", "") # 拡張子判定用
file_path3 = Path(file_path2) # loader用

if file_path2.endswith('.csv'):
	# SimpleCSVReader = download_loader("SimpleCSVReader")
	loader = MySimpleCSVReader.MySimpleCSVReader()
elif file_path2.endswith('.pdf'):
	PDFReader = download_loader("PDFReader")
	loader = PDFReader()
else:
	print('ファイルがcsv,pdfではありません')
	sys.exit()

# documentsの型は、List[Document]
documents = loader.load_data(file=file_path3)

print("documentsの内容")
num = 1
for doc in documents:
    print(str(num) + ": " + str(doc) + "\n")
    num = num + 1

# ブレークポイント設定
pdb.set_trace()

index = GPTSimpleVectorIndex.from_documents(documents, service_context=service_context)
print("\nindex: " + str(index) + "\n")

index.save_to_disk('index.json')

# インデックスの読込
llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="text-davinci-003", max_tokens=3500))
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)

index = GPTSimpleVectorIndex.load_from_disk(save_path="index.json", service_context=service_context)

# 質問(Ctrl+cで終了)
while True :
  print("質問を入力して下さい")
  question = input()
  
  print(index.query(question))
  

[AI] LlamaIndexアプリ製作 その2 仮想環境を使ったアプリ軽量化 PyQt6 / py2app / virtualenv

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

py2appを使ったappファイル作成の際の出力をチェックするとどうやら無関係なライブラリも取り込んでいるようなので、仮想環境を使って軽量化を図りました。

今回はvirtualenvを使いました。venvは標準機能として入っていますが、システムのPythonから離れることは出来ません。Pythonのバージョンを自由に選べるvirtualenvの方が使い勝手は良さそうです。

Pythonの主な仮想環境:pyenv, venv, virtualenv

仮想環境を使って最低限のライブラリのみ取り込むようにした結果、248.3MBを213.8MBまで減らすことが出来ました。それでもまだまだ大きいですね。言語自体を変えないとこれ以上は厳しそうです。

py2appで作成したappファイルについてうまく起動できないケースが散見されますが、appファイルの内部にあるMacOSディレクトリに実行ファイルがありますので、これをダブルクリックするとターミナルが起動し、エラー出力の内容を確認できます。

# Python3.10の仮想環境envを作成
virtualenv -p python3.10 env

# ライブラリをインストール
./env/bin/pip install ライブラリ名

# 仮想環境envの有効化
source env/bin/activate

# 仮想環境envの無効化
deactivate
LlamaIndexプロジェクト内に仮想環境envを作成
appファイル起動トラブル時は中にある実行ファイルを起動してエラー出力を確認する

[AI] LlamaIndexアプリ製作 その1 PyQt6 / py2app

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

LlamaIndexアプリの製作に着手しました。

簡単なアプリなのでそんなに工数は掛からないでしょう。取りあえずガワは作りました。

C++で書こうとするとlibcurlおよびEmbeddingのところがかなりややこしくなるため、今回はPyQt6で作ります。

現時点でサイズは240MBに達しています。自分的には論外な大きさですが、軽量高速化よりAI最新情報へのキャッチアップを優先します。

[AI] LlamaIndexによるGPT-3.5専門ボット化 CSV, PDF対応

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

専門チャットボット生成スクリプトについて取り込むファイルタイプとしてCSVとPDFに対応させました。

SimpleCSVReaderのインスタンス化をし忘れていて少し手間取りました。クラスのインスタンス化は言語によってやり方が微妙に違うので要注意です。C++やJavaのようにnewを使わないので見落としがち。

import os, logging, sys
from pathlib import Path
from llama_index import download_loader,LLMPredictor, GPTSimpleVectorIndex, ServiceContext
from langchain import OpenAI

# APIキーを環境変数から取得
apiKey = os.getenv("CHATGPT_API_KEY")
os.environ["OPENAI_API_KEY"] = apiKey

# ログレベルの設定(DEBUG)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))

# インデックスの作成および保存
llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="text-embedding-ada-002"))
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)

print("ファイルパスを入力して下さい")
file_path = input()
file_path2 = file_path.replace("'", "") # 拡張子判定用
file_path3 = Path(file_path2) # loader用

if file_path2.endswith('.csv'):
	SimpleCSVReader = download_loader("SimpleCSVReader")
	loader = SimpleCSVReader()
elif file_path2.endswith('.pdf'):
	PDFReader = download_loader("PDFReader")
	loader = PDFReader()
else:
	print('ファイルがcsv,pdfではありません')
	sys.exit()

nodes = loader.load_data(file=file_path3)

index = GPTSimpleVectorIndex.from_documents(nodes, service_context=service_context)
index.save_to_disk('index.json')

# インデックスの読込
llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="text-davinci-003", max_tokens=3500))
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)

index = GPTSimpleVectorIndex.load_from_disk(save_path="index.json", service_context=service_context)

# 質問(Ctrl+cで終了)
while True :
  print("質問を入力して下さい")
  question = input()
  
  print(index.query(question))
  

[AI] LlamaIndexによるGPT-3.5専門ボット化 エディタから実行

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

これまでGoogle Colabで検証していましたが、VSCodeに環境を移しました。

インデックス化するファイルさえあれば、GPT-3.5を手軽に専門ボット化できるようになりました。

ただし、この方法ではプロンプトを箇条書きにするなど分かりやすい構成にすることができず密度の高い質問が出来ないため、検証は一旦ストップすることにしました。

色々検討すればするほど、私の中でgpt-3.5-turboチャットボットの優位性が揺るがなくなっています。OpenAIのEmbeddingsについては開発がかなり遅れているように見受けられました。公開内容を制限している可能性もありますね。

import os, logging, sys
from pathlib import Path
from llama_index import download_loader,LLMPredictor, GPTSimpleVectorIndex, ServiceContext
from langchain import OpenAI

# APIキーを環境変数から取得
apiKey = os.getenv("CHATGPT_API_KEY")
os.environ["OPENAI_API_KEY"] = apiKey

# ログレベルの設定(DEBUG)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))

# インデックスの作成および保存
SimpleCSVReader = download_loader("SimpleCSVReader")

llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="text-embedding-ada-002"))
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)

csv_path = Path('fine_tuning_data.csv')

loader = SimpleCSVReader()
documents = loader.load_data(file=csv_path)

index = GPTSimpleVectorIndex.from_documents(documents, service_context=service_context)
index.save_to_disk('fine_tuning_data.json')

# インデックスの読込
llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name="text-davinci-003", max_tokens=3500))
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)

index = GPTSimpleVectorIndex.load_from_disk(save_path="fine_tuning_data.json", service_context=service_context)

# 質問(Ctrl+cで終了)
while True :
  print("質問を入力して下さい")
  question = input()
  
  print(index.query(question))
  

[Python] 353 Gmailの自動送信(ChatGPT編)

巷で話題のChatGPTにプログラミングさせてみました。

パスワードについて補足のネット検索が必要ではありますが、合格点の内容でした。ご丁寧にtry文で例外処理に対応しています。

Google検索を差し置いてプログラミング手法調査のファーストチョイスになりそうです。

ChatGPTは定型的スクリプト作成は得意なようです。何ができて何ができないのかその特性を見極める必要がありますね。

初級プログラマとしては、とりあえずこのAIよりは気の利いたコードを書けるようにしたいものです。

質問:
GmailAPIを使わずにPythonでGmailを自動送信するスクリプトを教えてください

ChatGPT回答:
Gmail APIを使わずにGmailを自動送信するには、SMTP(Simple Mail Transfer Protocol)を使うことができます。
次のようなコードを記述することでGmailを自動送信することができます:
import smtplib
from email.mime.text import MIMEText

def send_email(to, subject, body):
    gmail_user = 'your_gmail_address@gmail.com'
    gmail_password = 'your_gmail_password'

    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = gmail_user
    msg['To'] = to

    try:
        server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
        server.ehlo()
        server.login(gmail_user, gmail_password)
        server.send_message(msg)
        server.quit()
        print(F'Email was sent to "{to}"')
    except Exception as e:
        print(F'Something went wrong... {e}')
このスクリプトを実行すると、指定された宛先にメールが送信されます。
Gmailアカウントを使用する場合は、Gmailアカウントにアクセスするためのユーザー名とパスワードを設定する必要があります。

[Python] 352 Gmailの自動送信

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

Pythonの汎用性の高さには目を見張るものがありますが、さらに新たな便利機能を見つけました。

Gmailの自動送信はかなり重宝しそうなのでライブラリ化しました。gmail.pyをmy_libraryディレクトリに入れて使います。

このスクリプトで使うパスワードはGoogleが発行する2段階認証非対応デバイス用パスワードになります。

定期実行しているスクリプトでエラーが発生した際に自動メールで知らせることも可能になりますね。

import smtplib
from email.mime.text import MIMEText

def send_gmail(mailto, title, text_msg):
    from_email ='送信元Gmailアドレス' 
    cc_mail = ''
    
    msg = MIMEText(text_msg, 'plain')
    msg['Subject'] = title
    msg['To'] = mailto
    msg['From'] = from_email
    msg['cc'] = cc_mail
    
    smtp_host = 'smtp.gmail.com'
    smtp_port = 587
    smtp_password = '2段階認証非対応デバイス用パスワード'
    
    server = smtplib.SMTP(smtp_host, smtp_port)
    server.starttls()
    server.login(from_email, smtp_password)
    server.send_message(msg)
    server.quit()
from my_library import gmail

mailto = "送信先メールアドレス"
title = "テスト"
msg = "テスト\nメール"

gmail.send_gmail(mailto, title, msg)

参考サイト

[Python] 351 C++ソースコードとJSON要素の照合 その3 スクリプトを統合

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

AdobeXDアイテムデータからJSONファイルを作成するスクリプトとC++ソースコードの内容と比較するスクリプトを統合しました。

これでAdobeXDプラグイン出力のテキストファイルをターミナルにドラッグ&ドロップするだけでC++ソースコードとの比較結果が分かるようになります。

次はVSCodeの拡張機能作成に着手する予定です。

import pandas as pd

print("AdobeXDアイテムデータのパスを入力してください")
items_file = input()

# JSONファイル名作成
items_json_file = "/code/cpp/projects/VideoEditor/plugin/" + (items_file.split("/")[-1]).split(".")[0] + ".json"
print(items_json_file)

# 先頭と末尾のシングルクォートを削除
items_file2 = items_file[1:-1]
print(items_file2)

# データファイルを読み込む
with open(items_file2, "r") as tf:
    items_list = tf.read().replace("\n","").split(';')
    
# print(items_list)
# print(len(items_list))

# listからデータ抽出し、JSON文字列作成
json_str = ""
num = 0
for item in items_list:
    name = item.split("'")[1]
    # print(name + "\n")
    
    xy = (item.split("global X,Y:")[1]).split("parent")[0]
    x = xy.split(",")[0]
    y = xy.split(",")[1]
    # print(x + "\n")
    # print(y + "\n")
    
    wh = (item.split("{")[1]).split("global")[0]
    w = (wh.split("width:")[1]).split(", height")[0]
    h = wh.split("height:")[1]
    # print(w + "\n")
    # print(h + "\n")
    
    if num == 0:
        json_str += "{\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "],\n"
    elif num < len(items_list) -1:
        json_str += "\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "],\n"
    else:
        json_str += "\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "]}"

    # print(json_str + "\n")
    
    num += 1

# JSONファイル作成
with open(items_json_file, mode='w') as f:
    f.write(json_str.replace(" ",""))

# C++ソースファイル    
cpp_file = '/code/cpp/projects/VideoEditor/src/VideoEditor.cpp'
    
# 検索用ファイル文字列(行単位)
with open(cpp_file) as f:
    lines = f.readlines()

# 不一致ファイル名作成
false_json_file = "/code/cpp/projects/VideoEditor/plugin/" + (items_json_file.split("/")[-1]).split("_")[0] + "_false_" +  (items_json_file.split("/")[-1]).split("_")[1]

print(false_json_file)

# JSONファイルをpandasで読込
df = pd.read_json(items_json_file)
print(df)

# 非ウィジェットデータのリスト
notWidget_list = ['convertArea', 'STDOUT', 'IMAGESTOVIDEO', 'VIDEOTOIMAGES']
# 非ウィジェットデータ削除
df2 = df.drop(columns = notWidget_list)

# ウィジェット名リスト化
columns = df2.columns.values
print(columns)
print("columns_len: " + str(len(columns)))

# C++データとAdobeXDデータの照合
false_json = ""
false_count = 0
new_notWidget_list = list()
for col in columns:
    data = df.loc[:, col]
    # print(data.values)
    data2 = str(data.values[0]) + "," + str(data.values[1]) + "," + str(data.values[2]) + "," + str(data.values[3])
    print(data2)
    
    var_str = col + " = new"   
    print(var_str) 
    
    line_str = [line for line in lines if var_str in line]
    
    try:
        line_str2 = line_str[0]
        print(line_str)
        str_exist = data2 in line_str2
        print(str_exist)
        
        if str_exist == False:
            old_xy = (line_str2.split("(")[1]).split(",\"")[0]
            print("col, old_xy: " + col + ", " + str(old_xy))
            
            if false_count == 0:
                false_json += "{\"" + col + "\"" + ":[[" + old_xy + "],[" + data2 + "]],\n"
            else:
                false_json += "\"" + col + "\"" + ":[[" + old_xy + "],[" + data2 + "]],\n"
                
            false_count += 1

    except Exception as e:
        print(e)
        new_notWidget_list.append(col)
        
print("new_notWidget_list: " + str(new_notWidget_list))
print("false_count: " + str(false_count))

false_json2 = false_json[:-2] + "}"

# 不一致WidgetのJSONファイル作成
with open(false_json_file, mode='w') as f:
    f.write(false_json2)

[Python] 350 C++ソースコードとJSON要素の照合 その2 不一致データのJSON化

[M1 Mac, Monterey 12.6.3, Python 3.10.4]

前回の続きです。

C++ソースコードとAdobeXDのデータを照合し、不一致データをJSONファイルにまとめました。

これを見ながら手動でソースコードの修正ができます。C++ソースコードを自動修正するスクリプト作成までやるかどうかは未定です。

JSONの自由度の高さはなかなかのものです。文字列を連結するだけで作成できるので便利ですね。

import pandas as pd

file = 'VideoEditor.cpp'
    
# 照合用ファイル文字列(行単位)
with open(file) as f:
    lines = f.readlines()

# AdobeXDアイテムデータをpandasで読込
df = pd.read_json('items.json')
print(df)

# 非ウィジェットのリスト
notWidget_list = ['convertArea', 'STDOUT', 'IMAGESTOVIDEO', 'VIDEOTOIMAGES']
# 非ウィジェットデータ削除
df2 = df.drop(columns = notWidget_list)

# ウィジェット名リスト化
columns = df2.columns.values
print(columns)
print("columns_len: " + str(len(columns)))

# C++データとAdobeXDデータの照合
false_json = ""
false_count = 0
new_notWidget_list = list()
for col in columns:
    data = df.loc[:, col]
    # print(data.values)
    data2 = str(data.values[0]) + "," + str(data.values[1]) + "," + str(data.values[2]) + "," + str(data.values[3])
    print(data2)
    
    var_str = col + " = new"   
    print(var_str) 
    
    line_str = [line for line in lines if var_str in line]
    
    try:
        line_str2 = line_str[0]
        print(line_str)
        str_exist = data2 in line_str2
        print(str_exist)
        
        if str_exist == False:
            old_xy = (line_str2.split("(")[1]).split(",\"")[0]
            print("col, old_xy: " + col + ", " + str(old_xy))
            
            if false_count == 0:
                false_json += "{\"" + col + "\"" + ":[[" + old_xy + "],[" + data2 + "]],\n"
            else:
                false_json += "\"" + col + "\"" + ":[[" + old_xy + "],[" + data2 + "]],\n"
                
            false_count += 1

    except Exception as e:
        print(e)
        new_notWidget_list.append(col)
        
print("new_notWidget_list: " + str(new_notWidget_list))
print("false_count: " + str(false_count))

false_json2 = false_json[:-2] + "}"

# 不一致WidgetのJSONファイル作成
with open('false.json', mode='w') as f:
    f.write(false_json2)
{"y1Label":[[697,23,21,14],[698,23,21,14]],
"imageSec":[[475,170,80,20],[485,170,60,20]],
"imageNum":[[65,170,100,20],[65,170,50,20]],
"input":[[25,25,35,16],[25,25,16,16]]}