背景:有时候kanzi运行后节点不显示,可能是visible/opacity等属性设置不正确,排查困难。做一个实时节点树,方便查看节点信息。
1. 引入imgui
在vs工程里导入glad
,glfw
,imgui
代码
vs属性-链接库,增加glfw3.lib
2. 实现框架
在onProjectLoaded
里创建子线程
CreateThread(NULL, 0, multiTask, 0, 0, NULL);
子线程负责创建glfw
窗口,刷新,销毁窗口
DWORD WINAPI multiTask(LPVOID p) {
FCreateWindow();
while (!glfwWindowShouldClose(m_Window)) {
Update();
}
Destory();
return 0;
};
2.1 创建窗口
void FCreateWindow() {
glfwInit();
const char* glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
m_Window = glfwCreateWindow(m_WindowWidth, m_WindowHeight, "Xml", nullptr, nullptr);
glfwMakeContextCurrent(m_Window);
glfwSwapInterval(1); // Enable vsync
int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
printf("status-%d\n", status);
printf("Vendor-%s\n", glGetString(GL_VENDOR));
printf("Renderer-%s\n", glGetString(GL_RENDERER));
printf("Version-%s\n", glGetString(GL_VERSION));
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons;
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge;
float fontSize = 18.0f; // *2.0f;
io.Fonts->AddFontFromFileTTF(("./font/Alimama_DongFangDaKai_Regular.ttf"), fontSize, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
io.FontDefault = io.Fonts->AddFontFromFileTTF(("./font/Alimama_DongFangDaKai_Regular.ttf"), fontSize, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ImGuiStyle &style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
// Setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(m_Window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
};
2.2 刷新
void Update() {
glfwPollEvents();
//start
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
{
// bool show_demo_window = true;
// ImGui::ShowDemoWindow(&show_demo_window);
ImGui::Begin("Project");
if (g_pApplication) {
ShowProjectTreeTop();
}
ImGui::End();
bool show_demo_window = true;
ImGui::ShowDemoWindow(&show_demo_window);
}
//end
ImGuiIO &io = ImGui::GetIO();
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
// Rendering
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(m_Window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
io.DisplaySize = ImVec2(display_w, display_h);
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
GLFWwindow *backup_current_context = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backup_current_context);
}
glfwSwapBuffers(m_Window);
};
2.3 销毁窗口
void Destory() {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(m_Window);
glfwTerminate();
};
3. 实现功能逻辑
遍历kanzi运行时节点,用tree
展示
void ShowProjectTreeTop()
{
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen;
static bool align_label_with_current_x_position = false;
static bool test_drag_and_drop = true;
if (align_label_with_current_x_position)
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
static int selection_mask = 0;
int node_clicked = -1;
ImGuiTreeNodeFlags node_flags = base_flags;
const bool is_selected = (selection_mask & (1 << 0)) != 0;
shared_ptr<Node> node = dynamic_pointer_cast<Node>(g_pApplication->getRoot());
if (is_selected && node.get() == g_CurrentNode)
node_flags |= ImGuiTreeNodeFlags_Selected;
bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)0, node_flags, node->getName().c_str(), 0);
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
node_clicked = 0;
g_CurrentNode = node.get();
}
if (node_open)
{
ShowProjectTree(node);
ImGui::TreePop();
}
if (node_clicked != -1)
{
// Update selection state
// (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if (ImGui::GetIO().KeyCtrl)
selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
selection_mask = (1 << node_clicked); // Click to single-select
}
if (align_label_with_current_x_position)
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
}
void ShowProjectTree(shared_ptr<Node>& node)
{
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen;
static bool align_label_with_current_x_position = false;
static bool test_drag_and_drop = false;
if (align_label_with_current_x_position)
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
static int selection_mask = 0;
int node_clicked = -1;
size_t size = node->getAbstractChildCountOverride();
for (unsigned int i = 0; i < size; ++i)
{
shared_ptr<Node> object = dynamic_pointer_cast<Node>(node->getAbstractChildOverride(i));
// Disable the default "open on single-click behavior" + set Selected flag according to our selection.
// To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.
ImGuiTreeNodeFlags node_flags = base_flags;
const bool is_selected = (selection_mask & (1 << i)) != 0;
if (is_selected && object.get() == g_CurrentNode)
node_flags |= ImGuiTreeNodeFlags_Selected;
std::string name = object->getName();
if (object->getProperty(Node::VisibleProperty)) {
}
else {
name += " NoVisible";
node_flags |= ImGuiTreeNodeFlags_TextDisabled;
}
if (object->getProperty(Node::OpacityProperty)) {
}
else {
//ImGui::TextDisabled("NoVisible");
name += " NoOpacity";
node_flags |= ImGuiTreeNodeFlags_TextDisabled;
}
bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, (char *)name.c_str(), i);
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
node_clicked = i;
g_CurrentNode = object.get();
}
if (node_open)
{
if (object->getAbstractChildCountOverride() > 0)
{
ShowProjectTree(object);
}
else {
Viewport2DSharedPtr v2d = dynamic_pointer_cast<Viewport2D>(object);
if (v2d) {//Viewport2D
SceneSharedPtr sc = v2d->getScene();
if (sc->getChildCount() > 0)
{
shared_ptr<Node3D> node = dynamic_pointer_cast<Node3D>(sc);
ShowProjectTree3D(node);
}
}
}
ImGui::TreePop();
}
}
if (node_clicked != -1)
{
// Update selection state
// (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if (ImGui::GetIO().KeyCtrl)
selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
selection_mask = (1 << node_clicked); // Click to single-select
}
if (align_label_with_current_x_position)
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
}
void ShowProjectTree3D(shared_ptr<Node3D>& node)
{
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen;
static bool align_label_with_current_x_position = false;
static bool test_drag_and_drop = false;
if (align_label_with_current_x_position)
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
static int selection_mask = 0;
int node_clicked = -1;
size_t size = node->getChildCount();
for (unsigned int i = 0; i < size; ++i)
{
Node3DSharedPtr object = node->getChild(i);
// Disable the default "open on single-click behavior" + set Selected flag according to our selection.
// To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.
ImGuiTreeNodeFlags node_flags = base_flags;
const bool is_selected = (selection_mask & (1 << i)) != 0;
if (is_selected && object.get() == g_CurrentNode)
node_flags |= ImGuiTreeNodeFlags_Selected;
std::string name = object->getName();
if (object->getProperty(Node::VisibleProperty)) {
}
else {
name += " NoVisible";
node_flags |= ImGuiTreeNodeFlags_TextDisabled;
}
if (object->getProperty(Node::OpacityProperty)) {
}
else {
//ImGui::TextDisabled("NoVisible");
name += " NoOpacity";
node_flags |= ImGuiTreeNodeFlags_TextDisabled;
}
bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, (char *)name.c_str(), i);
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
node_clicked = i;
g_CurrentNode = object.get();
}
if (node_open)
{
if (object->getChildCount() > 0)
{
ShowProjectTree3D(object);
}
ImGui::TreePop();
}
}
if (node_clicked != -1)
{
// Update selection state
// (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
if (ImGui::GetIO().KeyCtrl)
selection_mask ^= (1 << node_clicked); // CTRL+click to toggle
else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
selection_mask = (1 << node_clicked); // Click to single-select
}
if (align_label_with_current_x_position)
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
}
4. 效果
运行kanzi程序后,会额外出来一个窗口,实时显示所有节点信息
节点无效置灰请参考 imgui tree节点无效置灰实现方案
5. 拓展
将来可以根据点击某一个节点,展示属性窗口。