#include "ximage.h" #include "tcl.h" #include "tk.h" #include "TTW.h" #define WIN32_LEAN_AND_MEAN #include #undef WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include "xlsxwriter.h" extern "C" { int Vfs_Init(Tcl_Interp *); int Memchan_Init(Tcl_Interp *); int Blt_Init(Tcl_Interp *); int Tkhtml_Init(Tcl_Interp *); void TkWinSetHINSTANCE(HINSTANCE hInstance); } #pragma region Tclモジュール読み込み処理 std::string read_vfs_script(const char *tarfile){ if (!tarfile) return ""; FILE *fp = std::fopen(tarfile, "rb"); if (!fp) return ""; std::string script; char buf[513]; buf[512] = '\0'; int datablocks = 0; long filesize = 0; bool add_to_script = false; while(!std::feof(fp)){ if (datablocks == 0){ // header size_t sz = fread(buf, 512, 1, fp); char typeflag = buf[156]; switch(typeflag){ case '1': case '2': case '3': case '4': case '5': case '6': case '7': continue; } filesize = std::strtol(buf + 124, 0, 8); datablocks = (filesize + 511) / 512; add_to_script = (std::strstr(buf, "vfs1.3/tarvfs.tcl") != 0) || (std::strstr(buf, "vfs1.3/vfsUtils.tcl") != 0); }else if (add_to_script){ size_t sz = std::fread(buf, 512, 1, fp); --datablocks; if (!add_to_script) continue; if (filesize < 512) buf[filesize] = '\0'; script += buf; filesize -= 512; }else{ std::fseek(fp, datablocks * 512, SEEK_CUR); datablocks = filesize = 0; } } std::fclose(fp); return script; } void LoadTarRT(Tcl_Interp *interp, const char *exedir_){ int rc; ::Tcl_StaticPackage(interp, "Tcl", Tcl_Init, 0); ::Tcl_StaticPackage(interp, "Tk", Tk_Init, 0); ::Tcl_StaticPackage(interp, "vfs", Vfs_Init, 0); ::Tcl_StaticPackage(interp, "memchan", Memchan_Init, 0); ::TkWinSetHINSTANCE(::GetModuleHandle("OSSLibs.dll")); // 実行ファイルのディレクトリを取得 std::string exedir = exedir_; for (size_t i = 0; i < exedir.size(); ++i) if (exedir[i] == '\\') exedir[i] = '/'; exedir = exedir.substr(0, exedir.find_last_of('/')+1); for (size_t i = exedir.size() - 1; i >= 1; --i){ if (exedir[i-1] == '/' && exedir[i] == '/') exedir.erase(i, 1); } std::string tcl_lib_dir = (exedir + "lib/tcl8.6/"); ::Tcl_SetVar2(interp, "tcl_library", 0, tcl_lib_dir.c_str(), TCL_GLOBAL_ONLY); ::Tcl_SetVar2(interp, "env", "TCL_LIBRARY", tcl_lib_dir.c_str(), TCL_GLOBAL_ONLY); // tarファイル をマウントするために必要なモジュール群をロード rc = ::Vfs_Init(interp); rc = ::Memchan_Init(interp); // tarファイル をマウントするために必要なスクリプトをtarファイルの中から見つけて取得 std::string scr = read_vfs_script((exedir + "tcl_rt.tar").c_str()); rc = ::Tcl_Eval(interp, scr.c_str()); // tarファイル をマウント rc = ::Tcl_Eval(interp, "package require vfs::tar;"); rc = ::Tcl_Eval(interp, "vfs::tar::Mount $tcl_library/../../tcl_rt.tar $tcl_library/../;"); // ↑ interp ではなく、 Tcl_FS の仕組み自体にマウントされるため、 interp とは関係なしに Tcl_FS系の関数で tcl_rt.tar の中身にアクセスできる。 } #pragma endregion void CreateRef(const char *prefix, const char *sheet, lxw_row_t row, lxw_col_t col, const char *surfix, TTW_DString &name){ name << prefix; if (sheet){ name << sheet << "!"; } int prefix_cnt = name.Length(); name.SetLength(prefix_cnt + 100); lxw_rowcol_to_cell(name + prefix_cnt, row, col); name.SetLength(std::strlen(name)); name << surfix; std::printf("ref:%s\n", name.Value()); } int CreateCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]){ TTW_DString filename; filename.Utf8ToSys(objv[1]); const char *sheet_name = "Sheet"; int chart_col_skip = 8, chart_row_skip = 13, chart_cols = 3; lxw_workbook *workbook = workbook_new(filename.Value()); lxw_worksheet *worksheet = workbook_add_worksheet(workbook, sheet_name); lxw_format *format = workbook_add_format(workbook); lxw_row_t row = 0; lxw_col_t col = 0; TTW_SplitUTF8String ss_line(TTW_GetString(objv[2]), "\n"); const char *line_start; int line_len; enum{ LT_Title, LT_Axis, LT_Value }LineType; std::vector charts; lxw_chart *chart = 0; lxw_chart_series *series = 0; TTW_DString ref; lxw_row_t current_category_row = 0; lxw_col_t current_category_col_max = 0; while(ss_line.Next(line_start, line_len)){ TTW_DString line(line_start, line_len); TTW_SplitUTF8String ss_token(line, "\t"); col = 0; const char *token_start; int token_len; while(ss_token.Next(token_start, token_len)){ TTW_DString token(token_start, token_len); std::printf("[%s]\t", token.Value()); if (col == 0){ char c = token[0]; if (c == '['){ LineType = LT_Title; chart = workbook_add_chart(workbook, LXW_CHART_SCATTER_SMOOTH_WITH_MARKERS); chart_title_set_name_range(chart, sheet_name, row, col); chart_legend_set_position(chart, LXW_CHART_LEGEND_OVERLAY_TOP_RIGHT); charts.push_back(chart); current_category_row = 0; current_category_col_max = 0; }else if (c == '*'){ LineType = LT_Axis; current_category_row = row; current_category_col_max = 0; }else{ LineType = LT_Value; series = chart_add_series(chart, 0, 0); chart_series_set_categories(series, sheet_name, current_category_row, 1, current_category_row, current_category_col_max); chart_series_set_values(series, sheet_name, row, 1, row, current_category_col_max); chart_series_set_name_range(series, sheet_name, row, 0); } worksheet_write_string(worksheet, row, col, token.Value(), format); }else{ if(LineType == LT_Title){ worksheet_write_string(worksheet, row, col, token.Value(), format); }else{ worksheet_write_number(worksheet, row, col, std::strtod(token.Value(), 0), format); } if (LineType == LT_Axis){ current_category_col_max = col; } } ++col; } ++row; std::printf("\n"); } int chart_row = row, chart_col = 0; for (size_t i = 0; i < charts.size(); ++i){ worksheet_insert_chart(worksheet, chart_row, chart_col, charts[i]); chart_col += chart_col_skip; if (chart_col >= chart_col_skip * chart_cols){ chart_col = 0; chart_row += chart_row_skip; } } workbook_close(workbook); return TCL_OK; } #if 0 int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { int argc = __argc; char **argv = __argv; #else int main(int argc, char *argv[]){ #endif const char *exedir_ = argv[0]; Tcl_Interp *interp = ::Tcl_CreateInterp(); LoadTarRT(interp, exedir_); Tcl_FindExecutable(argv[0]); int rc = 0; rc = Tcl_Init(interp); rc = Tk_Init(interp); ::Tcl_CreateObjCommand(interp, "::create_chart::create", CreateCmd, 0, 0); ::Tcl_Eval(interp, "wm title . {Create Chart};\n" "set filename {chart.xlsx};\n" "text .txt -width 80 -height 20;\n" "label .lfile -text {output filename};\n" "entry .efile -textvariable filename;\n" "button .bcreate -text {create} -command {::create_chart::create $filename [.txt get 1.0 end]};\n" "grid .txt - -sticky news;\n" "grid .lfile .efile -sticky ew;\n" "grid x .bcreate -sticky e;\n" ); ::Tk_MainLoop(); return 0; }