[C++] 243 SDL : テトリス製作 その2 既存ソース改変

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, NO IDE]

GitHubから入手したソースコードをNO IDE仕様に改変しました。今回はChatGPTのサポートなしです。

Debug.hの一部を書き換え、フォントファイル clacon2.ttfを入手してHomeの/Library/Fontsディレクトリに置き、ビルドを成功させました。

ソースコードはIntel Mac時代のものですが、Apple Siliconでも問題なく動きます。

次はPS4のワイヤレスコントローラで操作できるようにします。

#if defined _MSC_VER
#define HP_BREAK __debugbreak();
#elif defined __arm__
#define HP_BREAK __builtin_trap();
#elif defined __GNUC__
// #define HP_BREAK __asm__ ("int $3"); // コメント化
#define HP_BREAK __builtin_trap();    // この行を追加
#else
#error Unsupported compiler
#endif
# コンパイラ設定他
COMPILER = clang++
DEBUG = -g

# フラグ設定
CPPFLAGS = $(shell pkg-config sdl2 --cflags --libs) -std=c++11
LDFLAGS =

# includeパス(-I)
INCLUDE = -I./include -I/opt/homebrew/Cellar/sdl2/2.26.2/include \
		-I/opt/homebrew/Cellar/sdl2_ttf/2.20.2/include \
		-I/Volumes/DATA_m1/code/cpp/mylib/include

# ライブラリ(-l)
LIBRARY0 = -lsdl2 -lsdl2_ttf

# ライブラリパス(-L)
LIBRARY = -L/opt/homebrew/Cellar/sdl2/2.26.2/lib \
		-L/opt/homebrew/Cellar/sdl2_ttf/2.20.2/lib

# ソースファイル
SRCDIR = ./src
SRCS = $(shell find $(SRCDIR) -type f)

# オブジェクトファイル
OBJDIR = ./obj
OBJS = $(addprefix $(OBJDIR), $(patsubst ./src/%.cpp,/%.o,$(SRCS)))

# 実行ファイル
TARGETDIR = ./bin
TARGET = Tetris
	
# cppファイルからoファイル作成 $<:依存ファイル
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
	$(COMPILER) $(CPPFLAGS) $(INCLUDE) $(DEBUG) -o $@ -c $<

# oファイルから実行ファイル作成
$(TARGET):$(OBJS)
	$(COMPILER) -o $(TARGETDIR)/$@ $(OBJS) $(LIBRARY0) $(LIBRARY) $(LDFLAGS)

# 全ソース・コンパイル&ビルド
.PHONY:all
all: clean $(OBJS) $(TARGET)

# oファイル・実行ファイル削除
.PHONY:clean
clean:
	rm -rf $(OBJS) $(TARGETDIR)/$(TARGET)
テスト操作(60秒)

GitHub(sdl2-tetris)

[C++] 242 SDL : テトリス製作 その1 Bing-ChatGPT

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, NO IDE]

Bing-ChatGPTにアシストしてもらいながらテトリスを作っていきます。

ネットから見つけてきたサンプルコードが旧バージョンのSDLで作られていたため、旧関数をSDL2の新関数に置き換えつつ修正を加えました。

何とか真っ暗な画面から何かが描画されている状態にまで持ってこれましたが、そもそもの設計があまり筋が良いとも思えないので、GitHubで見つけた他のソースを土台にすることにしました。次回以降記事にします。

最後までGoogle検索せずにSDLのソースコード読み込みとChatGPTだけで形にできました。Google検索だったら1日以上掛かってもおかしくない修正が数時間に短縮できて生産性は爆上がりです。

[C++] 241 SDL : ChatGPTを使う

ChatGPTにテトリスのプログラムについて質問したところ、まずSDL描画の骨組みを説明しましたが、字数制限でテトリスの内容にまではたどり着けませんでした。

[C++] 240で紹介したMacOS特有のトラブルへの解決策も盛り込まれたなかなかのサンプルコードです。

ChatGPTを使えばStackOverflow英語版のお世話になることもなくなるのでしょうか。私にとってプログラミング学習のターニングポイントになるかもしれません。

ここまでAIが進化してくると、人間としては何を知っているか、何をクリエイトできるか、だけではない新たな価値観を模索する時代になったと認識せざるを得ません。

[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)

参考サイト

[C++] 240 SDL : ウィンドウの表示 MacOS固有のトラブル

[M1 Mac, Monterey 12.6.3, clang 13.0.0, SDL 2.26.2, NO IDE]

SDL2を使ってウィンドウを表示させるところでつまずきました。

イベントをチェックするループを設定しないとダメなようです。日本語ユーザーの方がおなじみのStackOverflow英語版で解決法を見つけてくれていました。

ただこのコードではマウスクリックやキー押下に反応してしまうので、スクリーンショットが撮れません。少し修正が必要です。

一応MacOSでも使えるようにしてくれていますが、初心者は実質門前払いのような状況です。

クロスプラットホームと言いながらdmgインストーラにはMacの文字は見当たらず。どう見てもSDLの開発者はMacユーザーに冷たいですね。ゲーム界隈の方はApple嫌いが多いとか?

#include "SDL2/SDL.h"
#include <stdio.h>

int main(int argc, char* argv[]) {
    SDL_Window *window;
    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow(
        "SDL Window",
        100,
        100,
        640,
        480,
        0
    );

    if (window == NULL) {
        printf("ウィンドウを生成できません: %s\n", SDL_GetError());
        return 1;
    }

    // MacOS以外では以下ループは不要
    SDL_Event e;
    bool quit = false;
    while (!quit){
        while (SDL_PollEvent(&e)){
            if (e.type == SDL_QUIT){
                quit = true;
            }
            if (e.type == SDL_KEYDOWN){
                quit = true;
            }
            if (e.type == SDL_MOUSEBUTTONDOWN){
                quit = true;
            }
        }
    }

    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

参考サイト

[C++] 239 FLTK : Fl_Boxに高さ不足の画像を表示した際の座標 アスペクト比

[M1 Mac, Monterey 12.6.3, clang 13.0.0, FLTK 3.8.1, NO IDE]

Fl_Boxに高さ不足の画像を表示した際、画像の左上を原点(0, 0)とするための調整が少しややこしかったので書き留めておきます。

横幅が一致していて高さが足りない画像の場合はその縦位置は適当に置かれているわけではなく、残った空白がアスペクト比で配分されていました。幾何学の世界ですね。

今回はアスペクト比が4:3なので、縦の空白は上部:下部 = 3:4になっています。これにより画像の左上Y座標をゼロにするための調整値が算出できます。

Fl_PNG_Image *png = new Fl_PNG_Image((paths[0]).c_str());
anaW = wInput -> value();
anaH = hInput -> value();

aspect = stof(string(anaW))/stof(string(anaH));

if (aspect > 4/3){
    newH = stof(string(anaH)) * 480/stof(string(anaW));
    cout << "newH: " << newH << endl;
    png_copy = png -> copy(480,round(newH));

    // Y調整値算出
    adjY = round((360 - newH) * 3/7);

} else {
    newW = stof(string(anaW)) * 360/stof(string(anaH));
    cout << "newW: " << newW << endl;
    png_copy = png -> copy(round(newW),360);
}

showBox -> image(png_copy);
showBox -> redraw();

[C++] 238 FLTK : アスペクト比固定、小数点以下の扱い

[M1 Mac, Monterey 12.6.3, clang 13.0.0, FLTK 3.8.1, NO IDE]

動画編集アプリの左下に画像をコマ送りで表示させるのですが、アスペクト比を元動画のままにして、経過時間を小数点第3位までとしました。

floatは有効桁数が7桁なので小数点以下で四捨五入しても、1.570000のような表示になります。そこでfloatを文字列に変換し、無限ループを利用して末尾が0である限り削除し続けるようにしました。

ただこの方法では小数点以下の桁数を固定できないのがデメリットです。Pythonであれば簡単に桁数を揃えられそうですが、C++は面倒なので最小限の手立てに留めておきます。

void showCB(Fl_Widget*, void*) {
    paths = getFilePath("/Volumes/DATA_m1/VideoEditor/images", "png");
    // スライダーの数値を取得
    slider -> bounds(0, (double)(paths.size() -1));

    showData(fpsFile);
    fpsChar = fpsInput -> value();
    if (fpsChar == NULL){
        return;
    }

    string fpsStr = string(fpsChar);
    sec = frameNum/stof(fpsStr);
    sec2 = round(sec*1000)/1000;
    sec2_str = to_string(sec2);

    while(1){
        if (sec2_str.back() == '0' or sec2_str == "0."){
            sec2_str.pop_back();
        } else {
            break;
        }
    }
    imageSec -> value(sec2_str.c_str());

    imageNumStr = to_string(frameNum +1) + "/" + to_string(paths.size());
    imageNum -> value(imageNumStr.c_str());

    Fl_PNG_Image *png = new Fl_PNG_Image((paths[0]).c_str());
    anaW = wInput -> value();
    anaH = hInput -> value();

    aspect = stof(string(anaW))/stof(string(anaH));

    if (aspect > 4/3){
        newH = stof(string(anaH)) * 480/stof(string(anaW));
        cout << "newH: " << newH << endl;
        png_copy = png -> copy(480,round(newH));
    } else {
        newW = stof(string(anaW)) * 360/stof(string(anaH));
        cout << "newW: " << newW << endl;
        png_copy = png -> copy(round(newW),360);
    }

    showBox -> image(png_copy);
    showBox -> redraw();
}

[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]]}