70 std::thread run_thread;
71 std::thread py_thread;
72 if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
74 printf(
"Error: %s\n", SDL_GetError());
79 #if defined(IMGUI_IMPL_OPENGL_ES2)
81 const char *glsl_version =
"#version 100";
82 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
83 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
84 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
85 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
86 #elif defined(__APPLE__)
88 const char *glsl_version =
"#version 150";
89 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
90 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
91 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
92 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
95 const char *glsl_version =
"#version 130";
96 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
97 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
98 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
99 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
103 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
104 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
105 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
106 SDL_Window *window = SDL_CreateWindow(
"riCOM", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1200, 800, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
109 std::cout <<
"Window could not be created! SDL Error: " << SDL_GetError() << std::endl;
111 SDL_GLContext gl_context = SDL_GL_CreateContext(window);
112 SDL_GL_MakeCurrent(window, gl_context);
113 SDL_GL_SetSwapInterval(1);
116 IMGUI_CHECKVERSION();
117 ImGui::CreateContext();
118 ImGuiIO &io = ImGui::GetIO();
121 mINI::INIFile ini_file(
"imgui.ini");
122 mINI::INIStructure ini_cfg;
123 ini_file.read(ini_cfg);
125 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
127 ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
128 ImGui_ImplOpenGL3_Init(glsl_version);
130 io.Fonts->AddFontFromMemoryCompressedTTF(KarlaRegular_compressed_data, KarlaRegular_compressed_size, 14.0f);
131 io.Fonts->AddFontFromMemoryCompressedTTF(RobotoMedium_compressed_data, RobotoMedium_compressed_size, 14.0f);
132 io.Fonts->AddFontFromMemoryCompressedTTF(CousineRegular_compressed_data, CousineRegular_compressed_size, 14.0f);
133 io.Fonts->AddFontFromMemoryCompressedTTF(DroidSans_compressed_data, DroidSans_compressed_size, 14.0f);
134 io.Fonts->AddFontFromMemoryCompressedTTF(ProggyClean_compressed_data, ProggyClean_compressed_size, 14.0f);
135 io.Fonts->AddFontFromMemoryCompressedTTF(ProggyTiny_compressed_data, ProggyTiny_compressed_size, 14.0f);
137 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
138 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
142 ImVec2 uv_min = ImVec2(0.0f, 0.0f);
143 ImVec2 uv_max = ImVec2(1.0f, 1.0f);
144 ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
145 ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);
147 ImVec2 menu_bar_size;
148 ImVec2 control_menu_size(200, 800);
150 const size_t n_textures = 16;
151 GLuint uiTextureIDs[n_textures];
152 glGenTextures(n_textures, uiTextureIDs);
154 for (
size_t i = 0; i < n_textures; i++)
156 glBindTexture(GL_TEXTURE_2D, uiTextureIDs[i]);
157 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
158 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
159 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
160 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
164 ImGui::FileBrowser openFileDialog;
165 openFileDialog.SetTitle(
"Open .mib or .t3p file");
166 openFileDialog.SetTypeFilters({
".mib",
".t3p"});
167 ImGui::FileBrowser saveFileDialog(ImGuiFileBrowserFlags_EnterNewFilename | ImGuiFileBrowserFlags_CreateNewDir);
168 saveFileDialog.SetTitle(
"Save image as .png");
169 ImGui::FileBrowser saveDataDialog(ImGuiFileBrowserFlags_EnterNewFilename | ImGuiFileBrowserFlags_CreateNewDir);
170 saveDataDialog.SetTitle(
"Save COM data in binary format");
171 std::string filename =
"";
175 bool b_merlin_live_menu =
true;
176 bool b_acq_open =
false;
177 bool b_started =
false;
178 bool b_file_selected =
false;
179 bool b_restarted =
false;
182 struct MerlinSettings merlin_settings;
194 std::string python_path;
198 python_path =
"python3";
215 const char *cmaps[] = {
"Parula",
"Heat",
"Jet",
"Turbo",
"Hot",
"Gray",
"Magma",
"Inferno",
"Plasma",
"Viridis",
"Cividis",
"Github",
"HSV"};
216 bool b_redraw =
false;
217 bool b_trigger_update =
false;
218 float menu_split_v = control_menu_size.y * 0.8f;
224 bool show_com_x =
false;
225 bool show_com_y =
false;
226 bool ricom_fft =
false;
227 bool vstem_fft =
false;
228 bool e_field_fft =
false;
230 std::map<std::string, ImGuiImageWindow<float>> generic_windows_f;
231 std::map<std::string, ImGuiImageWindow<std::complex<float>>> generic_windows_c;
233 generic_windows_f.emplace(
"RICOM-FFT",
ImGuiImageWindow<float>(
"RICOM-FFT", &uiTextureIDs[2],
false, 4, common_flags, &ricom_fft));
237 generic_windows_f.emplace(
"vSTEM-FFT",
ImGuiImageWindow<float>(
"vSTEM-FFT", &uiTextureIDs[4],
false, 4, common_flags, &vstem_fft));
240 generic_windows_f.emplace(
"COM-X",
ImGuiImageWindow<float>(
"RICOM-COMX", &uiTextureIDs[5],
true, 9, common_flags, &show_com_x));
241 generic_windows_f.emplace(
"COM-Y",
ImGuiImageWindow<float>(
"RICOM-COMY", &uiTextureIDs[6],
true, 9, common_flags, &show_com_y));
244 generic_windows_f.emplace(
"E-Field-FFT",
ImGuiImageWindow<float>(
"E-Field-FFT", &uiTextureIDs[8],
false, 4, common_flags, &e_field_fft));
251 ImGuiID dock_id = 3775;
259 while (SDL_PollEvent(&event))
261 ImGui_ImplSDL2_ProcessEvent(&event);
262 if (event.type == SDL_QUIT)
264 if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
269 ImGui_ImplOpenGL3_NewFrame();
270 ImGui_ImplSDL2_NewFrame();
274 if (ImGui::BeginMainMenuBar())
276 if (ImGui::BeginMenu(
"Appearance"))
278 ImGui::Combo(
"CBED Colormap", &ricom->
cbed_cmap, cmaps, IM_ARRAYSIZE(cmaps));
283 if (ImGui::BeginMenu(
"Hardware Settings"))
285 ImGui::Text(
"Plotting");
286 if (ImGui::DragInt(
"Image Refresh Interval [ms]", &ricom->
redraw_interval, 20, 10, 1000))
288 ini_cfg[
"Hardware"][
"Image Refresh Interval [ms]"] = std::to_string(ricom->
redraw_interval);
292 ImGui::Text(
"Multithreading");
295 ini_cfg[
"Hardware"][
"Threads"] = std::to_string(ricom->
n_threads);
297 if (ImGui::DragInt(
"Queue Size", &ricom->
queue_size, 1, 1, 256))
299 ini_cfg[
"Hardware"][
"Queue Size"] = std::to_string(ricom->
queue_size);
303 ImGui::Text(
"Merlin Camera");
304 if (ImGui::Checkbox(
"Live Interface Menu", &b_merlin_live_menu))
306 ini_cfg[
"Merlin"][
"Live Interface Menu"] = std::to_string(b_merlin_live_menu);
308 if (ImGui::DragInt(
"nx Merlin", &hardware_configurations[
CAMERA::MERLIN].nx_cam, 1, 1, 2048))
310 ini_cfg[
"Merlin"][
"nx"] = std::to_string(hardware_configurations[
CAMERA::MERLIN].nx_cam);
312 if (ImGui::DragInt(
"ny Merlin", &hardware_configurations[
CAMERA::MERLIN].ny_cam, 1, 1, 2048))
314 ini_cfg[
"Merlin"][
"ny"] = std::to_string(hardware_configurations[
CAMERA::MERLIN].ny_cam);
316 if (ImGui::InputText(
"IP", &ricom->
socket.
ip))
318 ini_cfg[
"Merlin"][
"ip"] = ricom->
socket.
ip;
320 if (ImGui::InputInt(
"COM-Port", &merlin_settings.com_port, 8))
322 ini_cfg[
"Merlin"][
"com_port"] = std::to_string(ricom->
socket.
port);
324 if (ImGui::InputInt(
"Data-Port", &ricom->
socket.
port, 8))
326 ini_cfg[
"Merlin"][
"data_port"] = std::to_string(ricom->
socket.
port);
328 if (ImGui::InputText(
"python path", &python_path))
330 ini_cfg[
"Merlin"][
"python path"] = python_path;
334 ImGui::Text(
"Timepix Camera");
336 if (ImGui::DragInt(
"nx Timepix", &hardware_configurations[
CAMERA::TIMEPIX].nx_cam, 1, 1, 2048))
338 ini_cfg[
"Timepix"][
"nx"] = std::to_string(hardware_configurations[
CAMERA::TIMEPIX].nx_cam);
340 if (ImGui::DragInt(
"ny Timepix", &hardware_configurations[
CAMERA::TIMEPIX].ny_cam, 1, 1, 2048))
342 ini_cfg[
"Timepix"][
"ny"] = std::to_string(hardware_configurations[
CAMERA::TIMEPIX].ny_cam);
346 if (ImGui::BeginMenu(
"Additional Imaging Modes"))
348 if (ImGui::Checkbox(
"Show CoM-X", &show_com_x))
359 if (ImGui::Checkbox(
"Show CoM-Y", &show_com_y))
370 if (ImGui::Checkbox(
"Show E-Field", &ricom->
b_e_mag))
381 if (ImGui::Checkbox(
"View vSTEM Image", &ricom->
b_vSTEM))
394 menu_bar_size = ImGui::GetWindowSize();
395 ImGui::EndMainMenuBar();
398 const ImGuiViewport *viewport = ImGui::GetMainViewport();
412 ImVec2 pos = viewport->Pos;
413 pos[1] += menu_bar_size.y;
414 ImGui::SetNextWindowPos(pos);
416 control_menu_size.y = viewport->Size.y - menu_bar_size.y;
417 ImGui::SetNextWindowSize(control_menu_size);
418 ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1));
420 ImGui::Begin(
"Navigation", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar);
422 bool b_nx_changed =
false;
423 ImGui::BeginChild(
"Controls", ImVec2(0, menu_split_v),
false);
424 if (ImGui::CollapsingHeader(
"General Settings", ImGuiTreeNodeFlags_DefaultOpen))
426 ImGui::Text(
"Scan Area");
427 b_nx_changed = ImGui::DragInt(
"nx", &ricom->
nx, 1, 1, SDL_MAX_SINT32);
428 ImGui::DragInt(
"ny", &ricom->
ny, 1, 1, SDL_MAX_SINT32);
429 ImGui::DragInt(
"Repetitions", &ricom->
rep, 1, 1, SDL_MAX_SINT32);
431 ImGui::DragInt(
"skip row", &ricom->
skip_row, 1, 0, SDL_MAX_SINT32);
432 ImGui::DragInt(
"skip img", &ricom->
skip_img, 1, 0, SDL_MAX_SINT32);
434 if (ImGui::IsItemHovered())
436 ImGui::SetTooltip(
"Adjustments for Flyback time:\n skip row: skip n frames after each scan line\n skip img: skip n frames after each scan (when using repetitions)");
439 ImGui::Text(
"CBED Centre");
441 bool offset_changed = ImGui::DragFloat2(
"Centre", &ricom->
offset[0], 0.1f, 0.0, (
float)*max_nx);
450 if (ImGui::CollapsingHeader(
"RICOM Settings", ImGuiTreeNodeFlags_DefaultOpen))
452 static int filter_max = 8;
453 static bool init_kernel_img =
true;
454 bool kernel_changed = ImGui::DragInt(
"Kernel Size", &ricom->
kernel.
kernel_size, 1, 1, 300);
457 bool rot_changed = ImGui::SliderFloat(
"Rotation", &ricom->
kernel.
rotation, 0.0f, 360.0f,
"%.1f deg");
458 bool filter_changed = ImGui::Checkbox(
"Use filter?", &ricom->
kernel.
b_filter);
460 if (init_kernel_img || rot_changed || kernel_changed || filter_changed || filter_changed2)
462 init_kernel_img =
false;
476 if (kernel_changed || b_nx_changed)
480 float sxk = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y * 3) / 2;
481 ImGui::Image((
void *)(intptr_t)uiTextureIDs[9], ImVec2(sxk, sxk), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f));
483 ImGui::Image((
void *)(intptr_t)uiTextureIDs[10], ImVec2(sxk, sxk), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f));
488 if (ImGui::CollapsingHeader(
"vSTEM Settings", ImGuiTreeNodeFlags_DefaultOpen))
490 bool inner_changed = ImGui::SliderFloat(
"Inner Radius", &ricom->
detector.
radius[0], 0.0f, 182.0f,
"%.1f px");
491 bool outer_changed = ImGui::SliderFloat(
"Outer Radius", &ricom->
detector.
radius[1], 0.0f, 182.0f,
"%.1f px");
492 if (inner_changed || outer_changed)
500 if (b_merlin_live_menu)
502 if (ImGui::CollapsingHeader(
"Merlin Live Mode", ImGuiTreeNodeFlags_DefaultOpen))
505 ImGui::InputInt(
"hvbias", &merlin_settings.hvbias, 1, 10);
506 ImGui::InputInt(
"threshold0", &merlin_settings.threshold0, 1, 8);
507 ImGui::InputInt(
"threshold1", &merlin_settings.threshold1, 1, 8);
508 ImGui::InputFloat(
"dwell time (us)", &merlin_settings.dwell_time, 0.01, 0.1);
509 ImGui::Checkbox(
"trigger", &merlin_settings.trigger);
511 ImGui::Checkbox(
"headless", &merlin_settings.headless);
512 ImGui::Checkbox(
"continuousrw", &merlin_settings.continuousrw);
514 ImGui::Checkbox(
"raw", &merlin_settings.raw);
516 if (merlin_settings.raw)
518 ImGui::Text(
"Depth");
525 ImGui::RadioButton(
"12", &ricom->
camera.
depth, 12);
529 ImGui::Checkbox(
"save file?", &merlin_settings.save);
535 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
"Connected");
539 if (ImGui::Button(
"Show Acqusition Info", ImVec2(-1.0f, 0.0f)))
547 ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f),
"Not Connected");
550 if (ImGui::Button(
"Start Acquisition", ImVec2(-1.0f, 0.0f)))
554 std::this_thread::sleep_for(std::chrono::milliseconds(500));
563 if (ImGui::CollapsingHeader(
"File reconstruction", ImGuiTreeNodeFlags_DefaultOpen))
566 if (ImGui::Button(
"Open File", ImVec2(-1.0f, 0.0f)))
568 openFileDialog.Open();
571 openFileDialog.Display();
572 if (openFileDialog.HasSelected())
574 filename = openFileDialog.GetSelected().string();
575 b_file_selected =
true;
576 openFileDialog.ClearSelected();
581 ImGui::Text(
"File: %s", filename.c_str());
586 ImGui::Text(
"Depth");
591 ImGui::RadioButton(
"12", &ricom->
camera.
depth, 12);
593 if (ImGui::IsItemHovered())
595 ImGui::SetTooltip(
"Only applicable for handling recorded files, \n recorded in raw mode.");
603 if (ImGui::Button(
"Run File", ImVec2(-1.0f, 0.0f)))
615 float panel_h_min = control_menu_size.y * 0.4;
616 float panel_h_max = control_menu_size.y - 32;
617 v_splitter(5, menu_split_v, panel_h_min, panel_h_max, pos.y + ImGui::GetStyle().ItemSpacing.y * 3);
619 ImGui::BeginChild(
"Progress", ImVec2(0, 0),
true, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar);
620 ImGui::ProgressBar(ricom->
fr_count / (ricom->
fr_total), ImVec2(-1.0f, 0.0f));
621 ImGui::Text(
"Speed: %.2f kHz", ricom->
fr_freq);
622 if (ImGui::Button(
"Quit", ImVec2(-1.0f, 0.0f)))
631 ImGui::DockSpace(1319);
632 static auto first_time =
true;
636 ImGui::DockBuilderRemoveNode(1319);
637 ImGui::DockBuilderAddNode(1319, ImGuiDockNodeFlags_DockSpace);
638 ImGui::DockBuilderDockWindow(
"CBED", 1319);
639 ImGui::DockBuilderFinish(1319);
642 control_menu_size = ImGui::GetWindowSize();
645 main_dock.render(ImVec2(control_menu_size.x, pos.y), ImVec2(viewport->Size.x - control_menu_size.x, viewport->Size.y - menu_bar_size.y));
647 ImGui::Begin(
"CBED",
nullptr, ImGuiWindowFlags_NoScrollbar);
649 ImVec2 rem_space = ImGui::GetContentRegionAvail();
650 float tex_wh = (std::min)(rem_space.x, rem_space.y);
651 ImVec2 p = ImGui::GetCursorScreenPos();
659 com_rel_x = (std::max)(p.x, com_rel_x);
660 com_rel_y = (std::max)(p.y, com_rel_y);
661 com_rel_x = (std::min)(p.x + tex_wh, com_rel_x);
662 com_rel_y = (std::min)(p.y + tex_wh, com_rel_y);
664 float cross_width = tex_wh / 15.0f;
672 ImGui::Image((ImTextureID)uiTextureIDs[0], ImVec2(tex_wh, tex_wh), uv_min, uv_max, tint_col, border_col);
673 ImGui::GetWindowDrawList()->AddCircle(ImVec2(centre_x, centre_y), tex_wh * 0.03, IM_COL32(255, 255, 255, 255), 256);
674 ImGui::GetWindowDrawList()->AddLine(ImVec2(com_rel_x - cross_width, com_rel_y), ImVec2(com_rel_x + cross_width, com_rel_y), IM_COL32(255, 0, 0, 255), 1.5f);
675 ImGui::GetWindowDrawList()->AddLine(ImVec2(com_rel_x, com_rel_y - cross_width), ImVec2(com_rel_x, com_rel_y + cross_width), IM_COL32(255, 0, 0, 255), 1.5f);
678 ImGui::GetWindowDrawList()->AddCircle(ImVec2(centre_x, centre_y), tex_wh * (ricom->
detector.
radius[0] / ricom->
camera.
nx_cam), IM_COL32(255, 50, 0, 255), 256);
679 ImGui::GetWindowDrawList()->AddCircle(ImVec2(centre_x, centre_y), tex_wh * (ricom->
detector.
radius[1] / ricom->
camera.
nx_cam), IM_COL32(255, 150, 0, 255), 256);
685 ImVec2 pos_t = viewport->Pos;
686 pos_t[0] += control_menu_size[0] + 128;
687 pos_t[1] += menu_bar_size[1] + 128;
688 ImGui::SetNextWindowPos(pos_t, ImGuiCond_FirstUseEver);
689 ImVec2 size = {200, 400};
690 ImGui::SetNextWindowSize(size, ImGuiCond_FirstUseEver);
692 ImGui::Begin(
"Acquisition Header", &b_acq_open);
693 ImGui::BeginChild(
"Scrolling");
700 bool trigger = (b_trigger_update != ricom->
b_busy) && (ricom->
b_busy ==
false);
701 bool redraw_tick = (ricom->
b_busy && b_redraw && b_started);
702 update_views(generic_windows_f, ricom, b_restarted, trigger, redraw_tick);
703 update_views(generic_windows_c, ricom, b_restarted, trigger, redraw_tick);
706 b_trigger_update = ricom->
b_busy;
710 glViewport(0, 0, (
int)io.DisplaySize.x, (
int)io.DisplaySize.y);
711 glClear(GL_COLOR_BUFFER_BIT);
712 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
714 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
716 SDL_Window *backup_current_window = SDL_GL_GetCurrentWindow();
717 SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
718 ImGui::UpdatePlatformWindows();
719 ImGui::RenderPlatformWindowsDefault();
720 SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
723 SDL_GL_SwapWindow(window);
728 if (run_thread.joinable())
730 if (py_thread.joinable())
733 ImGui_ImplOpenGL3_Shutdown();
734 ImGui_ImplSDL2_Shutdown();
735 ImGui::DestroyContext();
737 SDL_GL_DeleteContext(gl_context);
738 SDL_DestroyWindow(window);
741 ini_file.write(ini_cfg,
true);
void v_splitter(float thickness, float &size0, const float &min_h, const float &max_h, const float &offset)
void bind_tex(SDL_Surface *srf, GLuint tex_id)
#define GENERIC_WINDOW_C(name)
#define GENERIC_WINDOW(name)
void update_views(std::map< std::string, ImGuiImageWindow< T >> &generic_windows_f, Ricom *ricom, bool b_restarted, bool trigger, bool b_redraw)
std::vector< float > f_approx
void approximate_frequencies(size_t n_im)
std::array< float, 2 > com_public
bool b_recompute_detector
std::vector< float > stem_data
std::vector< std::complex< float > > e_field_data
std::vector< float > com_map_x
std::vector< float > com_map_y
std::string connection_information
void check_ini_setting(mINI::INIStructure &ini_cfg, std::string section, std::string key, char *value)
bool ShowFontSelector(const char *label, int &selectedFont, mINI::INIStructure &ini_cfg)
bool ShowStyleSelector(const char *label, int &style_idx, mINI::INIStructure &ini_cfg)
void set_font(int font_idx)
void set_style(int style_idx)
void run_connection_script(Ricom *r, MerlinSettings *merlin, const std::string &python_path)