[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();
}