Repository: hoffstadt/DearPyGui (language: python)

Issue:
Oddities regarding configuration attributes
DearPyGui: v1.1.1
Operating System: Windows 10/11 x64


The purpose of this bug report is to highlight several issues regarding configuration/information/states for specific items that I discovered while running a functional test on my own framework (they were discovered all at once and not one-at-a-time). Some of these may not actually be bugs or may be known/purposeful, but are included for the sake of completeness. Some may have also been included in other bug reports.


**NOTE**: When "is/is not configurable" is used to help describe an issue below, I refer to the fact that `SystemError` is thrown when calling `configure_item` if the attribute is not configurable, with no error being thrown if the attribute IS configurable.
* **mvWindowAppItem**: `on_close` is configurable. It is not included in the return of `get_item_configuration`.

* **mvNodeEditor**: `delink_callback` is configurable. The `delink_callback` key will be missing from `get_item_configuration` **only when `delink_callback` is not `None`**.

* **mvSubPlots**: The key representing `columns` in `get_item_configuration` is `cols`, but is configurable using `columns`. The function parameter in `add_subplots` for this value is also `columns`. 

* **mvDragPayload**: `drag_data` and `drop_data` are both missing from `get_item_configuration`, but no error is raised when trying to configure them.

* **mvInputText**: The key representing `multiline` in `get_item_configuration` is `multline` (likely misspelled), but is configurable using `multiline`. The function parameter in `add_input_text` for this value is also `multiline`. 
    
* **General** (most items):
    * `pos` is configurable via `configure_item`, but it is the sole "configurable" state in the return of `get_item_state`. My opinion is that this would be better in the return of `get_item_configuration`.
    * Information regarding an item's bound handler registry cannot currently be obtained through the DPG API.
    * Information regarding the bound application theme and font cannot currently be obtained through the DPG API.


Testcase code is below. I tried to keep it as short as possible, but is still quite long so I aplogize.  

```python
from dearpygui import dearpygui as dpg

dpg.create_context()
dpg.create_viewport()
dpg.setup_dearpygui()
dpg.show_viewport()


# Creating test case examples.
with dpg.window() as window:
    # Default for `delink_callback` is `None` in `dearpygui.dearpygui`.
    # In `dearpygui._dearpygui` the default is an empty string.
    with dpg.node_editor() as node_editor: 
        ...
    with dpg.subplots(rows=2, columns=5) as sub_plots:
        ...
    with dpg.drag_payload(
        drag_data="drag_data",
        drop_data="drop_data",
        parent   =dpg.add_text("`drag_payload` parent")
        ) as drag_payload:
        ...
    input_text     = dpg.add_input_text(label="`input_text`")
    colormap_scale = dpg.add_colormap_scale()
    knob_float     = dpg.add_knob_float(min_value=10, max_value=50)


# Testcase 1: mvWindowAppItem 
window_attrs = dpg.get_item_configuration(window) | \
               dpg.get_item_info(window) | \
               dpg.get_item_state(window)
print(f"[`mvWindowAppItem`] Test 1/1: {'on_close' in window_attrs}")


# Testcase 2: mvNodeEditor
print(f"[`mvNodeEditor`] Test 1/2: {'delink_callback' in dpg.get_item_configuration(node_editor)}")
dpg.configure_item(node_editor, delink_callback='')
print(f"[`mvNodeEditor`] Test 2/2: {'delink_callback' in dpg.get_item_configuration(node_editor)}")


# Testcase 3: mvSubPlots
print(f"[`mvSubplots`] Test 1/2: {'columns' in dpg.get_item_configuration(sub_plots)}")
dpg.configure_item(sub_plots, columns=7)
# The key representing `columns` is `cols`.
# The result of `dpg.get_item_configuration(sub_plots)["cols"]` is 7.
print(f"[`mvSubplots`] Test 2/2: {'cols' in dpg.get_item_configuration(sub_plots)}")


# Testcase 4: mvDragPayload
print(f"[`mvDragPayload`] Test 1/1: {any(attr in dpg.get_item_configuration(drag_payload) for attr in ('drag_data', 'drop_data'))}")
# No error raised here, but can't confirm the change outside of UI.
dpg.configure_item(drag_payload, drag_data="changed `drag_data`", drop_data="changed `drop_data`")


# Testcase 5: mvInputText
print(f"[`mvInputText`] Test 1/2: {'multiline' in dpg.get_item_configuration(input_text)}")
dpg.configure_item(input_text, multiline=True)  # configurable using `multiline`.
# The key representing `multiline` is `multline` (likely misspelled).
# The result of `dpg.get_item_configuration(input_text)["multline"]` is True.
print(f"[`mvInputText`] Test 2/2: {'multline' in dpg.get_item_configuration(input_text)}")


# Testcase 6: mvColorMapScale
print(f"[`mvColorMapScale`] Test 1/1: {'colormap' in dpg.get_item_configuration(colormap_scale)}")


# Testcase 7: mvKnobFloat
print(f"[`mvKnobFloat`] Test 1/2: {any(attr in dpg.get_item_configuration(knob_float) for attr in ('min_value', 'max_value'))}")
# No errors configuring `min/max_value`
dpg.configure_item(knob_float, min_value=25, max_value=100)
# `mix/max_value` represented by `min/max_scale` keys.
# The result of `dpg.get_item_configuration(knob_float)["min/max_scale"]` will be 25 or 100 respectively.
print(f"[`mvKnobFloat`] Test 2/2: {all(attr in dpg.get_item_configuration(knob_float) for attr in ('min_scale', 'max_scale'))}")


while dpg.is_dearpygui_running():
    dpg.render_dearpygui_frame()
dpg.destroy_context()
```



Discussion / hints:
Upon updating my unit test I've discovered a couple more:

* **mvFileExtension**: `extension` is not included in `get_item_configuration`.
* **mvColorMap**: `colors` and `qualitative` are not included in `get_item_configuration`.

I'm bound to find more as time goes on. Several drawing and plotting items were excluded in my test.

Fix patch:
diff --git a/src/mvContainers.cpp b/src/mvContainers.cpp
index 8fd7fd65b..a48a536e0 100644
--- a/src/mvContainers.cpp
+++ b/src/mvContainers.cpp
@@ -86,6 +86,22 @@ DearPyGui::fill_configuration_dict(const mvDragPayloadConfig& inConfig, PyObject
 {
     if (outDict == nullptr)
         return;
+
+    if (inConfig.dragData)
+    {
+        Py_XINCREF(inConfig.dragData);
+        PyDict_SetItemString(outDict, "drag_data", inConfig.dragData);
+    }
+    else
+        PyDict_SetItemString(outDict, "drag_data", GetPyNone());
+
+    if (inConfig.dropData)
+    {
+        Py_XINCREF(inConfig.dropData);
+        PyDict_SetItemString(outDict, "drop_data", inConfig.dropData);
+    }
+    else
+        PyDict_SetItemString(outDict, "drop_data", GetPyNone());
 }
 
 void
diff --git a/src/mvFileExtension.cpp b/src/mvFileExtension.cpp
index fb4d1fb9e..130a437c3 100644
--- a/src/mvFileExtension.cpp
+++ b/src/mvFileExtension.cpp
@@ -48,4 +48,6 @@ void mvFileExtension::getSpecificConfiguration(PyObject* dict)
 
 	PyDict_SetItemString(dict, "custom_text", mvPyObject(ToPyString(_customText)));
 	PyDict_SetItemString(dict, "color", mvPyObject(ToPyColor(_color)));
+	PyDict_SetItemString(dict, "extension", mvPyObject(ToPyString(_extension)) );
+
 }
\ No newline at end of file
diff --git a/src/mvPlotting.cpp b/src/mvPlotting.cpp
index 6959b8cb8..5cf9f9f1a 100644
--- a/src/mvPlotting.cpp
+++ b/src/mvPlotting.cpp
@@ -287,7 +287,7 @@ DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, mvDragPointConfig
 	outConfig.value = *static_cast<std::shared_ptr<std::array<double, 4>>*>(item.getValue());
 }
 
-void 
+void
 DearPyGui::set_data_source(mvAppItem& item, mvUUID dataSource, std::shared_ptr<std::vector<std::vector<double>>>& outValue)
 {
 	if (dataSource == item.config.source) return;
@@ -744,7 +744,7 @@ DearPyGui::draw_drag_point(ImDrawList* drawlist, mvAppItem& item, mvDragPointCon
 	}
 }
 
-void 
+void
 DearPyGui::draw_bar_series(ImDrawList* drawlist, mvAppItem& item, const mvBarSeriesConfig& config)
 {
 
@@ -943,7 +943,7 @@ DearPyGui::draw_scatter_series(ImDrawList* drawlist, mvAppItem& item, const mvBa
 	//-----------------------------------------------------------------------------
 	// post draw
 	//-----------------------------------------------------------------------------
-	// 
+	//
 	// pop font off stack
 	if (item.font)
 		ImGui::PopFont();
@@ -1011,7 +1011,7 @@ DearPyGui::draw_stair_series(ImDrawList* drawlist, mvAppItem& item, const mvBasi
 	//-----------------------------------------------------------------------------
 	// post draw
 	//-----------------------------------------------------------------------------
-	// 
+	//
 	// pop font off stack
 	if (item.font)
 		ImGui::PopFont();
@@ -1079,7 +1079,7 @@ DearPyGui::draw_stem_series(ImDrawList* drawlist, mvAppItem& item, const mvBasic
 	//-----------------------------------------------------------------------------
 	// post draw
 	//-----------------------------------------------------------------------------
-	// 
+	//
 	// pop font off stack
 	if (item.font)
 		ImGui::PopFont();
@@ -1150,7 +1150,7 @@ DearPyGui::draw_shade_series(ImDrawList* drawlist, mvAppItem& item, const mvBasi
 	//-----------------------------------------------------------------------------
 	// post draw
 	//-----------------------------------------------------------------------------
-	// 
+	//
 	// pop font off stack
 	if (item.font)
 		ImGui::PopFont();
@@ -2137,7 +2137,7 @@ DearPyGui::draw_plot_annotation(ImDrawList* drawlist, mvAppItem& item, mvAnnotat
 
 }
 
-void 
+void
 DearPyGui::set_positional_configuration(PyObject* inDict, mvBarSeriesConfig& outConfig)
 {
 	if (!VerifyRequiredArguments(GetParsers()[GetEntityCommand(mvAppItemType::mvBarSeries)], inDict))
@@ -2435,7 +2435,7 @@ DearPyGui::set_configuration(PyObject* inDict, mvBarSeriesConfig& outConfig)
 
 }
 
-void 
+void
 DearPyGui::set_configuration(PyObject* inDict, mvBasicSeriesConfig& outConfig)
 {
 	if (inDict == nullptr)
@@ -2823,7 +2823,7 @@ DearPyGui::fill_configuration_dict(const mvPlotLegendConfig& inConfig, PyObject*
 
 }
 
-void 
+void
 DearPyGui::fill_configuration_dict(const mvBarSeriesConfig& inConfig, PyObject* outDict)
 {
 	if (outDict == nullptr)
@@ -2984,6 +2984,8 @@ DearPyGui::fill_configuration_dict(const mvSubPlotsConfig& inConfig, PyObject* o
 		return;
 
 	PyDict_SetItemString(outDict, "rows", mvPyObject(ToPyInt(inConfig.rows)));
+	PyDict_SetItemString(outDict, "columns", mvPyObject(ToPyInt(inConfig.cols)));
+	// maybe remove this on next minor release?
 	PyDict_SetItemString(outDict, "cols", mvPyObject(ToPyInt(inConfig.cols)));
 	PyDict_SetItemString(outDict, "row_ratios", mvPyObject(ToPyList(inConfig.row_ratios)));
 	PyDict_SetItemString(outDict, "column_ratios", mvPyObject(ToPyList(inConfig.col_ratios)));
@@ -3072,7 +3074,7 @@ DearPyGui::apply_template(const mvPlotLegendConfig& sourceConfig, mvPlotLegendCo
 	dstConfig.outside = sourceConfig.outside;
 }
 
-void 
+void
 DearPyGui::apply_template(const mvBarSeriesConfig& sourceConfig, mvBarSeriesConfig& dstConfig)
 {
 	dstConfig.value = sourceConfig.value;
@@ -3244,7 +3246,7 @@ DearPyGui::apply_template(const mvPlotConfig& sourceConfig, mvPlotConfig& dstCon
 // Old Classes, in the process of removing OOP crap
 //-----------------------------------------------------------------------------
 
-void 
+void
 mvDragPoint::setPyValue(PyObject* value)
 {
 	std::vector<double> temp = ToDoubleVect(value);


Test patch:
diff --git a/testing/simple_tests.py b/testing/simple_tests.py
index f86afd106..7824e107b 100644
--- a/testing/simple_tests.py
+++ b/testing/simple_tests.py
@@ -238,6 +238,20 @@ def test_cfg_delink_callback_in_mvNodeEditor(self):
         self.assertTrue("delink_callback" in cfg2)
         self.assertTrue(cfg2.get("delink_callback", 0) is dl_cb)
 
+    def test_cfg_extensions_in_mvFileExtension(self):
+        with dpg.file_dialog():
+            ext = dpg.add_file_extension("*.*")
+        cfg = dpg.get_item_configuration(ext)
+        self.assertTrue("extension" in cfg)
+
+    def test_cfg_dragdropdata_in_mvDragPayload(self):
+        b = dpg.add_button(parent=self.wndw)
+        payload = dpg.add_drag_payload(parent=b)
+
+        cfg = dpg.get_item_configuration(payload)
+        self.assertTrue("drag_data" in cfg)
+        self.assertTrue("drop_data" in cfg)
+
     def tearDown(self):
         dpg.stop_dearpygui()
         dpg.destroy_context()