[C++] 28 FLTK : PyQt6アプリの移植 仮完了

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

PyQt6アプリのFLTKへの移植は主な機能を実装するところまでたどり着きました。色々寄り道しながら9日掛かりました。

対象がpngのみ、リサイズ画像の背景が黒い、例外処理が甘く堅牢性に問題あり、などなど改善すべき点は多いですが徐々に対応していきます。

途中gtk+への変更や最悪C++開発からの撤退も考えましたが、偏屈ながらも筋は通っているFLTKで突き進み、サイズ1.5MBの高速軽量アプリに仕上げました。ちなみにPyQt6版は259.7MBです。

なおlibopencv_coreには動的リンクしています。静的リンクしてアプリに埋め込むと少なくとも4.5MB増しになります。

開発を通して様々なC/C++ライブラリの存在を知ることができましたし、やれることの幅が確実に広がったように思います。

#include "processImage.h"
#include "makeIcns.h"
<中略>
// ver.1.0.0b1

using std::string; using std::to_string;
using std::cout; using std::cin;
using std::endl;
using std::stoi;using std::bitset;

Fl_Window *window;
Fl_Box *file;
Fl_Input *input_line;
Fl_Group *radio_btns;
Fl_Multiline_Output *output_line;
Fl_Radio_Round_Button *inspect_rbtn;
Fl_Radio_Round_Button *resize_rbtn;
Fl_Radio_Round_Button *icns_rbtn;
Fl_Box *width_label;
Fl_Input *width_input;
Fl_Box *height_label;
Fl_Input *height_input;
Fl_Button *exe_btn;
Fl_Button *clear_btn;

int onoff_inspect;
int onoff_resize;
int onoff_icns;

void inspect(){
    const char *path = input_line->value();
    
    if (checkImage(path)==1){
        std::array<int,4> infos = getInfoPNG(path);
        
        cout<<"width "<<infos[0]<<" height "<<infos[1]<<endl;
        cout<<"dpi_x "<<infos[2]<<" dpi_y "<<infos[3]<<endl;

        int *width = &infos[0];
        string width_str = to_string(*width);
        char const* width_char = width_str.c_str();

        int *height = &infos[1];
        string height_str = to_string(*height);
        char const* height_char = height_str.c_str();

        int *dpi_x = &infos[2];
        string dpi_x_str = to_string(*dpi_x);
        char const* dpi_x_char = dpi_x_str.c_str();

        int *dpi_y = &infos[3];
        string dpi_y_str = to_string(*dpi_y);
        char const* dpi_y_char = dpi_y_str.c_str();

        output_line->insert("width ");
        output_line->insert(width_char);
        output_line->insert(" height ");
        output_line->insert(height_char);
        output_line->insert("\n");

        output_line->insert("dpi_x ");
        output_line->insert(dpi_x_char);
        output_line->insert(" dpi_y ");
        output_line->insert(dpi_y_char);
        output_line->insert("\n");

    } else if (checkImage(path)==2){
        output_line->insert("jpg\n");
    } else {
        output_line->insert("This file is invalid.\n");
    }
    
}
void resize(){
    // リサイズ時の背景黒化は要対策
    const char *path = input_line->value();

    const char *width = width_input->value();
    const char *height = height_input->value();
    int down_width = atoi(width);
    int down_height = atoi(height);

    cv::Mat img,img_resize;
    img = cv::imread(path);;
    cv::resize(img,img_resize, cv::Size(down_width,down_height),0,0,cv::INTER_LINEAR);

    // リサイズファイル名作成
    std::string path_str = std::string(path);
    char del = '.';

    std::vector<std::string> list = split(path_str, del);
	std::string extension = list.back();
    list.pop_back();

    const char* del2 = ".";
    std::string pre = join(list,del2);
    std::string path_resize = pre + "_resized." + extension;

    cout<<path_resize<<endl;

    cv::imwrite(path_resize, img_resize);

    output_line->insert("Resize complete!\n");

}
void icns(){
    const char *path = input_line->value();

    std::array<int,4> infos = getInfoPNG(path);

    int *width = &infos[0];
    int *height = &infos[1];
    
    if (*width == 2048 and *height == 2048){
        makeIcns(path);

        output_line->insert("Make icns complete!\n");
    
    }else{
        output_line->insert("This file is invalid.\n");
    }
}

void execute_cb(Fl_Widget*, void*) {
    onoff_inspect = inspect_rbtn->value(); 
    onoff_resize = resize_rbtn->value();
    onoff_icns = icns_rbtn->value();

    if (onoff_inspect == 1){
        inspect();
    } else if (onoff_resize == 1){
        resize();
    } else {
        icns();
    }
}
void clear_cb(Fl_Widget*, void*) {
    input_line->value("");
}

class Box : Fl_Box {
    Fl_Input* input_line2;

    public:
    Box(int, int, int, int, Fl_Input*);
    private:
    auto handle(int) -> int override;
};

Box::Box(int x, int y, int width_input, int height_input, Fl_Input* input) : Fl_Box(FL_NO_BOX, x, y, width_input, height_input, "") {
    this->input_line2 = input;
}

auto Box::handle(int event) -> int {
    switch (event) {
        case FL_DND_DRAG:
        case FL_DND_ENTER:
        case FL_DND_RELEASE:
        return 1;

        case FL_PASTE:
            input_line2->value(Fl::event_text());
            input_line2->textsize(12);
            input_line2->textfont(FL_HELVETICA);

        return 1;

        default:
        return Fl_Box::handle(event);
    }
}

int main(int argc, char **argv) {
    Fl::set_font(FL_HELVETICA, "Osaka");
    window = new Fl_Window(100,100,360,220,"IMAGE INSPECTOR");
    window->color(fl_rgb_color(112,128,144));
    
    // file
    file = new Fl_Box(15,15,35,16,"File");
    file->labelsize(14);
    file->labelcolor(fl_rgb_color(255,239,213));
    file->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));

    input_line = new Fl_Input(50,10,220,25,"");
    input_line->textsize(12);

    radio_btns = new Fl_Group(50,40,90,70,"");{
        radio_btns->labelsize(12);
        // inspect_rbtn
        inspect_rbtn = new Fl_Radio_Round_Button(50,40,90,20,"Inspect");
        inspect_rbtn->labelcolor(fl_rgb_color(255,239,213));
        inspect_rbtn->setonly();
        
        // resize_rbtn
        resize_rbtn = new Fl_Radio_Round_Button(50,65,90,20,"Resize");
        resize_rbtn->labelcolor(fl_rgb_color(255,239,213));
        
        // icns_rbtn
        icns_rbtn = new Fl_Radio_Round_Button(50,90,90,20,"icns作成");
        icns_rbtn->labelcolor(fl_rgb_color(255,239,213));
        
    }
    radio_btns->end();

    width_label = new Fl_Box(135,70,15,10,"W");
    width_label->labelsize(12);
    width_label->labelcolor(fl_rgb_color(255,239,213));
    width_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));
    
    width_input = new Fl_Input(155,65,45,20,"");
    width_input->textsize(12);

    height_label = new Fl_Box(205,70,15,10,"H");
    height_label->labelcolor(fl_rgb_color(255,239,213));
    height_label->labelsize(12);
    height_label->align(Fl_Align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT));
    
    height_input = new Fl_Input(220,65,45,20,"");
    height_input->textsize(12);

    exe_btn = new Fl_Button(290,10,50,30,"実行");
    exe_btn->color(fl_rgb_color(112,128,144));
    exe_btn->labelcolor(fl_rgb_color(255,239,213));
    exe_btn->labelsize(14);
    exe_btn->callback(execute_cb);
    exe_btn->when(FL_WHEN_RELEASE); // 省略可

    clear_btn = new Fl_Button(290,50,50,30,"クリア");
    clear_btn->color(fl_rgb_color(112,128,144));
    clear_btn->labelcolor(fl_rgb_color(255,239,213));
    clear_btn->labelsize(14);
    clear_btn->callback(clear_cb);

    output_line = new Fl_Multiline_Output(50,115,240,100,"");
    output_line->textsize(12);

    Box *box = new Box(0, 0, 360,220, input_line);

    window->end();
    window->show(argc, argv);
    return Fl::run();
}