Eine kleine, abhängigkeitsfreie Node-Editor-Erweiterung für Dear Imgui.
Imnodes zielt darauf ab, eine einfache Schnittstelle im Sofortmodus zum Erstellen eines Knoteneditors innerhalb eines ImGui-Fensters bereitzustellen. Imnodes bietet einfache, anpassbare Bausteine, die ein Benutzer zum Erstellen seines Knoteneditors benötigt.
Merkmale:
imnodes.h
, imnodes_internal.h
und imnodes.cpp
und fügen Sie sie zusammen mit ImGui in Ihr Projekt ein. Dieses Repository enthält einige Beispieldateien unter example/
. Sie sind als einfache Beispiele gedacht, die Ihnen eine Vorstellung davon geben, was Sie mit imnodes erstellen können.
Wenn Sie die Beispiele erstellen müssen, können Sie dazu das bereitgestellte CMake-Skript verwenden.
# Initialize the vcpkg submodule
$ git submodule update --init
# Run the generation step and build
$ cmake -B build-release/ -S . -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
$ cmake --build build-release -- -j
Beachten Sie, dass dies nicht unter Linux getestet wurde und auf der Plattform wahrscheinlich fehlschlagen wird.
Hier ein kleiner Überblick über die Verwendung der Erweiterung. Für weitere Informationen zur Beispielverwendung scrollen Sie zum Ende der README-Datei.
Bevor etwas unternommen werden kann, muss die Bibliothek initialisiert werden. Dies kann gleichzeitig mit der Initialisierung dear imgui
erfolgen.
ImGui::CreateContext ();
ImNodes::CreateContext ();
// elsewhere in the code...
ImNodes::DestroyContext ();
ImGui::DestroyContext ();
Der Knoteneditor ist ein Arbeitsbereich, der Knoten enthält. Der Knoteneditor muss wie jedes andere UI-Element innerhalb eines Fensters instanziiert werden.
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
ImNodes::EndNodeEditor ();
ImGui::End ();
Jetzt sollten Sie einen Arbeitsbereich mit einem sichtbaren Raster im Fenster haben. Ein leerer Knoten kann nun instanziiert werden:
const int hardcoded_node_id = 1 ;
ImNodes::BeginNodeEditor ();
ImNodes::BeginNode (hardcoded_node_id);
ImGui::Dummy (ImVec2( 80 . 0f , 45 . 0f ));
ImNodes::EndNode ();
ImNodes::EndNodeEditor ();
Knoten, wie Fenster in dear imgui
müssen eindeutig identifiziert werden. Wir können die Knotentitel jedoch nicht zur Identifizierung verwenden, da es möglich sein sollte, viele Knoten mit demselben Namen im Arbeitsbereich zu haben. Stattdessen verwenden Sie zur Identifizierung einfach ganze Zahlen.
Attribute sind der UI-Inhalt des Knotens. Ein Attribut hat auf beiden Seiten des Knotens einen Stift (den kleinen Kreis). Es gibt zwei Arten von Attributen: Eingabe- und Ausgabeattribute. Eingabeattribut-Pins befinden sich auf der linken Seite des Knotens und Ausgabeattribut-Pins auf der rechten Seite. Pins müssen wie Knoten eindeutig identifiziert werden.
ImNodes::BeginNode (hardcoded_node_id);
const int output_attr_id = 2 ;
ImNodes::BeginOutputAttribute (output_attr_id);
// in between Begin|EndAttribute calls, you can call ImGui
// UI functions
ImGui::Text ( " output pin " );
ImNodes::EndOutputAttribute ();
ImNodes::EndNode ();
Der Erweiterung ist es eigentlich egal, was im Attribut steht. Es rendert lediglich den Pin für das Attribut und ermöglicht dem Benutzer, Verknüpfungen zwischen Pins zu erstellen.
Mit BeginNodeTitleBar
und EndNodeTitleBar
kann dem Knoten eine Titelleiste hinzugefügt werden. Wie bei Attributen platzieren Sie den Inhalt Ihrer Titelleiste zwischen den Funktionsaufrufen. Beachten Sie, dass diese Funktionen aufgerufen werden müssen, bevor Attribute oder andere dear imgui
UI-Elemente zum Knoten hinzugefügt werden, da das Layout des Knotens in der Reihenfolge von oben nach unten aufgebaut ist.
ImNodes::BeginNode (hardcoded_node_id);
ImNodes::BeginNodeTitleBar ();
ImGui::TextUnformatted ( " output node " );
ImNodes::EndNodeTitleBar ();
// pins and other node UI content omitted...
ImNodes::EndNode ();
Der Benutzer muss auch seine eigenen Verknüpfungen zwischen Knoten rendern. Ein Link ist eine Kurve, die zwei Attribute verbindet. Ein Link ist nur ein Paar von Attribut-IDs. Und wie Knoten und Attribute müssen auch Links durch eindeutige Ganzzahlwerte identifiziert werden:
std::vector<std::pair< int , int >> links;
// elsewhere in the code...
for ( int i = 0 ; i < links.size(); ++i)
{
const std::pair< int , int > p = links[i];
// in this case, we just use the array index of the link
// as the unique identifier
ImNodes::Link (i, p. first , p. second );
}
Nachdem EndNodeEditor
aufgerufen wurde, können Sie mit dem Funktionsaufruf IsLinkCreated
überprüfen, ob während des Frames ein Link erstellt wurde:
int start_attr, end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr))
{
links. push_back ( std::make_pair (start_attr, end_attr));
}
Neben der Suche nach neuen Links können Sie auch prüfen, ob sich der Mauszeiger über UI-Elemente bewegt:
int node_id;
if (ImNodes::IsNodeHovered(&node_id))
{
node_hovered = node_id;
}
Sie können auch überprüfen, ob ein Knoten ausgewählt wurde. Knoten können angeklickt oder durch Anklicken und Ziehen des Kästchenselektors ausgewählt werden.
// Note that since many nodes can be selected at once, we first need to query the number of
// selected nodes before getting them.
const int num_selected_nodes = ImNodes::NumSelectedNodes();
if (num_selected_nodes > 0 )
{
std::vector< int > selected_nodes;
selected_nodes. resize (num_selected_nodes);
ImNodes::GetSelectedNodes (selected_nodes. data ());
}
Weitere UI-Ereignisfunktionen finden Sie unter imnodes.h
.
Wie bei dear imgui
kann der Stil der Benutzeroberfläche geändert werden. Sie können den Farbstil einzelner Knoten, Pins und Links in der Bildmitte festlegen, indem Sie ImNodes::PushColorStyle
und ImNodes::PopColorStyle
aufrufen.
// set the titlebar color of an individual node
ImNodes::PushColorStyle (
ImNodesCol_TitleBar, IM_COL32( 11 , 109 , 191 , 255 ));
ImNodes::PushColorStyle (
ImNodesCol_TitleBarSelected, IM_COL32( 81 , 148 , 204 , 255 ));
ImNodes::BeginNode (hardcoded_node_id);
// node internals here...
ImNodes::EndNode ();
ImNodes::PopColorStyle ();
ImNodes::PopColorStyle ();
Wenn der Stil nicht mitten im Frame festgelegt wird, kann stattdessen ImNodes::GetStyle
aufgerufen werden und die Werte können direkt im Stilarray festgelegt werden.
// set the titlebar color for all nodes
ImNodesStyle& style = ImNodes::GetStyle();
style.colors[ImNodesCol_TitleBar] = IM_COL32( 232 , 27 , 86 , 255 );
style.colors[ImNodesCol_TitleBarSelected] = IM_COL32( 241 , 108 , 146 , 255 );
Um eine schnellere Navigation in großen Diagrammen zu ermöglichen, können Sie ein interaktives Minikarten-Overlay verwenden. Die Minikarte kann gezoomt und gescrollt werden. Editorknoten verfolgen das Schwenken der Minikarte entsprechend.
ImGui::Begin ( " node editor " );
ImNodes::BeginNodeEditor ();
// add nodes...
// must be called right before EndNodeEditor
ImNodes::MiniMap ();
ImNodes::EndNodeEditor ();
ImGui::End ();
Die relative Größe und Eckposition der Minikarte im Editorbereich kann wie folgt angegeben werden:
// MiniMap is a square region with a side length that is 20% the largest editor canvas dimension
// See ImNodesMiniMapLocation_ for other corner locations
ImNodes::MiniMap ( 0 . 2f , ImNodesMiniMapLocation_TopRight);
Die Minikarte unterstützt auch eine begrenzte Anpassung des Node-Hovering durch einen benutzerdefinierten Rückruf.
// User callback
void mini_map_node_hovering_callback ( int node_id, void * user_data)
{
ImGui::SetTooltip ( " This is node %d " , node_id);
}
// Later on...
ImNodes::MiniMap ( 0 . 2f , ImNodesMiniMapLocation_TopRight, mini_map_node_hovering_callback, custom_user_data);
// 'custom_user_data' can be used to supply extra information needed for drawing within the callback
ImNodes können angepasst werden, indem beim Kompilieren ein imnodes_config.h
-Header bereitgestellt und die Definition von IMNODES_USER_CONFIG=imnodes_config.h
angegeben wird.
Derzeit ist es möglich, den Typ der Minimap-Hovering-Rückruffunktion zu überschreiben. Dies ist nützlich, wenn Sie Bindungen für eine andere Sprache generieren.
Hier ist ein Beispiel für imnodes_config.h, das einen Pybind-Wrapper für den Rückruf generiert.
# pragma once
# include < pybind11/functional.h >
namespace pybind11 {
inline bool PyWrapper_Check (PyObject *o) { return true ; }
class wrapper : public object {
public:
PYBIND11_OBJECT_DEFAULT (wrapper, object, PyWrapper_Check)
wrapper ( void * x) { m_ptr = (PyObject*)x; }
explicit operator bool () const { return m_ptr != nullptr && m_ptr != Py_None; }
};
} // namespace pybind11
namespace py = pybind11;
# define ImNodesMiniMapNodeHoveringCallback py::wrapper
# define ImNodesMiniMapNodeHoveringCallbackUserData py::wrapper
ImGui::Separator()
erstreckt sich über die aktuelle Fensterspanne. Die Verwendung eines Trennzeichens innerhalb eines Knotens führt daher dazu, dass das Trennzeichen aus dem Knoten in das Knoteneditor-Raster gelangt. Weitere Informationen zur Bibliotheksnutzung finden Sie im Verzeichnis examples/
.