[imgui] 7. CSVEditor
2023년 7월 10일
목표 #
- float 데이터를 저장하는 테이블 구현
- csv 형태로 저장, 로드
- 슬라이더로 행, 열 크기 조절가능

구현코드 #
메인함수 #
1class WindowClass
2{
3public:
4 static constexpr auto popUpFlags =
5 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
6 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
7 static constexpr auto popUpSize = ImVec2(300.0f, 100.0f);
8 static constexpr auto popUpButtonSize = ImVec2(120.0f, 0.0f);
9 static constexpr auto popUpPos = ImVec2(1280.0f / 2.0f - popUpSize.x / 2.0f,
10 720.0f / 2.0f - popUpSize.y / 2.0f);
11
12 // 행,열 최대길이
13 static constexpr auto maxNumRows = 30;
14 static constexpr auto maxNumCols = 8;
15
16public:
17 WindowClass()
18 : colSize(0), rowSize(0), data({}), filenameBuffer("test.csv"){};
19 void Draw(std::string_view label);
20
21private:
22 void DrawSizeButtons(); // 행, 열 길이조절하는 슬라이더, 버튼
23 void DrawIoButtons(); // Save, Load, Clear
24 void DrawTable(); // 실제 테이블이 구현되는곳
25
26 void DrawSavePopup();
27 void DrawLoadPopup();
28
29 // 셀 클릭했을때 값 지정할 수 있는 팝업
30 void DrawValuePopup(const int row, const int col);
31
32 void SaveToCsvFile(std::string_view filename);
33 void LoadFromCsvFile(std::string_view filename);
34
35 // 셀에서 value를 format에 맞게 데이터 출력
36 template <typename T>
37 void PlotCellValue(std::string_view formatter, const T value);
38
39 // 팝업 레이아웃 설정 모아둔 함수
40 void SetPopupLayout();
41
42private:
43 std::int32_t colSize;
44 std::int32_t rowSize;
45 std::vector<std::vector<float>> data; // 실제 테이블의 셀 데이터
46
47 char filenameBuffer[256];
48};
1void WindowClass::Draw(std::string_view label)
2{
3 constexpr static auto window_flags =
4 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
5 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar;
6 constexpr static auto window_size = ImVec2(1280.0F, 720.0F);
7 constexpr static auto window_pos = ImVec2(0.0F, 0.0F);
8
9 ImGui::SetNextWindowSize(window_size);
10 ImGui::SetNextWindowPos(window_pos);
11
12 ImGui::Begin(label.data(), nullptr, window_flags);
13
14 DrawSizeButtons();
15 ImGui::Separator();
16 DrawIoButtons();
17 ImGui::Separator();
18 DrawTable();
19
20 ImGui::End();
21}
DrawSizeButton #
1void WindowClass::DrawSizeButtons()
2{
3 auto user_control_rows = false;
4 auto user_control_cols = false;
5
6 auto slider_value_rows = rowSize;
7 auto slider_value_cols = colSize;
8
9 ImGui::Text("Num Rows: ");
10 ImGui::SameLine();
11
12 // 정수형 슬라이더 구현.
13 // 슬라이더와 버튼이 조작됐는지 체크. 이후 조작여부에 따라 테이블 수정해야되기 때문
14 if (ImGui::SliderInt("##numRows", &slider_value_rows, 0, maxNumRows))
15 {
16 user_control_rows = true;
17 rowSize = slider_value_rows;
18 }
19 ImGui::SameLine();
20 if (ImGui::Button("Add Row") && rowSize < maxNumRows)
21 {
22 ++rowSize;
23 user_control_rows = true;
24 }
25 ImGui::SameLine();
26 if (ImGui::Button("Drop Row") && rowSize > 0)
27 {
28 --rowSize;
29 user_control_rows = true;
30 }
31
32 ImGui::Text("Num Cols: ");
33 /* ... Column 도 Row 처럼 버튼 컨트롤 ... */
34
35 if (user_control_rows)
36 {
37 // rows 가 변경된 경우, row 추가
38 data.resize(rowSize, std::vector<float>(colSize, 0.0f));
39 }
40 else if (user_control_cols)
41 {
42 for (auto row = 0; row < rowSize; ++row)
43 {
44 data[row].resize(colSize, 0.0f);
45 }
46 }
47}
DrawIoButtons #
그냥 버튼에 따라 팝업을 호출하는 함수이다.
1void WindowClass::DrawIoButtons()
2{
3 if (ImGui::Button("Save"))
4 ImGui::OpenPopup("Save File");
5 ImGui::SameLine();
6 if (ImGui::Button("Load"))
7 ImGui::OpenPopup("Load File");
8 ImGui::SameLine();
9 if (ImGui::Button("Clear"))
10 {
11 data.clear();
12 rowSize = 0;
13 colSize = 0;
14 }
15 DrawSavePopup();
16 DrawLoadPopup();
17}
Save / Load 로직 #
csv 파일이기 때문에 저장이나 로드할때 , 구분자를 이용해서 데이터를 구분했다.
1void WindowClass::SaveToCsvFile(std::string_view filename)
2{
3 auto out = std::ofstream{filename.data()};
4 if (!out.is_open())
5 return;
6
7 for (std::int32_t row = 0; row < rowSize; ++row)
8 {
9 for (std::int32_t col = 0; col < colSize; ++col)
10 {
11 out << data[row][col] << ",";
12 }
13 out << "\n";
14 }
15 out.close();
16}
DrawTable #
데이터를 테이블 형태로 그려주는 함수이다.
1// 데이터를 포맷에 맞게 text 요소를 그려주는 함수.
2// 테이블을 생성하고 나면 TableNextColumn() 를 호출해야 테이블에 칸이 생성되고
3// 칸이 생성된 상태에서 내부에 Text 요소를 넣는것이다.
4template <typename T>
5void WindowClass::PlotCellValue(std::string_view formatter, const T value)
6{
7 ImGui::TableNextColumn();
8 ImGui::Text(formatter.data(), value);
9}
10
11void WindowClass::DrawTable()
12{
13 constexpr static auto table_flags =
14 ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuter;
15
16 static auto row_clicked = 0;
17 static auto col_clicked = 0;
18
19 if (colSize == 0)
20 return;
21
22 // 테이블의 시작. colSize가 1일때부터 테이블이 그려진다.
23 ImGui::BeginTable("Table", colSize, table_flags);
24
25 // 열 이름 설정. 테이블이 그려지는데는 상관없지만, 각 열에대한 스타일링도 따로 할수도 있다.
26 for (std::int32_t col = 0; col < colSize; ++col)
27 {
28 const auto col_name = fmt::format("{}", 'A' + col);
29 ImGui::TableSetupColumn(col_name.data(),
30 ImGuiTableColumnFlags_WidthFixed,
31 1280.0f / static_cast<float>(colSize));
32 }
33
34 // 열 하나를 그리는데, Headers 플래그를 넣어서 강조. 그리고 내부에는 열이름 데이터를 채움
35 ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
36 for (std::int32_t col = 0; col < colSize; ++col)
37 PlotCellValue("%c", 'A' + col);
38
39 for (std::int32_t row = 0; row < rowSize; ++row)
40 {
41 for (std::int32_t col = 0; col < colSize; ++col)
42 {
43 PlotCellValue("%f", data[row][col]);
44 if (ImGui::IsItemClicked())
45 {
46 ImGui::OpenPopup("Change Value");
47 row_clicked = row;
48 col_clicked = col;
49 }
50 else if (ImGui::IsItemHovered())
51 {
52 // 마우스만 올려둔 경우 행, 열의 위치를 출력하는 툴팁 그리기
53 ImGui::SetTooltip("Cell: (%d, %d)", row, col);
54 }
55 }
56 ImGui::TableNextRow();
57 }
58 DrawValuePopup(row_clicked, col_clicked);
59 ImGui::EndTable();
60}
Change Value Popup #
얘는 좀 코드가 달라서 추가했다. 비슷하긴함
1void WindowClass::DrawValuePopup(const int row, const int col)
2{
3 static char buffer[64] = { 0, };
4 const auto esc_pressed =
5 ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape));
6
7 SetPopupLayout();
8 if (ImGui::BeginPopupModal("Change Value", nullptr, popUpFlags))
9 {
10 const auto label = fmt::format("##{}_{}", row, col);
11 ImGui::InputText(label.data(), buffer, sizeof(buffer));
12
13 if (ImGui::Button("Save", popUpButtonSize))
14 {
15 // 값을 data 벡터에 집어넣는다.
16 data[row][col] = std::stof(buffer);
17 ImGui::CloseCurrentPopup();
18 }
19
20 ImGui::SameLine();
21
22 if (ImGui::Button("Cancel", popUpButtonSize) || esc_pressed)
23 {
24 ImGui::CloseCurrentPopup();
25 }
26 ImGui::EndPopup();
27 }
28}