[C++]103 FLTK : 開発モードの設定 Fl_Sys_Menu_Bar

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

前回記事のFL_Window 2窓設定を開発モードとしてMacOSのシステムメニューから選択できるようにしました。配置していたトグルボタンを削除し、普段は通常モードとして使用します。

開発モードを選択する度にトグルボタンの様に表示/非表示が切り替わります。

Fl_Window *window2;
int mode = 0; // 0:通常モード, 1:開発モード

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

    static Fl_Menu_Item	items[] = {
    { "設定", 0, 0, 0, FL_SUBMENU },
    { "開発モード", 0, showSubWindow, 0, 0 },
    { 0 },
    { 0 }
    };
    Fl_Sys_Menu_Bar *menubar;
    menubar = new Fl_Sys_Menu_Bar(0, 0, 60, 20);
    menubar->box(FL_FLAT_BOX);
    menubar->menu(items);

    Fl_Window *window = new Fl_Window(50,50,660,480,"Color Sample");
    window->color(fl_rgb_color(238,238,238));

    <中略>
    
    window->end();
    window->show(argc, argv);
    
    // サブWindow
    window2 = new Fl_Window(750,50,360,480,"出力用");
    window2->color(fl_rgb_color(238,238,238));

    // output
    output_line2 = new Fl_Multiline_Output(0,0,360,480,"");
    output_line2->textsize(12);

    window2->end();

    return Fl::run();
}
extern Fl_Window *window2;
extern int mode;

void showSubWindow(Fl_Widget*, void*){
    if (mode == 1){
        mode = 0;
    } else {
        mode = 1;
    }

    if (mode == 1){
        window2->show();
    } else {
        window2->hide();
    }
}

[C++]102 FLTK : Fl_Windowの複数表示

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

コンソールが表示される実行ファイルとappファイルで挙動が異なることが多いため、appファイルで出力を確認できる2窓の開発用アプリを用意しました。

Name欄から取得した文字列からひらがなを抽出してGUI内位置検索したものの、結果を表示させても該当なしになる現象を動画に収めています。右Windowから分かるように抽出したひらがなは文字化けしています。

Fl_Window *window2;
Fl_Toggle_Button *tgl_btn;

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

    Fl_Window *window = new Fl_Window(50,50,660,480,"Color Sample");
    window->color(fl_rgb_color(238,238,238));

    <中略>

    // サブWindow表示切り替え用トグルボタン
    tgl_btn = new Fl_Toggle_Button(602,227,50,15,"W表示");
    tgl_btn->labelsize(12);
    tgl_btn->callback(showSubWindow);

    window->end();
    window->show(argc, argv);
    
    // サブWindow
    window2 = new Fl_Window(750,50,360,480,"出力用");
    window2->color(fl_rgb_color(238,238,238));

    // output
    output_line2 = new Fl_Multiline_Output(0,0,360,480,"");
    output_line2->textsize(12);

    window2->end();
    window2->show(argc, argv);

    return Fl::run();
}
extern Fl_Window *window2;
extern Fl_Toggle_Button *tgl_btn;

void showSubWindow(Fl_Widget*, void*){
    int onoff_btn = tgl_btn->value();
    
    if (onoff_btn == 1){
        window2->hide();
    } else {
        window2->show();
    }
}

[C++]101 FLTK : 同名関数の使用

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

製作中のカラーアプリはCode欄から取得したカラーコードの有無を検索し、ボタンのタブ内位置を表示する機能を搭載していますが、その際にFl_Boxのカラー表示も反映するようにしました。

callback用のshowColor関数を使い回します。引数が異なるのであれば同名の関数設定が可能です。知ってはいましたが初めて使います。もちろん関数の内容が違っていても構いません。

本当はJava(Swing)のdoClickメソッドのようにプログラムから表示ボタンを押させたかったのですが、FLTKドキュメントを隅々まで調べても方法が見つかりませんでした。

// コールバック用
void showColor(Fl_Widget*, void*){
	onoff_zero = zero_rbtn->value();
    onoff_sharp = sharp_rbtn->value();
    onoff_hex = hex_rbtn->value();
    onoff_rgb = rgb_rbtn->value();
    
    if (onoff_zero == 1){
        ToZeroConvert();
    } else if (onoff_sharp == 1){
        ToSharpConvert();
    } else if (onoff_hex == 1){
        ToHexConvert();
    } else {
        ToRGBConvert();
    }
}

// 通常用
void showColor(){
	onoff_zero = zero_rbtn->value();
    onoff_sharp = sharp_rbtn->value();
    onoff_hex = hex_rbtn->value();
    onoff_rgb = rgb_rbtn->value();
    
    if (onoff_zero == 1){
        ToZeroConvert();
    } else if (onoff_sharp == 1){
        ToSharpConvert();
    } else if (onoff_hex == 1){
        ToHexConvert();
    } else {
        ToRGBConvert();
    }
}

[C++]100 FLTK : ラジオボタンの逆送り

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

とりあえずJavaからの移植が完了しましたが、色々物足りない箇所が出てきたので手を加えていきます。

まずはカラーコードのタイプを順送りで変えているところに逆送りボタンを追加しました。

次はカラー表示の履歴を記録し、進む、戻るボタンで変えられるようにします。

void prevType(Fl_Widget*, void*){
	onoff_zero = zero_rbtn->value();
    onoff_sharp = sharp_rbtn->value();
    onoff_hex = hex_rbtn->value();
    onoff_rgb = rgb_rbtn->value();

    if (onoff_zero == 1){
        onoff_zero = zero_rbtn->value(false);
        onoff_rgb = rgb_rbtn->value(true);
        ToRGBConvert();
    } else if (onoff_sharp == 1){
        onoff_sharp = sharp_rbtn->value(false);
        onoff_zero = zero_rbtn->value(true);
        ToZeroConvert();
    } else if (onoff_hex == 1){
        onoff_hex = hex_rbtn->value(false);
        onoff_sharp = sharp_rbtn->value(true);
        ToSharpConvert();
    } else {
        onoff_rgb = rgb_rbtn->value(false);
        onoff_hex = hex_rbtn->value(true);
        ToHexConvert();
    }
}

[C++] 99 FLTK : カラーコードからGUI内ボタン位置検索

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

カラーコードからGUI内ボタン位置を検索できるようにしました。カラー名からの検索は前回記事で書いたようにappファイルでひらがなの抽出ができずに難航しているため、一旦これで仮完成とします。

ひらがな抽出はPythonモジュールでもうまくいかないので、C++で再挑戦してみます。

// CEクラスとfindIndex関数は自製

void searchLocation(Fl_Widget*, void*){
    string name_alpha, name_hira;
    string location;
    int column,row;

    input_name = name_input->value();
    input_code = code_input->value();

	onoff_name = name_rbtn->value();
    onoff_code = code_rbtn->value();

    if (onoff_name == 1){
        cout << "Name検索" << endl;

        name_hira = CE.extract(input_name, HIRAGANA);
        name_alpha = CE.extract(input_name, ALPHABET);
        
        cout << "Alphabet " << name_alpha << endl;
        cout << "ひらがな " << name_hira << endl;

        if (name_alpha != ""){
            cout << "140色検索" << endl;

            auto index = findIndex(colorList_name, name_alpha);
            if (index != -1){
                column = index/28 + 1;
                row = index%28 + 1;
                
                location = "140色 " + to_string(column) + "列 " + to_string(row) + "行";
                roma_input->value("");
                code_input->value("");
                location_input->value("");
                location_input->insert(location.c_str());
            } else{
                location_input->value("");
                location_input->insert("該当なし");
            }
            
        } else if (name_hira !=""){
            cout << "和色検索" << endl;

            int process = 0; // 検索状態 0:検索中, 1:検索終了
            int tab_num = 1;
            for (vector<string> list:colorList2_name){
                auto index = findIndex(list, name_hira);
                if (index != -1){
                    column = index/30 + 1;
                    row = index%30 + 1;
                    
                    location = tab_names[tab_num] + " " + to_string(column) + "列 " + to_string(row) + "行";
                    roma_input->value("");
                    code_input->value("");
                    location_input->value("");
                    location_input->insert(location.c_str());
                    process = 1;
                    break;
                }
                tab_num += 1;
            }
            if (process==0){
                location_input->value("");
                location_input->insert("該当なし");
            }
            
        } else {
            cout << "検索文字列なし" << endl;
        }

    } else {
        cout << "Code検索" << endl;
        int process = 0;
        // input_codeを0xへ変換
        code_zero = ToZeroConvert2();

        cout << "code 140色検索" << endl;

        auto index = findIndex(colorList_code, code_zero);
        if (index != -1){
            column = index/28 + 1;
            row = index%28 + 1;
            
            string result_name = colorList_name[index];
            location = "140色 " + to_string(column) + "列 " + to_string(row) + "行 " + result_name;
            string name = colorList_name[index];
            name_input->value("");
            roma_input->value("");
            location_input->value("");
            location_input->insert(location.c_str());
            process = 1;
        }

        cout << "code 和色検索" << endl;

        if (process == 0){
            int tab_num = 1;
            for (vector<string> list:colorList2_code){
                auto index = findIndex(list, code_zero);
                if (index != -1){
                    column = index/30 + 1;
                    row = index%30 + 1;
                    
                    string result_name = colorList2_name[tab_num -1][index];
                    location = tab_names[tab_num] + " " + to_string(column) + "列 " + to_string(row) + "行 " + result_name;
                    name_input->value("");
                    roma_input->value("");
                    location_input->value("");
                    location_input->insert(location.c_str());
                    process = 1;
                    break;
                }
                tab_num += 1;
            }
            if (process==0){
                location_input->value("");
                location_input->insert("該当なし");
            }
        }
    }

[C++] 98 文字列から各文字種を取り出す 列挙型 enum

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

[C++] 96のコードを列挙型 enumを使って書き直しました。これで文字種を直接指定でき、対応するインデックス番号を覚える必要がなくなります。

こちらのswitch文の方がif文よりも洗練された感じですが、条件ごとにいちいちbreakを入れるので見た目は今ひとつです。

今月はstructとenumの作例を作ることを課題の一つにしており、これでクリアです。

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

string hiragana;
string expr;

enum CharType{
    NUMBER,
    ALPHABET,
    HIRAGANA,
    KATAKANA,
    KANJI
};

string narrow(const wstring &src) {
	char *mbs = new char[src.length() * MB_CUR_MAX + 1];
	wcstombs(mbs, src.c_str(), src.length() * MB_CUR_MAX + 1);
	return mbs;
	delete [] mbs;
}

wstring wide(const string &src) {
	wchar_t *wcs = new wchar_t[src.length() + 1];
	mbstowcs(wcs, src.c_str(), src.length() + 1);
	return wcs;
	delete [] wcs;
}

string extract(string str, CharType ct){
    switch (ct)
    {
    case NUMBER:{    
        expr = "[0-9]+"; // 数字
        break;
    }
    case ALPHABET:{
        expr = "[a-zA-Z]+"; // 英字
        break;
    }
    case HIRAGANA:{
        expr = "[\\u3041-\\u309F]+"; // ひらがな
        break;
    }
    case KATAKANA:{
        expr = "[\\u30A0-\\u30FF]+"; // カタカナ
        break;
    }
    case KANJI:{
        expr = "[\\u4E00-\\u9FFF]+"; // 漢字
        break;
    }
    }

    wstring wstr = wide(str);
    wstring wexpr = wide(expr);

    std::wregex we(wexpr);
    std::wsmatch wm;
    if(std::regex_search(wstr, wm, we)){
        hiragana = narrow(wm.str());
    }
    return hiragana;
}

int main()
{
    setlocale(LC_CTYPE, "");

    string test = "日本語ハローわーるどHelloはろー[123]";
    
    string number = extract(test, NUMBER);
    string alphabet = extract(test, ALPHABET);
    string hiragana = extract(test, KATAKANA);
    string katakana = extract(test, HIRAGANA);
    string kanji = extract(test, KANJI);

    cout << "数字 " << number << endl;
    cout << "英字 " << alphabet << endl;
    cout << "ひらがな " << hiragana << endl;
    cout << "カタカナ " << katakana << endl;
    cout << "漢字 " << kanji << endl;
}
--------------------------------------------------
出力
--------------------------------------------------
数字 123
英字 Hello
ひらがな ハロー
カタカナ わ
漢字 日本語

[C++] 97 英字有無の判定と取り出し 構造体 struct

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

文字列内に英字があるかどうかの判定とその取り出しができる関数を作成しました。structデータを使いたかったので作ってみました。

Javaでもclassにてstructのようなデータの集合体を作ることができます。これまでJavaでは配列を作って関数の戻り値にしていましたが、データ型を揃える必要があり不便に思っていました。

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

struct Result{
    bool judge;
    string alphabet;
};

Result alphabetExtract(string str)
{
    std::regex pattern("[a-zA-Z]+");
    std::smatch sm;
    if (std::regex_search(str, sm, pattern)){
        string ch = sm.str();
        struct Result res = {true, ch};
        return res;
    }else{
        struct Result res = {false, ""};
        return res;
    }
}

int main()
{
    setlocale(LC_CTYPE, "");

    string test = "123ハローわぁるどHello";
    struct Result res = alphabetExtract(test);
    string alp = res.alphabet;

    cout << "判定結果 " << res.judge << endl;
    cout << "英字 " << alp.c_str() << endl;
}
--------------------------------------------------
出力
--------------------------------------------------
判定結果 1
英字 Hello

[C++] 96 文字列から各文字種を取り出す動的ライブラリ dylib

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

文字列から数字、英字、ひらがな、カタカナ、漢字をそれぞれ取り出す動的ライブラリを作成しました。

ただし最初の単語しか認識せず、2番目以降は取り出しできない限定機能です。文字列全体からもれなく取り出すのであればPythonの方が簡単に書けそうです。

このような便利な動的ライブラリを作成していくとおのずと主言語はC++になっていくでしょう。

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

class CharExtract{
public:
string narrow(const wstring &src);
wstring wide(const string &src);
string extract(string str,int num);
};
#include <cppstd.h> // 自製c++標準ライブラリ群
#include <CharExtract.h>

string hiragana;
string expr;

string CharExtract::narrow(const wstring &src) {
	char *mbs = new char[src.length() * MB_CUR_MAX + 1];
	wcstombs(mbs, src.c_str(), src.length() * MB_CUR_MAX + 1);
	return mbs;
}

wstring CharExtract::wide(const string &src) {
	wchar_t *wcs = new wchar_t[src.length() + 1];
	mbstowcs(wcs, src.c_str(), src.length() + 1);
	return wcs;
}

string CharExtract::extract(string str, int num){
    if (num == 0){
        expr = "[0-9]+"; // 数字
    } else if (num == 1){
        expr = "[a-zA-Z]+"; // 英字
    } else if (num == 2){
        expr = "[\\u3041-\\u309F]+"; // ひらがな
    } else if (num == 3) {
        expr = "[\\u30A0-\\u30FF]+"; // カタカナ
    } else if (num == 4){
        expr = "[\\u4E00-\\u9FFF]+"; // 漢字
    }

    wstring wstr = wide(str);
    wstring wexpr = wide(expr);

    std::wregex we(wexpr);
    std::wsmatch wm;
    if(std::regex_search(wstr, wm, we)){
        character = narrow(wm.str());
    }
    return character;
}
clang++ -dynamiclib -o CharExtract.dylib \
CharExtract.cpp \
-I/code/cpp/mylib/include -std=c++17
#include <cppstd.h> // 自製c++標準ライブラリ群
#include <CharExtract.h>

CharExtract CE;

int main()
{
    setlocale(LC_CTYPE, "");

    string test = 日本語ハローわーるどHelloはろー[123]";
    
    string number = CE.extract(test, 0);
    string alphabet = CE.extract(test, 1);
    string hiragana = CE.extract(test, 2);
    string katakana = CE.extract(test, 3);
    string kanji = CE.extract(test, 4);

    cout << "数字 " << number << endl;
    cout << "英字 " << alphabet << endl;
    cout << "ひらがな " << hiragana << endl;
    cout << "カタカナ " << katakana << endl;
    cout << "漢字 " << kanji << endl;
}
--------------------------------------------------
出力
--------------------------------------------------
数字 123
英字 Hello
ひらがな わ     // 長音"ー"はカタカナ扱い
カタカナ ハロー
漢字 日本語

[C++] 95 文字列からひらがなを取り出す wregex

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

前回は開発速度を優先して泥臭い手法を用いましたが、今回はよりプログラマらしい内容に書き直しました。

対象文字列と全てのひらがなをstringからwstringに変換し、正規表現を使って対象文字列からひらがなを取り出しました。ひらがな設定は文字列リテラルを書き出すのではなくunicodeを使っています。

setlocaleでロケールを設定しないとひらがなを扱えないので要注意です。

マルチバイト文字の扱いにくさはC/C++の弱点の一つですね。Visual C++やC#など、Microsoftはその辺のフォローが行き届いているように思います。まあそれでもWindowsをメインOSにしようとは思いませんが..

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

string hiragana;

string narrow(const wstring &src) {
	char *mbs = new char[src.length() * MB_CUR_MAX + 1];
	wcstombs(mbs, src.c_str(), src.length() * MB_CUR_MAX + 1);
	return mbs;
}

wstring wide(const string &src) {
	wchar_t *wcs = new wchar_t[src.length() + 1];
	mbstowcs(wcs, src.c_str(), src.length() + 1);
	return wcs;
}

string hiragana_extract(string str){
    string expr = "[\\u3041-\\u309F]+"; // ひらがな

    wstring wstr = wide(str);
    wstring wexpr = wide(expr);

    std::wregex we(wexpr);
    std::wsmatch wm;
    if(std::regex_search(wstr, wm, we)){
        hiragana = narrow(wm.str());
    }
    return hiragana;
}

int main()
{
    setlocale(LC_CTYPE, "");

    string test = "水浅葱 みずあさぎhello [123]";
    
    string hiragana = hiragana_extract(test);

    cout << "ひらがな抽出 " << hiragana << endl;
}
--------------------------------------------------
出力
--------------------------------------------------
ひらがな抽出 みずあさぎ

参考サイト1
参考サイト2

[C++] 94 文字列からひらがなを取り出す

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

日本語はマルチバイト文字なので文字列の扱いが難しいです。

今回は漢字とひらがなが混ざった文字列からひらがなだけを取り出しました。

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

vector<string> hiragana_list = 
    {
    "あ","い","う","え","お",
    "か","き","く","け","こ",
    "さ","し","す","せ","そ",
    "た","ち","つ","て","と",
    "な","に","ぬ","ね","の",
    "は","ひ","ふ","へ","ほ",
    "ま","み","む","め","も",
    "や","ゆ","よ",
    "ら","り","る","れ","ろ",
    "わ","を","ん",
    "が","ぎ","ぐ","げ","ご",
    "ざ","じ","ず","ぜ","ぞ",
    "だ","ぢ","づ","で","ど",
    "ば","び","ぶ","べ","ぼ",
    "ぱ","ぴ","ぷ","ぺ","ぽ",
    "ゃ","ゅ","ょ","っ"
    };

vector<string> str_kana;

string hiragana_extract(string str)
{
    int pos;
    unsigned char lead; 
    int char_size;

   for (pos = 0; pos < str.size(); pos += char_size) {
        lead = str[pos];

        if (lead < 0x80) {
            char_size = 1;
        } else if (lead < 0xE0) { 
            char_size = 2;
        } else if (lead < 0xF0) {
            char_size = 3;
        } else {
            char_size = 4;
        }

        cout << str.substr(pos, char_size) << endl;
        string moji = str.substr(pos, char_size);
        
        for (auto &item : hiragana_list) {
            if (item == moji) {
                str_kana.push_back(moji);
            }
        }
    }

    // vector内ひらがなを結合
    std::ostringstream os;
    std::copy(str_kana.begin(), str_kana.end(), std::ostream_iterator<string>(os));
    string hiragana = os.str(); 

    return hiragana;
}

int main(int argc, char **argv)
{
    string str = "水浅葱 みずあさぎ";

    string hiragana = hiragana_extract(str);

    cout << "ひらがな抽出 " << hiragana << endl;

    return 0;
}
--------------------------------------------------
出力
--------------------------------------------------
水
浅
葱
 
み
ず
あ
さ
ぎ
ひらがな抽出 みずあさぎ

2022/7/21追記:
以下の記事にてより洗練されたコードに書き直しました。