[C++] 39 FLTK : findコマンド生成アプリ/前日の取得 time_t型, tm構造体

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

作成中のアプリで検索期間を処理する際、開始日については入力した日の前日の日付文字列が必要になります。

time_t 型からtm構造体に変換するなどして対応しました。かなりややこしかったですし、まだ未解決の箇所もあります。

それは月をまたぐ場合です。例えば2022年3月1日の前日は2月28日ですが現時点のコードでは2月31日になってしまいます。

うるう年も含め対策が必要となります。Excelのシリアル値のようなものがあれば便利なんですが。

time_t preDate_t;
struct tm preDate;
const tm* preDate2;

void execute_cb(Fl_Widget*, void*) {
    stringstream cmd;
    stringstream pre;
    const char* dir = dir_input->value();
    const char* from_char = from_input->value();
    string from_str = string(from_char);

    std::cout << "from_str " << from_str << endl;
    string day = from_str.substr(6);
    string month = from_str.substr(4, 2);
    string year = from_str.substr(0, 4);

    std::cout << day << " " << month << " " << year <<endl;

    preDate = { 0, 0, 0, stoi(day) -1, stoi(month) -1, stoi(year)};
    preDate_t = std::mktime(&preDate);
    preDate2 = localtime(&fromDate_t);

    int pre_y = preDate2->tm_year;
    int pre_m = preDate2->tm_mon + 1;
    int pre_d = preDate2->tm_mday;

    std::cout << "pre_y pre_m pre_d " << pre_y << " " << pre_m << " " << pre_d <<endl;

    pre << preDate2->tm_year;
    pre << setw(2) << setfill('0') << preDate2->tm_mon;
    pre << setw(2) << setfill('0') << preDate2->tm_mday;
    
    std::cout << pre.str() << endl;
    
    cmd << "cd " << string(dir) << " && " << "find `pwd` -type file -newerct '" << pre.str() << " 23:59' ! -name '*DS_Store*'";
    std::cout << cmd.str() << endl;

    system((cmd.str()).c_str());
}

[C++] 38 FLTK : findコマンド生成アプリ/Fl_Native_File_Chooser

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

OSネイティブなFile Chooserが使えるとされるFl_Native_File_Chooserを試してみました。

MacOSのFile Chooserは正しく表示されましたが、ディレクトリパスの取り込みはうまくいきませんでした。Windowを閉じることもできず、仕方なくDockから終了させました。この現象確認はIntel Macでは未実施です。

#include <Fl_Native_File_Chooser.H>

void filechooser_cb(Fl_Widget*, void*) {
    // Fl_File_Chooser chooser(".",                        // directory
    //                         "*",                        // filter
    //                         Fl_File_Chooser::DIRECTORY,     // chooser type
    //                         "Dir Select");        // title
    
    Fl_Native_File_Chooser* chooser = new Fl_Native_File_Chooser();
    chooser->type(Fl_Native_File_Chooser::BROWSE_DIRECTORY);
    chooser->title("Dir Select");
    chooser->filter("*");
    chooser->directory(".");

    chooser->show();

    while(chooser->show())
        { Fl::wait(); }

    dir_input->insert(chooser->filename());
}

[C++] 37 FLTK : findコマンド生成アプリ/Fl_File_Chooser

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

ディレクトリ選択のためにFl_File_Chooserを使用します。

案外すんなりと導入できました。見た目は硬派な感じです。

#include <FL/Fl_File_Chooser.H>

void filechooser_cb(Fl_Widget*, void*) {
    Fl_File_Chooser chooser(".",      // directory
                            "*",      // filter
                            Fl_File_Chooser::DIRECTORY, // chooser type
                            "Dir");   // title
    chooser.show();

    while(chooser.shown())
        { Fl::wait(); }

    dir_input->insert(chooser.value());
}

参考サイト

[C++] 36 日時の取得 localtime関数

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

開発中のアプリにデフォルトとして今日の日付を表示するため、関数を用意しました。

今回は年月日だけですが年月日時分秒も可能です。

ネット情報から拝借したものをアレンジしています。

#include <iostream>
#include <iomanip>

using std::setfill; using std::setw;
using std::stringstream;

string today;
time_t t;
stringstream s;
const tm* localTime;

string getDatetimeStr() {
    t = time(nullptr);
    localTime = localtime(&t);
    s << localTime->tm_year + 1900;
    s << setw(2) << setfill('0') << localTime->tm_mon + 1;
    s << setw(2) << setfill('0') << localTime->tm_mday;
    // s << setw(2) << setfill('0') << localTime->tm_hour;
    // s << setw(2) << setfill('0') << localTime->tm_min;
    // s << setw(2) << setfill('0') << localTime->tm_sec;
    return s.str();
}

参考サイト

[Python] 96 再掲 Nikon画像ファイルの日付別取り込み

[M1 Mac, Big Sur 11.6.5, Python 3.10.0]

前のブログサイトからの引っ越しです。画像ファイルをSDカードから外部SSDへ日付振り分けしてコピーします。

Nikon Trasnferという配布アプリで日付振り分けができることを知らずにコードを書きました。今も愛用しています。

# coding: UTF-8

import shutil ,os ,glob
from PIL import Image, ExifTags
import collections

path_list = glob.glob('/NIKON D500 /DCIM/101ND500/*.JPG')

exif_datetime_list = []
for path in path_list: 
    img = Image.open(path)

    exif = { ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS }

    # print(exif)
    exif_datetime = [v for k, v in exif.items() if k == "DateTimeOriginal"]
    print(exif_datetime[0])

    exif_datetime_str = (exif_datetime[0].split(':')[0])[2:4] + exif_datetime[0].split(':')[1] + (exif_datetime[0].split(':')[2])[0:2]
    print(exif_datetime_str)
    exif_datetime_list.append(exif_datetime_str)

print(exif_datetime_list)

# 作成日の内訳を集計し作成日のリストを作成する
keys = collections.Counter(exif_datetime_list).keys()
print(keys)

# 作成日フォルダを作成する
for key in keys:
    try:
        os.mkdir('/photo/D500/' + str(key))
    except FileExistsError:
        pass

# 画像ファイルを各フォルダにコピーする
for path in path_list:
    
    # 画像ファイルを読み込む
    img = Image.open(path)

    exif = { ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS }

    exif_datetime = [v for k, v in exif.items() if k == "DateTimeOriginal"]
    
    exif_datetime_str = (exif_datetime[0].split(':')[0])[2:4] + exif_datetime[0].split(':')[1] + (exif_datetime[0].split(':')[2])[0:2]

    # ファイルパスを文字列に変換する
    path_str = str(path)

    # ファイル名を抽出する(スラッシュで分割した最後の文字列)
    filename = path_str.split('/')[-1]

    # RAWファイル名を生成する
    filename_raw = filename.split('.')[0] + '.NEF'

    # RAWファイルのコピー元を生成する
    filename_raw_src = '/NIKON D500 /DCIM/101ND500/' + str(filename_raw)
    # RAWファイルのコピー先を生成する
    filename_raw_dest = '/photo/D500/'+ str(exif_datetime_str) + '/' + str(filename_raw)

    print(f'RAWコピー元 {filename_raw_src}')
    print(f'RAWコピー先 {filename_raw_dest}\n')

    filename_jpeg_dest = '/photo/D500/'+ str(exif_datetime_str) + '/' + str(filename)

    print(f'JPEGコピー元 {path}')
    print(f'JPEGコピー先 {filename_jpeg_dest}\n')

    # JPGファイルをコピーする
    shutil.copy2(path,filename_jpeg_dest)

    # RAWファイルをコピーする
    try:
        shutil.copy2(filename_raw_src,filename_raw_dest)
    except FileNotFoundError :
        pass

# Macのデスクトップ通知
def main():
    os.system("osascript -e 'display notification \"Nikonファイルコピー\nコード実行完了\"'")

if __name__ == '__main__':
    main()

[C++] 34 FLTK : findコマンド生成アプリ/絶対パス

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

アプリ作成の仕込みとしてfindコマンドへの理解を深めています。

前回の記事で検索結果を絶対パスで出力する場合はディレクトリ指定の所をpwdに置き換えます。

cd /test && \
find `pwd` -type file -newerat '20220414 23:59' ! -newerat '20220415 23:59' ! -name "*DS_Store*" -and -name "*PkgInfo*" && \
find `pwd` -type file -newerat '20220414 23:59' ! -newerat '20220415 23:59' ! -name "*DS_Store*" -and -name "*PkgInfo*" | wc -l
--------------------------------------------------
出力例
--------------------------------------------------
/Python/test/PkgInfo
       1

[C++] 33 FLTK : findコマンド生成アプリ/日付指定

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

FLTKアプリ第2弾の作成に着手しました。

今一つ信用できないFinderによる検索やSpotlight検索に代わる実用アプリです。

例えば、4/15にtestディレクトリで作成、アクセス、更新したファイルの名前と件数を出力するコマンドは以下の通りです。作成中のアプリはこのコマンドを生成して実行してくれます。

このコマンドでの日時の指定範囲です。
20220414 23:59 < range <= 20220415 23:59

cd /test && \
find . -type file -newerct '20220414 23:59' ! -newerct '20220415 23:59' ! -name "*DS_Store*" && \
find . -type file -newerct '20220414 23:59' ! -newerct '20220415 23:59' ! -name "*DS_Store*" | wc -l && \
find . -type file -newerat '20220414 23:59' ! -newerat '20220415 23:59' ! -name "*DS_Store*" && \
find . -type file -newerat '20220414 23:59' ! -newerat '20220415 23:59' ! -name "*DS_Store*" | wc -l && \
find . -type file -newermt '20220414 23:59' ! -newermt '20220415 23:59' ! -name "*DS_Store*" && \
find . -type file -newermt '20220414 23:59' ! -newermt '20220415 23:59' ! -name "*DS_Store*" | wc -l
--------------------------------------------------
出力例
--------------------------------------------------
       0
./Python/python/test.py
./ShellScript/test.sh
       3
./app/FileFinder/File Finder.png
       1

[C++] 32 FLTK: 親子ウィジェットを消去するコールバック [移植完了]

前回の続きです。

表示させたモーダルダイアログを消すコールバックを作成しました。最初は汎用ポインタvoid*の扱い方がわかりませんでした。

モーダルダイアログとOKボタンの親子関係を構築させ、ボタンを押すと親子ウィジェット共に消去するといった内容です。解決するまではOKボタンを押すとボタンだけが消えるという怪現象に取りつかれていました。

FLTKの仕様にひたすら振り回されました。当たり前の話ですが、つじつまが合うように書いていけばいずれ解決するという感じです。時間的コスパはとてつもなく悪いですね。

FLTKはあらゆる機能をカバーしていますが、実装するのが本当に大変です。これでQtのような遅さだったらとてもやってられないです。

続けてFl_Multiline_Outputにスクロールバーを付けようとしたところ、ドラッグ&ドロップができなくなったので止めました。スクロールバーがなくてもカーソルを動かせば見えなくなった行を確認できます。

リサイズ時の背景黒化は解決し堅牢性もそれなりの水準に達したので、PNG用アプリとしてPyQt6からの移植を一旦完了とします。

#include "modalDialog.h"
#include <string>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Widget.H>
 
Fl_Button *button;

modalDialog::modalDialog(int w, int h, const char* title, const char* msg)
    : Fl_Window(w, h, title){

    int ws, hs, xs, ys;
    int margin_x = 70;
    int margin_y = 15;

    ws = 60;
    hs = 25;
    xs = w - ws - margin_x;
    ys = h - hs - margin_y;

    // メッセージ
    Fl_Box *g = new Fl_Box(0, 0, w, h-40, msg);
        
    // OKボタン
    button = new Fl_Button(xs, ys, ws, hs, "OK");
    button->parent(this);
    button->callback(PushButtonOK,this);
    button->down_box(FL_UP_BOX);

    resizable(this);
    end();
}

modalDialog::~modalDialog()
{
}

void modalDialog::PushButtonOK(Fl_Widget* widget,void* x)
{
    Fl_Group* window = widget->parent();
    window->hide();
}

背景色黒化対策

img = cv::imread(path,cv::IMREAD_UNCHANGED);

[C++] 31 FLTK:子ウィジェットの座標

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8, OpenCV 4.5.5]

自製したモーダルダイアログの位置設定に苦労しました。

親ウィンドウを指定したものの相対位置を設定する方法が分からず、仕方ないのでルートを把握しこれに加減して座標を定めました。

ただこの方法では親ウィンドウを動かしても子ウィンドウの位置は変わらずです。あくまでも応急措置になります。

とある大学の研究室がモーダルダイアログ作成のコード例を公開しており、最初はこれを参考にしましたが結局大半は自分で書く形になりました。

難解なC++を使ってコンピュータシミュレーションを研究するというのはさぞかし大変なことでしょう。

2022/4/6追記:
デモアプリの動作を確認したところ、子ウィンドウは親ウィンドウの相対位置を取れていませんでした。もしかしたら出来ない仕様なのかもしれません。
2022/7/28追記:
親ウインドウのルートから子ウィンドウの相対位置を設定できました。以下の記事に記しました。

<該当部分のみ>

dlg = new modalDialog(300, 150, "", "Attention");
        dlg->hotspot(window);
        int x = dlg->x_root();
        int y = dlg->y_root();
        cout<<"x_root "<< x <<" y_root "<< y <<endl;
        dlg->resize(x-10,y+110,300,150);
        dlg->set_modal();
        dlg->show();