[C++] 143 FLTK : ダブルクリック時の動作 その2 Fl::event_clicks()

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

[C++] 138の続きです。

Fl_File_Browserをダブルクリックして上下層のディレクトリをブラウズできるようにしました。

Fl_File_Chooser::directory関数がポインタ演算を使っていてとても読みにくくメンテナンス性も低くなるため、この関数は使わないようにしました。ポインタ演算はかなりハード寄りの表現なので、オープンソースでは使わない方がいいように思います。

extern Fl_Input *dirInput;
extern FileChooser* chooser;
extern const char*  dir_; // FileChooser内カレントディレクトリ

char selectPath[FL_PATH_MAX]; // 選択ファイルパス

vector<string> split(const string& str, char delim){
    std::istringstream iss(str);
    string tmp;
    vector<string> res;
    while (getline(iss, tmp, delim)) res.push_back(tmp);
    return res;
}

void FileBrowserCB()
{
	char *dirName;
	const char* delim2 = "/";

	// クリックしたディレクトリ名を取得
	dirName = (char *)FileBrowser->Fl_Browser::text(FileBrowser->Fl_Browser::value());
	cout << "dirName " << dirName << endl;

	if (!dirName) return;

	string dirNameStr = string(dirName);

	// ディレクトリ名を絶対パスに変換
	if (dirNameStr.compare("../") == 0){ // "../"をクリックした場合は上層へ移行
		string dirStr = string(dir_);
		cout << "dirStr " << dirStr << endl;

		vector<string> dirStrList = split(dirStr);
		dirStrList.pop_back();

		std::ostringstream os;
		std::copy(dirStrList.begin(), dirStrList.end(), std::ostream_iterator<std::string>(os, delim2));
		std::string dirUpper = os.str();

		cout << "dirUpper " << dirUpper << endl;

		strcat(selectPath, dirUpper.c_str());
	} else {
		strcat(selectPath, dir_);
		strcat(selectPath, dirName);
	}

	cout << "selectPath " << selectPath << endl; 

	if (Fl::event_clicks()) {
		cout << "ダブルクリックしました" << endl;

		// 選択したディレクトリの内容を表示し、カレントディレクトリdir_を更新
		if (std::filesystem::is_directory(selectPath)){
			FileBrowser->load(selectPath);

			char* dir_0 = new char[strlen(selectPath) + 1];
    		strcpy(dir_0, selectPath);
			dir_ = dir_0;
		}

		if (string(dir_).compare("/") == 0){
			cout << "この階層が上限です" << endl;
			return;
		}

		cout << "ダブルクリック後 dir_ " << dir_ << endl;

		selectPath[0] = '\0';
		return;
	} 

	//選択ディレクトリ名を表示
	inputFileName->value("");
	inputFileName->value(selectPath);

	// カレントディレクトリdir_確認
	cout << "dir_ " << dir_ << endl;

	selectPath[0] = '\0';
}

void btnOKCB(Fl_Return_Button*, void*)
{
	const char* dir = inputFileName->value();
	dirInput->value(dir);
	chooser->Fl_Window::hide();
}

[C++] 142 FLTK : 線の描画 Lineクラスの改良

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

前回の続きです。

他のウィジェットと同様に後から色や線種などを変えられるようにしました。

Lineクラス作成を通してFl_Widgetの仕組みが理解できたのは収穫でした。

右の縦線は角のある実線
#pragma once
#include <FLstd.h>
#include <cppstd.h>

class Line: public Fl_Widget
{
	int styletype_, stylewidth_;
	Fl_Color color_;
public:
	enum {
	FL_SOLID = 0 , FL_DASH = 1 , FL_DOT = 2 , FL_DASHDOT = 3 ,
	FL_DASHDOTDOT = 4 , FL_CAP_FLAT = 0x100 , FL_CAP_ROUND = 0x200 , FL_CAP_SQUARE = 0x300 ,
	FL_JOIN_MITER = 0x1000 , FL_JOIN_ROUND = 0x2000 , FL_JOIN_BEVEL = 0x3000
	};
	
	Line(int x,int y, int w,int h);
	~Line();
	void draw();

	Fl_Color color() const {return color_;}
	void color(Fl_Color bg) {color_ = bg;}

	vector<int> style() const {return {styletype_, stylewidth_};}
	void style(int type, int width) {styletype_= type; stylewidth_= width;}
};
#include <FLstd.h>
#include <cppstd.h>
#include <Line.h>

Line::Line(int x,int y, int w,int h):Fl_Widget(x,y,w,h){
	styletype_ = FL_SOLID;
	stylewidth_ = 1;
	color_	 = FL_GRAY;
}
Line::~Line(){}
void Line::draw(){
	Fl_Color color1 = color();
	fl_color(color1);

	vector<int> style1 = style();
	fl_line_style(style1[0],style1[1]);

	int x1 = x(), y1 = y();
	int x2 = x()+ w(), y2 = y()+ h();
	fl_line(x1,y1,x2,y2);
}
Line *line1 = new Line(40,171,309,0);

Line *line2 = new Line(349,10,0,195);
	line2->color(fl_rgb_color(0,0,0));
	line2->style(Line::FL_CAP_SQUARE,5);

Line *line3 = new Line(278,131,71,0);

Line *line4 = new Line(278,131,0,41);

[C++] 141 FLTK : 線の描画 Lineクラスの引数追加

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

[C++] 139の続きです。

前の記事では線の座標と長さを引数で設定しましたが、色や線種・太さを追加しました。

x()関数をヒントにrgb()関数とstyle()関数を作成し、これらの戻り値で設定値を取得できるようにしました。

引数が多いとコードを見てもどの引数が何を設定しているのか分かりにくいので、後ろ5つは省略可能にしました。デフォルトは太さ1の黒い実線になります。

右の縦線はデフォルト設定の黒い実線
#include <FLstd.h>
#include <cppstd.h>
#include <Line.h>

Line::Line(int x,int y, int w,int h, int r, int g, int b, int styletype, int stylewidth):Fl_Widget(x,y,w,h){
	r_ = r, g_ = g, b_ = b;
	styletype_ = styletype, stylewidth_ = stylewidth;
}
Line::~Line(){}
void Line::draw(){
	vector<int> rgb1 = rgb();
	fl_color(fl_rgb_color(rgb1[0], rgb1[1], rgb1[2]));

	vector<int> style1 = style();
	fl_line_style(style1[0],style1[1]);

	int x1 = x(), y1 = y();
	int x2 = x()+ w(), y2 = y()+ h();
	fl_line(x1,y1,x2,y2);
}
#pragma once
#include <FLstd.h>
#include <cppstd.h>

class Line: public Fl_Widget
{
	int r_, g_, b_;
	int styletype_, stylewidth_;
public:
	Line(int x,int y, int w,int h, int r = 0, int g = 0, int b = 0, int styletype = 0, int stylewidth = 1);
	~Line();
	void draw();
	vector<int> rgb() const {return {r_,g_,b_};}
	vector<int> style() const {return {styletype_, stylewidth_};}
};
#include "Line.h"

Line *line1 = new Line(40,171,309,0,211,207,217,0,1);
Line *line2 = new Line(349,10,0,195); // デフォルトの黒い実線
Line *line3 = new Line(278,131,71,0,211,207,217,0,1);
Line *line4 = new Line(278,131,0,41,211,207,217,0,1);

[C++] 140 ウィジェットツールキットwxWidgetsの導入

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

最近FLTKの独特な仕様に振り回されることが多くなってきたので、他のウィジェットツールキットも試してみます。

前からチェックしていたwxWidgetsを導入してみました。インストールだけならHomebrewでできます。最新の3.2.0をインストールしました。

brew install wxwidgets

ソースコードやサンプル、デモを確認したい場合はGitHubからダウンロードしてビルドします。3.2.0はなぜかpngフォルダが空なので3.1.7をダウンロードしました。

コマンドは以下の通りです。

# 本体のビルド
mkdir build-cocoa-debug
cd build-cocoa-debug

../configure --with-opengl --disable-shared --enable-monolithic \
--with-libjpeg --with-libtiff=builtin --with-libpng --with-zlib \
--with-mac --disable-sdltest --enable-unicode --enable-display \
--enable-propgrid --disable-webview --prefix=/Users/[ユーザ名]/Dev/wxWidgets-staticlib \
CXXFLAGS="-std=c++0x" --with-libiconv=/usr

make

# サンプルとデモのビルド
cd samples; make;cd ..
cd demos;   make;cd ..

wxWidgetsはOSのLook&Feelを採用しており、macOSの場合はCocoa風になります。

[C++] 139 FLTK : 線の描画 fl_line

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

4月に書いた[C++] 48の記事以降、その内容が最善な方法ではないと自覚していて、ずっともやもやしていたのですが、ようやくシンプルに線を描けるようになりました。

Fl_Widgetを継承したLineクラスのdraw()関数内でx()関数、y()関数、w()関数、h()関数を使うことで解決しました。

x()ではなくgetx()だったら瞬時に意味を理解していたと思いますが、引数の有無でgetとsetを使い分けるのがFLTKの仕様ですから慣れるしかないです。

FLTKはいいツールだというのに、こういった癖の強いところでC++ユーザーに受け入れられないのではと思います。せめてLineクラスは標準Widgetとして用意しておくべきでしょう。

GUI内に4本の線を描画
#include <Line.h>

Line::Line(int x,int y, int w,int h):Fl_Widget(x,y,w,h){
}
Line::~Line(){}
void Line::draw(){
	fl_color(fl_rgb_color(211,207,217));
	fl_line_style(0,1);
	int x1 = x(), y1 = y();
	int x2 = x()+ w(), y2 = y()+ h();
	fl_line(x1,y1,x2,y2);
}
#pragma once
#include <FLstd.h> // 自製
#include <cppstd.h> // 自製

class Line: public Fl_Widget
{
public:
	Line(int x,int y, int w, int h);
	~Line();
	void draw();
};
#include "Line.h"

Line *line1 = new Line(40,171,309,0);
Line *line2 = new Line(349,10,0,195);
Line *line3 = new Line(278,131,71,0);
Line *line4 = new Line(278,131,0,41);

参考サイト

[C++] 138 FLTK : ダブルクリック時の動作 Fl::event_clicks()

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

ダブルクリック時の条件分岐にはFl::event_clicks()を使います。Fl_File_Chooser2.cppのソースコードを読んでその存在を知りました。公式サイトのEvents handling functionsに説明があります。

FLTKは結構使い込んでいますが、まだまだ知らないことだらけです。

void FileBrowserCB()
{
	char *fileName;

	if (Fl::event_clicks()) {
		cout << "ダブルクリックしました" << endl;
		// 以下、ダブルクリックした時の動作を書く
	}

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

	selectPath[0] = '\0';

	// ディレクトリ名にファイル名を結合
	strcat(selectPath ,appDir);
	strcat(selectPath ,"/");
	strcat(selectPath ,fileName);

	cout << "selectPath " << selectPath << endl; 

	if (browserType == 1) { // FL_MULTI_BROWSERになっている場合
		char* name = new char[strlen(selectPath) + 1];
    	strcpy(name, selectPath);
		selectPaths.push_back(name);
	}

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

}

[C++] 137 FLTK : ファイル検索アプリのグレードアップ着手 FileChooser

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

先月7月上旬で開発を中断していたファイル検索アプリをさらにグレードアップさせることにしました。

とりあえずカラーアプリ製作時に作成したFileChooserを導入しましたが、これは固定したディレクトリからファイルを選択する機能しかないため、ファイルブラウザとしての機能を追加する必要があります。

[C++] 136 FLTK : Final Cut Proのライブラリ掃除 改良版

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE, C++17]

前回の続きです。

サイズ的にはあまり重要ではありませんが、Thumnail Mediaディレクトリも空にするようにしました。

これでFinal Cut Library ManagerというアプリのClean機能と同等になりました。

void FCPClean(){
    string dirPath = "/movie"; // ライブラリ保存ディレクトリ
    vector<string> paths = getFilePath(dirPath, "fcpbundle");

    int num = 1;
    for (string path:paths){
        cout << num << " " << path << endl;

        vector<string> dirs = getDirPath(path);

        int num2 = 1; 
        for (string dir:dirs){

            if (dir.find("High Quality Media") != std::string::npos){
                cout << "High Quality Mediaディレクトリ " << num2 << " " << dir << endl;

                struct stat statBuf;
                int detect = stat(dir.c_str(), &statBuf);
                if (detect == 0){
                    vector<string> dirs2 = getDirPath(dir);
                    for (string dir:dirs2){
                        cout << "削除対象dir " << dir << endl;
                        std::filesystem::remove_all(dir);
                    }
                } else {
                    cout << "ディレクトリは削除済みです" << endl;
                }
                num2 += 1;
            }
        }

        int num3 = 1; 
        for (string dir:dirs){

            if (dir.find("Thumbnail Media") != std::string::npos){
                cout << "Thumbnail Mediaディレクトリ " << num3 << " " << dir << endl;

                struct stat statBuf;
                int detect = stat(dir.c_str(), &statBuf);
                if (detect == 0){
                    vector<string> dirs2 = getDirPath(dir);
                    for (string dir:dirs2){
                        cout << "削除対象dir " << dir << endl;
                        std::filesystem::remove_all(dir);
                    }
                } else {
                    cout << "ディレクトリは削除済みです" << endl;
                }
                num3 += 1;
            }
        }
        num += 1;
    }

    output_line->insert("FCP Clean完了!\n");
}

[C++] 135 FLTK : Final Cut Proのライブラリ掃除

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE, C++17]

Final Cut ProのライブラリにあるHigh Quality Mediaディレクトリの中身を削除する機能をビデオツールアプリに追加しました。

これでライブラリ肥大化の原因であるHigh Quality Mediaディレクトリが全て空になります。

#include <sys/stat.h>

vector<string> getFilePath(string dir, string ext) {
    glob_t globbuf;
    vector<string> files;

    string suffix = "/*." + ext;
    cout << "suffix " << suffix << endl;

    glob((dir + suffix).c_str(), 0, NULL, &globbuf);

    for (int i = 0; i < globbuf.gl_pathc; i++) {
        files.push_back(globbuf.gl_pathv[i]);
    }

    globfree(&globbuf);

    return files;
}

vector<string> getDirPath(string dir) { // 再帰的に検索
    vector<string> dirs;

    for (const std::filesystem::directory_entry& dir_entry : std::filesystem::recursive_directory_iterator(dir)) {
        if (dir_entry.is_directory()){
            dirs.emplace_back(dir_entry.path().string());
        }
    }
    return dirs;
}

void FCPClean(){
    string dirPath = "/movie"; // ライブラリ保存ディレクトリ
    vector<string> paths = getFilePath(dirPath, "fcpbundle");

    int num = 1;
    for (string path:paths){
        cout << num << " " << path << endl;

        vector<string> dirs = getDirPath(path);

        int num2 = 1; 
        for (string dir:dirs){

            if (dir.find("High Quality Media") != std::string::npos){
                cout << "High Quality Mediaディレクトリ " << num2 << " " << dir << endl;

                struct stat statBuf;
                int detect = stat(dir.c_str(), &statBuf);
                if (detect == 0){
                    vector<string> dirs2 = getDirPath(dir);
                    for (string dir:dirs2){
                        cout << "削除対象dir " << dir << endl;
                        std::filesystem::remove_all(dir);
                    }
                } else {
                    cout << "ディレクトリはありません" << endl;
                }
                num2 += 1;
            }
        }
        num += 1;
    }
    output_line->insert("FCP Clean完了!\n");
}
--------------------------------------------------
出力例
--------------------------------------------------
suffix /*.fcpbundle
1 /movie/blog.fcpbundle
High Quality Mediaディレクトリ 1 /movie/blog.fcpbundle/programming/Render Files/High Quality Media
削除対象dir /movie/blog.fcpbundle/programming/Render Files/High Quality Media/testA
削除対象dir /movie/blog.fcpbundle/programming/Render Files/High Quality Media/testB
High Quality Mediaディレクトリ 2 /movie/blog.fcpbundle/programming/Render Files/High Quality Media/testA
ディレクトリはありません
High Quality Mediaディレクトリ 3 /movie/blog.fcpbundle/programming/Render Files/High Quality Media/testB
ディレクトリはありません

22/8/13追記:
改良版を作成しました。

[C++] 134 FLTK : ゼロ埋め日付の作成

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

vector<vector<string>>をCSVファイルとして保存する際、自動的にファイル名に日付と通し番号が付加されるようにしました。通し番号はゼロ埋め2桁にしたので開始番号01で99個まではソートが乱れることはありません。

購入したFinal Cut Proで早速操作動画を4トラックにて作成してみました。DTMの方はCubase Proを使っているのでプロジェクトの扱い方は何となく分かります。

ただシェイプの点滅を作成するのは少々面倒ですから簡単にできるらしいMotionを使ってみたいのですが、App Storeのレビューが散々なので様子見です。

vector<string> get_file_path(string dir, string str) {
    glob_t globbuf;
    vector<string> files;

    string suffix = "/" + str + "*.*";

    cout << "suffix " << suffix << endl;

    glob((dir + suffix).c_str(), 0, NULL, &globbuf);

    for (int i = 0; i < globbuf.gl_pathc; i++) {
        files.push_back(globbuf.gl_pathv[i]);
    }

    globfree(&globbuf);

    return files;
}

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

    time_t today = time(NULL);
    struct tm *pnow = localtime(&today);
    
    int year = pnow->tm_year-100;
    int month = pnow->tm_mon + 1;
    int day = pnow->tm_mday;

    // 日付の0埋め文字列化
    std::ostringstream os;
    os << std::setfill('0') << std::setw(2) << year;
    os << std::setfill('0') << std::setw(2) << month;
    os << std::setfill('0') << std::setw(2) << day;
    
    string today_str = os.str();

    cout << "now_str " << today_str << endl;

    // アプリ用ディレクトリの取得
    homedir = getenv("HOME");
    cout << "homedir " << homedir << endl;

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

    cout << "appdir " << appdir << endl;

    vector<string> files_today = get_file_path(appdir, today_str);

    int count = files_today.size();

    // 通し番号の0埋め文字列化
    std::ostringstream os2;
    os2 << std::setfill('0') << std::setw(2) << count + 1;
    string count_str = os2.str();
    
    string filename = appdir + "/" + today_str + "_list" + count_str + ".csv";

    cout << "filename " << filename << endl;

    csvProcessChar::make(filename.c_str(),selectColorList);
}