[C++] 126 FLTK : FileChooserの作成 最低限の機能実装

[M1 Mac, Big Sur 11.6.8, FLTK 1.3.8, NO IDE]

自製FileChooserに以下の機能を実装しました。元ソースコードを移植しようした時とは打って変わってシンプルな内容になりました。一から自分の好きなように書くとあっさりでした。

1.アプリ用ディレクトリの全ファイルをFl_File_Browserに表示。
2.ファイルをクリックしてファイルパスをFl_File_Inputに表示。
3.FileChooserを閉じて、カラーリストファイルを読み込む。

後はFl_File_BrowserのFilter機能、Closeボタン、プレビュー機能を実装して一旦完了とする予定です。

#include <FileChooser.h>
#include <FileChooser2.h>
#include <btnAction.h>

int BrowserType;
char pathname[FL_PATH_MAX]; // アプリ用ディレクトリ格納
char pathname2[FL_PATH_MAX]; // 選択ファイルパス格納

void fileListShow(const char* dirname)
{	
	// 設定ディレクトリの読込&全ファイル表示
	FileBrowser->load(dirname);

	// 設定ディレクトリをpathnameへコピー
	fl_filename_absolute(pathname, sizeof(pathname), dirname);
	cout << "pathname " << pathname << endl;

	inputFileName->value(dirname);
}

void BrowserTypeSet(int t) {
	cout << "int t " << t << endl;

	BrowserType = t;
	if (t == FileChooser::MULTI){
		FileBrowser->type(FL_MULTI_BROWSER);
	}else{
		FileBrowser->type(FL_HOLD_BROWSER);
	}

	if (t == FileChooser::CREATE){
		btnNew->activate();
	}else{
		btnNew->deactivate();
	}

	if (t == FileChooser::DIRECTORY){
		FileBrowser->filetype(Fl_File_Browser::DIRECTORIES);
	}else{
		FileBrowser->filetype(Fl_File_Browser::FILES);
	}
}

void FileBrowserCB()
{
	char *filename;

	// クリックしたファイルのファイル名を取得
	filename = (char *)FileBrowser->Fl_Browser::text(FileBrowser->Fl_Browser::value());
	cout << "filename " << filename << endl; 
	
	if (!filename) return;

	// pathname2を空にする
	pathname2[0] = '\0';

	// ディレクトリ名にファイル名を結合
	strcat(pathname2 ,pathname);
	strcat(pathname2 ,"/");
	strcat(pathname2 ,filename);

	cout << "pathname2 " << pathname2 << endl; 

	//ファイルパスを表示
	inputFileName->value("");
	inputFileName->value(pathname2);

}

void btnOKCB(Fl_Return_Button*, void*)
{
	// FileChooserを閉じる
	chooser->Fl_Window::hide();
}

[C++] 125 FLTK : FileChooserの作成 Fl_File_Browser

[M1 Mac, Big Sur 11.6.8, FLTK 1.3.8, NO IDE]

元ソースコードを活用してFileChooserを作成しようとしましたが、どうやらC++03以前のレガシーC++で書かれているようで、加えて変数や関数の命名が抽象的かつ主体・客体が混在していてとても読みにくいため、Fl_File_Chooserのコンストラクタ以外はほぼ一から作成することにしました。

とりあえずFl_File_Browserに指定ディレクトリ内のファイルを全て表示させました。

ソースコードを読んでみて、FLTKの開発が遅々として進まない理由が少し分かったような気がします。

[C++] 124 FLTK : FileChooserの作成 Fl_File_Chooser

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

Fl_File_Chooserの最大の欠点は相対位置を取れず常に座標(0, 0)に表示される所です。

そこでFl_File_ChooserとFl_Windowを多重継承したFileChooserクラスを作成しました。コンストラクタにあるFl_Double_Windowは削除して書き換えます。とりあえずガワだけFl_File_Chooserに似せて作ってみました。

これから操作系を実装していきます。

int x_win = window->x_root();
int y_win = window->y_root();
cout<<"x_win "<< x_win <<" y_win "<< y_win <<endl;

FileChooser* chooser = new FileChooser(appdir.c_str(), 
                        "*.csv",
                        FileChooser::SINGLE,
                        "File_Chooser Load",490,380
                        );
    
chooser->resize(x_win+85,y_win+50,490,380);
chooser->set_modal();
chooser->show();

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

[C++] 123 FLTK : Fl_File_Chooserから複数ファイルを削除 

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

Fl_File_Chooserから複数のファイルを選択して削除できるようにしました。

第3引数で単数選択(SINGLE)、複数選択(MULTI)、新規ファイル作成(CREATE)、ディレクトリ選択(DIRECTORY)を設定できます。

#include <filesystem>
#include <FileChooser.h> // 自製
namespace fs = std::filesystem;

void deleteFavList(Fl_Widget*, void*){
    cout << "deleteFavList" << endl;

    homedir = getenv("HOME");
    cout << "homedir " << homedir << endl;

    string cs = "/ColorSample";
    string appdir = string(homedir) + cs;

    if (!fs::exists(appdir)){
        fs::create_directory(appdir);
    }

    FileChooser* chooser = new FileChooser(appdir.c_str(), // 呼び出すディレクトリ
                        "*.csv",      // フィルタ
                        Fl_File_Chooser::MULTI, // 複数選択タイプ
                        "File_Chooser Delete");   // タイトル
    
    chooser->show();

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

    // 選択ファイル数を取得
    int num_select = chooser->count();

    if (num_select == 0){
        return;
    }

    // 選択ファイルのパスを出力し削除
    for (int n = 1; n < num_select +1; ++n){
        const char* filePath = chooser->value(n);
        cout << "filePath" << n << " " << filePath << endl;
        remove(filePath);
    }

    delete chooser;
}

[C++] 122 文字列末尾の判定および削除

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

備忘として残しておきます。

// 文字列末尾が","であれば削除する
string str = "xxxxx";

int length = str.size();
char last = str.at(length -1);
string last_str{last}; // 初期化リストを用いる手法でchar単体→string変換
   
if (last_str.find(",") != std::string::npos){
    str.pop_back();
}

[C++] 121 FLTK : CSVファイルの読込 Fl_File_Chooser

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

よく使う色をまとめたCSVファイルを作成しておいてアプリに読み込ませました。

RGBで登録するとカンマがvectorに入り込むため処理が少しややこしくなります。

#include <csvProcessChar.h>
#include <cppstd.h> // 自製C++標準ライブラリ群

vector<vector<const char*>> csvProcessChar::load(string path) {
    string eles;
    string ele;
    vector<vector<const char*>> vec2D;
    
    // csvファイル指定
    std::ifstream file(path);

    while (getline(file, eles)) {
        vector<const char*> vec;  
        std::istringstream line(eles);
        vector<string> rgb;

        cout << "eles " << eles << endl;

        int num = 1;
        if (eles.find("RGB") == std::string::npos){
            while (getline(line, ele, ',')) {
                if (num == 1){
                    const char* name_c = ele.c_str();
                    char* name_c2 = new char[strlen(name_c) + 1];
                    strcpy(name_c2, name_c);
                    vec.push_back(name_c2);
                } else if (num == 2){
                    const char* roma_c = ele.c_str();
                    char* roma_c2 = new char[strlen(roma_c) + 1];
                    strcpy(roma_c2, roma_c);
                    vec.push_back(roma_c2);
                } else {
                    const char* code_c = ele.c_str();
                    char* code_c2 = new char[strlen(code_c) + 1];
                    strcpy(code_c2, code_c);
                    vec.push_back(code_c2);
                    
                }
                num += 1;
            }
        } else {
            // RGBの場合
            while (getline(line, ele, ',')) {
                if (num == 1){
                    const char* name_c = ele.c_str();
                    char* name_c2 = new char[strlen(name_c) + 1];
                    strcpy(name_c2, name_c);
                    vec.push_back(name_c2);
                } else if (num == 2){
                    const char* roma_c = ele.c_str();
                    char* roma_c2 = new char[strlen(roma_c) + 1];
                    strcpy(roma_c2, roma_c);
                    vec.push_back(roma_c2);
                } else {
                    rgb.push_back(ele); // 3番目以降は一旦vectorへ入れてから後で結合する
                }
                num += 1;
            }
        }
        if (!(rgb.size() == 0)){
            cout << "RGBあり" << endl;
            const char* delim = ","; // 区切り文字

            // vector要素結合
            std::ostringstream os;
            std::copy(rgb.begin(), rgb.end(), std::ostream_iterator<string>(os, delim));
            string str = os.str();

            const char* code_c = str.c_str();
            char* code_c2 = new char[strlen(code_c) + 1];
            strcpy(code_c2, code_c);
            vec.push_back(code_c2);

        }
        vec2D.push_back(vec);
    }

    return vec2D;
}

[C++] 119 FLTK : 名前を付けて保存 Fl_File_Chooser

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

色表示履歴をcsvファイルに保存する際、自分でファイル名を付けることもできるようにしました。Fl_File_ChooserをCREATEタイプで使用します。

OKボタン以外をクリックすると異常終了するので、まだ手を入れる必要があります。

static Fl_Menu_Item	items[] = {
        { "★", 0, 0, 0, FL_SUBMENU },
        { "読込", 0, loadFavList, 0, 0 },
        { "保存", 0, 0, 0, FL_SUBMENU },
        { "名前を付けて保存", 0, saveFavListManu, 0, 0 },
        { "自動保存", 0, saveFavListAuto, 0, 0 },
        { 0 },
        { "消去", 0, deleteFavList, 0, 0 },
        { 0 },
        { "履歴", 0, 0, 0, FL_SUBMENU },
        { "消去", 0, deleteHistory, 0, 0 },
        { 0 },        
    #ifdef DEV
        { "開発", 0, 0, 0, FL_SUBMENU },
        { "開発モード", 0, showSubWindow, 0, 0 },
        { 0 }
    #else
    #endif
    };
    
    Fl_Sys_Menu_Bar *menubar;
    menubar = new Fl_Sys_Menu_Bar(0, 0, 60, 20);
    menubar->box(FL_FLAT_BOX);
    menubar->menu(items);
--------------------------------------------------
#include <csvProcessChar.h>
#include <filesystem>

namespace fs = std::filesystem;
using std::cout; using std::endl;
using std::string; using std::to_string;

vector<vector<const char*>> selectColorList;

void saveFavListManu(Fl_Widget*, void*){
    cout << "saveFavListManu" << endl;

    homedir = getenv("HOME");
    cout << "homedir " << homedir << endl;

    string cs = "/ColorSample";
    string appdir = string(homedir) + cs;

    if (!fs::exists(appdir)){
        fs::create_directory(appdir);
    }

    Fl_File_Chooser chooser(".",      // directory
                            "*",      // filter
                            Fl_File_Chooser::CREATE, // chooser type
                            "File_Chooser");   // title
    chooser.show();

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

    const char* filePath = chooser.value();

    if (filePath == nullptr){
        return;
    }

    csvProcessChar::make(filePath,selectColorList);
}

[C++] 118 FLTK : 色表示履歴をCSVファイルに保存

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

色表示履歴(2次元vector)をホームディレクトリ直下のアプリ用ディレクトリにCSVファイルとして保存できるようにしました。

前々回記事で作成した静的ライブラリのconst char*版 csvProcessChar.aを使用しています。今のところappファイルのサイズは1.8MBです。

ファイル名は仮にtest.csvとしていますが、2208011200_FavList.csvのように日時を付加したファイル名にしたいところです。

さすがに秒数まで加えると長すぎるので付けません。ただ同じ分内で複数作成した時に上書きにならないよう工夫が必要になります。

static Fl_Menu_Item	items[] = {
        { "★", 0, 0, 0, FL_SUBMENU },
        { "読込", 0, loadFavList, 0, 0 },
        { "保存", 0, saveFavList, 0, 0 },
        { "消去", 0, deleteFavList, 0, 0 },
        { 0 },
        { "履歴", 0, 0, 0, FL_SUBMENU },
        { "消去", 0, deleteHistory, 0, 0 },
        { 0 },        
    #ifdef DEV
        { "開発", 0, 0, 0, FL_SUBMENU },
        { "開発モード", 0, showSubWindow, 0, 0 },
        { 0 }
    #else
    #endif
    };
    
Fl_Sys_Menu_Bar *menubar;
    menubar = new Fl_Sys_Menu_Bar(0, 0, 60, 20);
    menubar->box(FL_FLAT_BOX);
    menubar->menu(items);
--------------------------------------------------
#include <csvProcessChar.h>
#include <filesystem>

namespace fs = std::filesystem;
using std::cout; using std::endl;
using std::string; using std::to_string;

vector<vector<const char*>> selectColorList;

void saveFavList(Fl_Widget*, void*){
    cout << "saveFavList" << endl;

    homedir = getenv("HOME");
    cout << "homedir " << homedir << endl;

    string cs = "/ColorSample";
    string appdir = string(homedir) + cs;

    if (!fs::exists(appdir)){
        fs::create_directory(appdir);
    }

    string path = appdir + "/test.csv";
    csvProcessChar::make(path,selectColorList);
}

[C++] 117 ホームディレクトリの取得、ディレクトリの存在確認・作成

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

ホームディレクトリ直下に任意のディレクトリを作成するコードを書きました。直下にColorSampleディレクトリがなければ、これを作成します。

filesystemヘッダはC++17以降で使えます。

#include <iostream>
#include <vector>
#include <filesystem>

namespace fs = std::filesystem;

using std::string; using std::vector;
using std::cout; using std::endl;

int main () {
    const char *homedir;

    homedir = getenv("HOME");
    cout << "homedir " << homedir << endl;

    string cs = "/ColorSample";
    string appdir = string(homedir) + cs;

    if (!fs::exists(appdir)){
        fs::create_directory(appdir);
    }
}

[C++] 116 2次元vectorからCSVファイルを作成・読込する動的・静的ライブラリを作る

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

前回、前々回の成果を合わせてCSVファイルを作成・読み込みする動的・静的ライブラリを作成しました。

#include <iostream>
#include <fstream>
#include <vector>
#include <utility>
#include <sstream>

using std::string; using std::vector;
using std::cout; using std::endl;

class csvProcess {
public:
static void make(string path, vector<vector<string>> vec2D);
static vector<vector<string>> load(string path);
};
#include <csvProcess.h>

vector<vector<string>> csvProcess::load(string path) {
    string eles;
    string ele;
    vector<vector<string>> vec2D;

    // csvファイル指定
    std::ifstream file(path);

    while (getline(file, eles)) {
        vector<string> vec;  
        std::istringstream line(eles);

        while (getline(line, ele, ',')) {
            vec.push_back(ele);
        }
        
        vec2D.push_back(vec);
    }

    int line_num = 1;
    for (const auto &items : vec2D) {
        for (const auto &item : items) {
            cout << line_num << " " << item << endl;
        }
        line_num += 1;
    }

    return vec2D;
}

void csvProcess::make(string path, vector<vector<string>> vec2D) {
    // ファイル名指定
    std::ofstream file(path);

    // データ取り込み
    for (const auto &data : vec2D) {
        int num = 0;
        for (const auto &ele : data){
            int len = data.size();
            if (num < len -1){
                file << ele << ",";
            } else {
                file << ele << "\n";
            }
            num += 1;
        }
    }

    // ファイル出力完了
    file.close();
}
# 動的ライブラリ作成コマンド
clang++ -dynamiclib -o csvProcess.dylib csvProcess.cpp \
-I/code/cpp/mylib/include -std=c++17

# 静的ライブラリ作成コマンド
clang++ -std=c++17 -I/code/cpp/mylib/include -g -o csvProcess.o \
-c csvProcess.cpp

ar r csvProcess.a csvProcess.o
#include <csvProcess.h>

int main () {
    vector<vector<string>> vec2D;

    // vector作成
    vector<string> data1 = {"cadetblue","-","0x5F9EA0"};
    vector<string> data2 = {"若竹色","わかたけいろ","0x68BE8D"};
    vec2D.push_back(data1);
    vec2D.push_back(data2);

    string path = "test.csv";

    csvProcess::make(path, vec2D);
}