diff --git a/DearPyGui/include/mvMarvel.h b/DearPyGui/include/mvMarvel.h index c4181ac53..fb65e97c8 100644 --- a/DearPyGui/include/mvMarvel.h +++ b/DearPyGui/include/mvMarvel.h @@ -279,6 +279,24 @@ namespace Marvel { {mvPythonDataType::Integer, "xoffset"}, {mvPythonDataType::Integer, "yoffset"} }, "Adds a point with text to a plot.", "None", "Plotting") }); + + parsers->insert({ "set_xticks", mvPythonParser({ + {mvPythonDataType::String, "plot"}, + {mvPythonDataType::Object, "label_pairs", "list of [str,float]"}, + }, "Sets plots x ticks and labels", "None", "Plotting") }); + + parsers->insert({ "set_yticks", mvPythonParser({ + {mvPythonDataType::String, "plot"}, + {mvPythonDataType::Object, "label_pairs", "list of [str,float]"}, + }, "Sets plots y ticks and labels", "None", "Plotting") }); + + parsers->insert({ "reset_xticks", mvPythonParser({ + {mvPythonDataType::String, "plot"}, + }, "Sets plots x ticks and labels back to automatic", "None", "Plotting") }); + + parsers->insert({ "reset_yticks", mvPythonParser({ + {mvPythonDataType::String, "plot"}, + }, "Sets plots y ticks and labels back to automatic", "None", "Plotting") }); } static void AddLogCommands(std::map* parsers) diff --git a/DearPyGui/include/mvPythonTranslator.h b/DearPyGui/include/mvPythonTranslator.h index 58b4b9337..3603ddeda 100644 --- a/DearPyGui/include/mvPythonTranslator.h +++ b/DearPyGui/include/mvPythonTranslator.h @@ -68,13 +68,14 @@ namespace Marvel { static mvVec2 ToVec2 (PyObject* value, const std::string& message = "Type must be a list or tuple of floats."); static std::string ToString(PyObject* value, const std::string& message = "Type must be a string."); - static std::vector ToVectVec2 (PyObject* value, const std::string& message = "Type must be a list/tuple of list/tuple."); - static std::vector ToIntVect (PyObject* value, const std::string& message = "Type must be a list or tuple of integers."); - static std::vector ToFloatVect (PyObject* value, const std::string& message = "Type must be a list or tuple of floats."); - static std::vector ToStringVect (PyObject* value, const std::string& message = "Type must be a list or tuple of strings."); - static std::vector> ToVectInt2 (PyObject* value, const std::string& message = "Type must be an list/tuple of integer."); - static std::vector> ToVectPairString(PyObject* value, const std::string& message = "Type must be an list/tuple of string pairs."); - static std::vector> ToVectVectString(PyObject* value, const std::string& message = "Type must be an list/tuple of list/tuple of strings."); + static std::vector ToVectVec2 (PyObject* value, const std::string& message = "Type must be a list/tuple of list/tuple."); + static std::vector ToIntVect (PyObject* value, const std::string& message = "Type must be a list or tuple of integers."); + static std::vector ToFloatVect (PyObject* value, const std::string& message = "Type must be a list or tuple of floats."); + static std::vector ToStringVect (PyObject* value, const std::string& message = "Type must be a list or tuple of strings."); + static std::vector> ToVectInt2 (PyObject* value, const std::string& message = "Type must be an list/tuple of integer."); + static std::vector> ToVectPairString (PyObject* value, const std::string& message = "Type must be an list/tuple of string pairs."); + static std::vector> ToVectVectString (PyObject* value, const std::string& message = "Type must be an list/tuple of list/tuple of strings."); + static std::vector> ToVectPairStringFloat(PyObject* value, const std::string& message = "Type must be an list/tuple of str,float pairs."); private: diff --git a/DearPyGui/src/Core/AppItems/mvPlot.h b/DearPyGui/src/Core/AppItems/mvPlot.h index e04486902..3adb79508 100644 --- a/DearPyGui/src/Core/AppItems/mvPlot.h +++ b/DearPyGui/src/Core/AppItems/mvPlot.h @@ -76,6 +76,38 @@ namespace Marvel { m_colormap = colormap; } + void resetXTicks() + { + m_xlabels.clear(); + m_xclabels.clear(); + m_xlabelLocations.clear(); + } + + void resetYTicks() + { + m_ylabels.clear(); + m_yclabels.clear(); + m_ylabelLocations.clear(); + } + + void setXTicks(const std::vector& labels, const std::vector& locations) + { + m_xlabels = labels; + m_xlabelLocations = locations; + + for (const auto& item : m_xlabels) + m_xclabels.push_back(item.data()); + } + + void setYTicks(const std::vector& labels, const std::vector& locations) + { + m_ylabels = labels; + m_ylabelLocations = locations; + + for (const auto& item : m_ylabels) + m_yclabels.push_back(item.data()); + } + void clear() { for (auto& series : m_series) @@ -97,6 +129,17 @@ namespace Marvel { if (m_setYLimits) ImPlot::SetNextPlotLimitsY(m_ylimits.x, m_ylimits.y, ImGuiCond_Always); + if (!m_xlabels.empty()) + { + // TODO: Checks + ImPlot::SetNextPlotTicksX(m_xlabelLocations.data(), (int)m_xlabels.size(), m_xclabels.data()); + } + if (!m_ylabels.empty()) + { + // TODO: Checks + ImPlot::SetNextPlotTicksY(m_ylabelLocations.data(), (int)m_ylabels.size(), m_yclabels.data()); + } + if (ImPlot::BeginPlot(m_name.c_str(), m_xaxisName.c_str(), m_yaxisName.c_str(), ImVec2((float)m_width, (float)m_height), m_flags, m_xflags, m_yflags) ) @@ -185,6 +228,13 @@ namespace Marvel { std::string m_queryCallback; bool m_queried = false; float m_queryArea[4] = {0.0f , 0.0f, 0.0f, 0.0f}; + + std::vector m_xlabels; + std::vector m_ylabels; + std::vector m_xclabels; // to prevent conversion from string to char* every frame + std::vector m_yclabels; // to prevent conversion from string to char* every frame + std::vector m_xlabelLocations; + std::vector m_ylabelLocations; std::vector m_series; diff --git a/DearPyGui/src/Core/mvMarvel.cpp b/DearPyGui/src/Core/mvMarvel.cpp index 1d2693467..0fd130322 100644 --- a/DearPyGui/src/Core/mvMarvel.cpp +++ b/DearPyGui/src/Core/mvMarvel.cpp @@ -869,6 +869,114 @@ namespace Marvel { return mvPythonTranslator::GetPyNone(); } + PyObject* reset_xticks(PyObject* self, PyObject* args, PyObject* kwargs) + { + const char* plot; + + if (!(*mvApp::GetApp()->getParsers())["reset_xticks"].parse(args, kwargs, __FUNCTION__, &plot)) + return mvPythonTranslator::GetPyNone(); + + mvAppItem* aplot = mvApp::GetApp()->getItem(plot); + if (aplot == nullptr) + { + std::string message = plot; + ThrowPythonException(message + " plot does not exist."); + return mvPythonTranslator::GetPyNone(); + } + mvPlot* graph = static_cast(aplot); + + graph->resetXTicks(); + + return mvPythonTranslator::GetPyNone(); + } + + PyObject* reset_yticks(PyObject* self, PyObject* args, PyObject* kwargs) + { + const char* plot; + + if (!(*mvApp::GetApp()->getParsers())["reset_yticks"].parse(args, kwargs, __FUNCTION__, &plot)) + return mvPythonTranslator::GetPyNone(); + + mvAppItem* aplot = mvApp::GetApp()->getItem(plot); + if (aplot == nullptr) + { + std::string message = plot; + ThrowPythonException(message + " plot does not exist."); + return mvPythonTranslator::GetPyNone(); + } + mvPlot* graph = static_cast(aplot); + + graph->resetXTicks(); + + return mvPythonTranslator::GetPyNone(); + } + + PyObject* set_xticks(PyObject* self, PyObject* args, PyObject* kwargs) + { + const char* plot; + PyObject* label_pairs; + + if (!(*mvApp::GetApp()->getParsers())["set_xticks"].parse(args, kwargs, __FUNCTION__, &plot, &label_pairs)) + return mvPythonTranslator::GetPyNone(); + + mvAppItem* aplot = mvApp::GetApp()->getItem(plot); + if (aplot == nullptr) + { + std::string message = plot; + ThrowPythonException(message + " plot does not exist."); + return mvPythonTranslator::GetPyNone(); + } + mvPlot* graph = static_cast(aplot); + + auto mlabel_pairs = mvPythonTranslator::ToVectPairStringFloat(label_pairs); + + std::vector labels; + std::vector locations; + + for (const auto& item : mlabel_pairs) + { + labels.emplace_back(item.first.c_str()); + locations.emplace_back((double)item.second); + } + + graph->setXTicks(labels, locations); + + return mvPythonTranslator::GetPyNone(); + } + + PyObject* set_yticks(PyObject* self, PyObject* args, PyObject* kwargs) + { + const char* plot; + PyObject* label_pairs; + + if (!(*mvApp::GetApp()->getParsers())["set_yticks"].parse(args, kwargs, __FUNCTION__, &plot, &label_pairs)) + return mvPythonTranslator::GetPyNone(); + + mvAppItem* aplot = mvApp::GetApp()->getItem(plot); + if (aplot == nullptr) + { + std::string message = plot; + ThrowPythonException(message + " plot does not exist."); + return mvPythonTranslator::GetPyNone(); + } + mvPlot* graph = static_cast(aplot); + + auto mlabel_pairs = mvPythonTranslator::ToVectPairStringFloat(label_pairs); + + std::vector labels; + std::vector locations; + + for (const auto& item : mlabel_pairs) + { + labels.emplace_back(item.first.c_str()); + locations.emplace_back((double)item.second); + } + + graph->setYTicks(labels, locations); + + return mvPythonTranslator::GetPyNone(); + } + PyObject* set_plot_xlimits_auto(PyObject* self, PyObject* args, PyObject* kwargs) { const char* plot; @@ -5462,6 +5570,10 @@ namespace Marvel { ADD_PYTHON_FUNCTION(is_plot_queried) ADD_PYTHON_FUNCTION(get_plot_query_area) ADD_PYTHON_FUNCTION(clear_plot) + ADD_PYTHON_FUNCTION(reset_xticks) + ADD_PYTHON_FUNCTION(reset_yticks) + ADD_PYTHON_FUNCTION(set_xticks) + ADD_PYTHON_FUNCTION(set_yticks) ADD_PYTHON_FUNCTION(set_plot_xlimits_auto) ADD_PYTHON_FUNCTION(set_plot_ylimits_auto) ADD_PYTHON_FUNCTION(set_plot_xlimits) diff --git a/DearPyGui/src/Core/mvPythonTranslator.cpp b/DearPyGui/src/Core/mvPythonTranslator.cpp index 66c023da5..97bfbb1d1 100644 --- a/DearPyGui/src/Core/mvPythonTranslator.cpp +++ b/DearPyGui/src/Core/mvPythonTranslator.cpp @@ -450,4 +450,37 @@ namespace Marvel { return results; } + + std::vector> mvPythonTranslator::ToVectPairStringFloat(PyObject* value, const std::string& message) + { + std::vector> items; + mvGlobalIntepreterLock gil; + + if (PyTuple_Check(value)) + { + for (size_t i = 0; i < PyTuple_Size(value); i++) + { + PyObject* item = PyTuple_GetItem(value, i); + if (PyTuple_Size(item) == 2) + items.emplace_back(PyUnicode_AsUTF8(PyTuple_GetItem(item, 0)), (float)PyFloat_AsDouble(PyTuple_GetItem(item, 1))); + + } + + } + else if (PyList_Check(value)) + { + for (size_t i = 0; i < PyList_Size(value); i++) + { + PyObject* item = PyList_GetItem(value, i); + if (PyList_Size(item) == 2) + items.emplace_back(PyUnicode_AsUTF8(PyList_GetItem(item, 0)), (float)PyFloat_AsDouble(PyList_GetItem(item, 1))); + + } + } + + else + ThrowPythonException(message); + + return items; + } } \ No newline at end of file diff --git a/DearPyGui/stubs/dearpygui.pyi b/DearPyGui/stubs/dearpygui.pyi index 5aaa2d164..b9ce73903 100644 --- a/DearPyGui/stubs/dearpygui.pyi +++ b/DearPyGui/stubs/dearpygui.pyi @@ -744,6 +744,22 @@ def set_plot_xlimits(plot: str, xmin: float, xmax: float) -> None: """Sets x axis limits of a plot. (can be undone with set_plot_xlimits_auto()""" ... +def set_xticks(plot: str, label_pairs: List[List[str, float]]) -> None: + """Sets plots x ticks and labels""" + ... + +def set_yticks(plot: str, label_pairs: List[List[str, float]]) -> None: + """Sets plots x ticks and labels""" + ... + +def reset_xticks(plot: str) -> None: + """Sets plots x ticks and labels back to automatic""" + ... + +def reset_yticks(plot: str) -> None: + """Sets plots y ticks and labels back to automatic""" + ... + def set_plot_xlimits_auto(plot: str) -> None: """Sets plots x limits to be automatic.""" ... diff --git a/DearSandbox/Demo.py b/DearSandbox/Demo.py index 1133b5b9c..3ca47752c 100644 --- a/DearSandbox/Demo.py +++ b/DearSandbox/Demo.py @@ -236,6 +236,8 @@ def InsertCol(sender, data): add_button("Plot data", callback="PlotCallback") add_listbox("Colormaps", ("Default", "Dark", "Pastel", "Paired", "Viridis", "Plasma", "Hot", "Cool", "Pink", "Jet"), width=500, height=3, callback="colormapCallback") add_plot("Plot", "x-axis", "y-axis", height=-1); +set_xticks("Plot", [["X-axis", 0], ["1", 1]]) +set_yticks("Plot", [["Y-axis", 0], ["1", 1]]) end_tab() add_tab("Simple Plots") add_simple_plot("Simpleplot1", (0.3, 0.9, 2.5, 8.9))