[C++] 77 FLTK : OpenGLグラフィックの切替

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

複数のOpenGLグラフィックをボタンで切替できるようにしました。

Windowに埋め込んだままでの切替は今の私のスキルではできず、2枚目からは別ウインドウになります。

glgraph= new GL_Graph(380, 10, 190, 190, nullptr);
        
next_btn = new Fl_Button(575,85,20,20,"[絵文字]");
next_btn->callback(next_cb);
int box_num = 0; グラフィック番号の初期値
int box_nums = 2; グラフィックの個数

void next_cb(Fl_Widget*, void*) {
    box_num +=1;
    if (box_num >= box_nums){
        box_num = 0;
    }

    switch(box_num){
        case 0:
        {
            delete(glgraph2);
            glgraph= new GL_Graph(380+100, 10+100, 190, 190, nullptr);
            glgraph->show(); // draw,redrawではうまくいかず
            break;
        }
        case 1:
        {
            delete(glgraph);
            glgraph2= new GL_Graph2(380+100, 10+100, 190, 190, nullptr);
            glgraph2->show();
            break;
        }
    }

    output_line->insert((to_string(box_num)).c_str());
    output_line->insert("\n");
}

[C++] 76 FLTK : OpenGLグラフィックの埋め込み

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

FLTKアプリにOpenGLグラフィックを埋め込みました。

アプリ開発の合間にグラフィックのコードを試し書きして描画したりできるので活用したいです。


#pragma once
#include <cppstd.h> //自製標準ヘッダファイル群
#include <FLstd.h> //自製FLヘッダファイル群
#include <GL/glew.h> //この順序で固定
#include <FL/glut.h> //この順序で固定

const GLfloat MY_LIGHT0_POS[4] = { 2.0f, 4.0f, 1.0f, 0.0f };
const GLfloat MY_LIGHT_AMBI[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
const GLfloat MY_LIGHT_DIFF[4] = { 0.9f, 0.9f, 0.9f, 1.0f };
const GLfloat MY_LIGHT_SPEC[4] = { 0.2f, 0.2f, 0.2f, 1.0f };

class GL_Box : public Fl_Gl_Window
{
    public:
    GL_Box(int x_, int y_, int w_, int h_, const char* l);
    ~GL_Box();

    void InitGL(void);
    void Resize(int w, int h);
    void Display(void);

    private:
    void draw(void);
};

参考サイト

[C++] 75 FLTK : カスタムウィジェットの埋め込み

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

ファイル検索アプリの右上空白部分に簡易タイマーを埋め込んでみました。スタート、ストップボタンを付けると実用的になります。

FLTKやOpenGLによる小品グラフィックスを埋め込んで、遊びながらスキルアップというのも楽しそうです。

参考サイト

[C++] 74 ファイル検索アプリ : Fl_Check_Button

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

ファイル検索アプリに機能を追加しました。

recボタンにチェックを入れると再帰的(recursive)なファイルリストを作成します。リストにあるファイルを削除すると空のディレクトリツリーが作成できます。

GUIではglobとありますが、実際はstd::filesystem::directory_iteratorを使っています。

いつの間にかおなじみの.DS_Storeファイルができていました。コマンドで作成しないように設定しておきます。

右上の空白は何を入れるか決めていません。何か図形を動かしてみても面白そうです。

[C++] 73 ディレクトリ内ファイルリストの作成 C++17

[M1 Mac, Big Sur 11.6.7, clang 13.0.0]

前回はワイルドカードを使ってファイルのリストを作成しましたが、全てのファイルをリスト化する場合はdirectory_iteratorクラスなどを使います。この方法はC++17から標準で採用されています。

これについては後発学習組で助かりました。C++14以前の標準ライブラリで関数を作るのは結構手間でしょうから。

ところでBoost C++ ライブラリの現在の立ち位置が気になります。C++11以降、標準ライブラリへの採用が増えていて存在感が薄れているような。プログラミング教育や競技プログラミング界隈(一部を除く)ではまるで存在しないかのように扱われています。これは前からかな。

私自身は可読性、メンテナンス性、コンパイル速度への影響を考慮し、アプリ製作では今のところ使わない方針です。個人的にはsplit関数を早く標準に採用して欲しいところです。

#include <filesystem>
#include <string>
#include <vector>

vector<string> get_file_path(string dir) {
    vector<string> files;

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

// サブディレクトリ内も再帰的に検索する場合
vector<string> get_file_path2(string dir) {
    vector<string> files;

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

参考サイト

[C++] 72 パターン一致パスのリスト生成 glob.h 

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

ファイル検索アプリに新機能を追加しました。

glob.hを使って、Pythonのglob.glob()と同じようにパターン一致パスのリストを作成できるようにしました。このアプリでは*.*でディレクトリ内の全てのファイルパスをリストにします。

vector<string>をconst char*に変換するのに少し手間がかかりました。後々のために関数にしておきたいです。

またFl_Multiline_Outputが右端で折り返してくれないという仕様が発覚しましたが、ALL選択で全てコピーできるためとりあえずこれでOKとします。

#include <glob.h>

stringstream paths;

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

    glob((dir + "/*.*").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> files = get_file_path(string(dir_char));

for (size_t i = 0; i < files.size(); ++i) {
    if (i == 0){
        paths << "{" ;
        paths << "'" << files[i] << "'";
    } else {
        paths << ", " << "'" << files[i] << "'";
    }
}
paths << "}";
output_line->insert((paths.str()).c_str());

参考サイト

[C++] 71 FLTK : スクロールバーの配置 Fl_Scroll

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

Fl_Multiline_Outputにスクロールバーを配置しました。

コード上はまずFl_Scrollを配置して、それよりもサイズの大きいFl_Multiline_Outputを当てはめる形になります。驚くほど簡単です。

ただしこの方法では未出力状態でもスクロールバーが表示されます。Fl_ScrollとFl_Multiline_Outputを同じサイズにすればエリア外表示が発生した時点でスクロールバーが出現するのではと考えましたが、スクロールバー自体が設定できませんでした。

この点については今後の課題とし、先に進みます。

Fl_Scroll scroll(10,210,580,263);
output_line = new Fl_Multiline_Output(10,210,581,264,""); // 縦横1ピクセルだけ大きくした
output_line->textsize(12);
scroll.end();

2022/6/30追記:
type関数でスクロールバーの種類を選べることが分かりました。しかしBOTH_ALWAYS = 7だけデフォルト表示のはずがBOTH=3でも同じように表示されます。結局解決には至りませんでした。

Fl_Scroll scroll(10,210,580,263);
scroll.type(3); //BOTH
output_line = new Fl_Multiline_Output(10,210,581,264,"");
output_line->textsize(12);
scroll.end();

[C++] 70 ファイル検索アプリ : 標準出力の取得関数

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

前回の続きです。

標準出力を取得する関数を作成しました。個人的に使用頻度は高くなりそうです。

製作中のアプリは今のところたった250KBの超軽量です。リソースが潤沢な時代ではありますが、軽さと速さへの追求はやめられないです。

ちなみに近々統合予定のJavaScriptアプリ(ファイルを削除してディレクトリツリーを作るだけの単機能)は210MBの超重量です。単機能なのに800倍とは恐れ入ります。

もうGUIアプリ作成についてはこのままJavaには戻らず、C++とObjective-Cにどっぷり浸かりそうです。

void func_stdout(string cmd){
    FILE* fp = popen(cmd.c_str(), "r");

    char buffer[512];
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        output_line->insert(buffer);
        std::cout << buffer;
    }
    pclose(fp);
}

[C++] 69 ファイル検索アプリ : 標準出力の取得 popen関数

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

ファイル検索アプリの検索結果を標準出力から取得し、Fl_Multiline_Outputに表示させました。

標準出力をわざわざ取得しようとする方はあまり多くないようで、調査に少々手間取りました。最初はGCC限定の方法をそれと気づかず試したために寄り道してしまいました。

これでメインのGUIとコンソールを一体化させることができました。

あとはスクロールバーを配置して、新機能を追加します。

ここ1週間ほどゲームプログラミングの学習で疲弊していましたが、やはり受け身の勉強よりも作りたいものを好きなように作る方が断然楽しいですね。

他人様の書いたコードを読解して手を入れるのは、例えNO IDE環境でもつらいものがあります。ビルドが異様に遅くてストレスフルなIDE環境でしたら早々に離脱しているでしょう。

#include <cstdio>
#include <cstdlib>
#include <iostream>

stringstream cmd;

const char* grep_char = grep_input->value();
cmd << "grep -rI --ignore-case " << string(grep_char) << " " << string(dir_char);
std::cout << cmd.str() << endl;

FILE* fp = popen((cmd.str()).c_str(), "r");

char buffer[1024];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
       output_line->insert(buffer);
       std::cout << buffer;
}
pclose(fp);

[C++] 68 動的ライブラリdylibの作成 初回 split

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

前回[C++] 67で作成した関数をsplitクラスにまとめて動的ライブラリdylibにしました。このファイルはプロジェクトとは別に外部クラス用のmylibディレクトリに保管します。dylibファイルはオブジェクトファイルと同等と考えると理解しやすいです。

ライブラリのヘッダファイルをincludeして、クラスをオブジェクト化すると、クラスのメンバ関数として使用することができます。

これで他のプログラムからもsplitクラスが利用可能になりました。

clang++ -dynamiclib -o split.dylib split.cpp -I/mylib/include
#ifndef SPLIT_H
#define SPLIT_H

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <vector>

using std::string; using std::vector;

class split{
    
public:
// 文字列を文字列delで分割しリスト化する関数
vector<string> splits(string str, char del);

// 分割リストのstart番目からend番目までの要素を結合する関数
string splitjoin(string str, char del, int start, int end);

// 作業ディレクトリを変更する関数
int chCWD(string str, char del, int start, int end);
};

#endif
#include "split.h"

using std::string; using std::vector;

// 文字列を文字列delで分割しリスト化する関数
vector<string> split::splits(string str, char del) {
    int first = 0;
    int last = str.find_first_of(del);
 
    vector<string> result;
 
    while (first < str.size()) {
        string subStr(str, first, last - first);
 
        result.push_back(subStr);
 
        first = last + 1;
        last = str.find_first_of(del, first);
 
        if (last == string::npos) {
            last = str.size();
        }
    }
    return result;
}
// 分割リストのstart番目からend番目までの要素を結合する関数
string split::splitjoin(string str, char del, int start, int end){
    string result2;
    vector<string> list = split::splits(str, del);

    vector<string> list2;
    for (int i = 0; i < list.size(); i++){
        if (end < 0){
            end += list.size();
        }
        if (i >= start && i <= end) {
            list2.push_back(list[i]);
        }
    }

    for (int i = 0; i< list2.size(); i++){
        result2.append(list[i] + del);
    }
    return result2;
}

// 作業ディレクトリを変更する関数
int split::chCWD(string str, char del, int start, int end){
    string new_str = split::splitjoin(str, del, start, end);

    int rtn = chdir(new_str.c_str());
    return rtn;
}
# includeパス(-I)
INCLUDE = -I./include  -I/usr/local/include/FL -I/mylib/include // 実際は絶対パス

# ライブラリ直接パス(-l)
LIBRARY = -lpng16 -lz -ljpeg /mylib/lib/split.dylib
<該当箇所のみ>

#include "split.h"

class split spt;

int main(int argc, char **argv) {

    vector<string> l = spt.splits(string(argv[0]), '/');

    // 作業ディレクトリを変更
    int rtn = spt.chCWD(argv[0], '/', 0, -2);
    cout << rtn << endl ;