From de31f271430f156310f96e6f0a90643915f34a0a Mon Sep 17 00:00:00 2001 From: Vladimir Ein Date: Tue, 28 May 2024 01:35:23 +0500 Subject: [PATCH 1/2] fix (mvTable): Allow creation of synced tables. --- src/mvTables.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mvTables.cpp b/src/mvTables.cpp index b56df39c0..9b952b05e 100644 --- a/src/mvTables.cpp +++ b/src/mvTables.cpp @@ -164,8 +164,6 @@ void mvTable::draw(ImDrawList* drawlist, float x, float y) apply_local_theming(this); { - ScopedID id(uuid); - auto row_renderer = [&](mvAppItem* row, mvAppItem* prev_visible_row=nullptr) { //TableNextRow() ends the previous row, if any, and determines background color for it. From 8a565dc0d89c50d04e1a4fa0c31c7cdc2bf32c12 Mon Sep 17 00:00:00 2001 From: Vladimir Ein Date: Wed, 24 Dec 2025 21:46:59 +0500 Subject: [PATCH 2/2] feat (tables): Added mvSyncedTables for easier creation of synced tables. --- dearpygui/_dearpygui.pyi | 5 ++++ dearpygui/_dearpygui_RTD.py | 44 ++++++++++++++++++++++++++++ dearpygui/dearpygui.py | 52 +++++++++++++++++++++++++++++++++ src/mvAppItem.cpp | 20 +++++++++++++ src/mvAppItem.h | 1 + src/mvAppItemTypes.inc | 1 + src/mvTables.cpp | 57 ++++++++++++++++++++++++++++++++++++- src/mvTables.h | 9 +++++- 8 files changed, 187 insertions(+), 2 deletions(-) diff --git a/dearpygui/_dearpygui.pyi b/dearpygui/_dearpygui.pyi index 3ffbd5356..040a498a4 100644 --- a/dearpygui/_dearpygui.pyi +++ b/dearpygui/_dearpygui.pyi @@ -547,6 +547,10 @@ def add_subplots(rows : int, columns : int, *, label: str ='', user_data: Any =' """Adds a collection of plots.""" ... +def add_synced_tables(*, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', parent: Union[int, str] ='', before: Union[int, str] ='', show: bool ='', filter_key: str ='') -> Union[int, str]: + """Links all tables that are immediate children of this container so that they share their state (mostly column sizes). Other children are rendered as is. This is an experimental feature, use with caution.""" + ... + def add_tab(*, label: str ='', user_data: Any ='', use_internal_label: bool ='', tag: Union[int, str] ='', indent: int ='', parent: Union[int, str] ='', before: Union[int, str] ='', payload_type: str ='', drop_callback: Callable ='', show: bool ='', filter_key: str ='', delay_search: bool ='', tracked: bool ='', track_offset: float ='', closable: bool ='', no_tooltip: bool ='', order_mode: int ='') -> Union[int, str]: """Adds a tab to a tab bar.""" ... @@ -1788,6 +1792,7 @@ mvNodeAttribute=0 mvTable=0 mvTableColumn=0 mvTableRow=0 +mvSyncedTables=0 mvDrawLine=0 mvDrawArrow=0 mvDrawTriangle=0 diff --git a/dearpygui/_dearpygui_RTD.py b/dearpygui/_dearpygui_RTD.py index abb68f6a9..0f1c0f5c6 100644 --- a/dearpygui/_dearpygui_RTD.py +++ b/dearpygui/_dearpygui_RTD.py @@ -2338,6 +2338,30 @@ def subplots(rows, columns, **kwargs): finally: internal_dpg.pop_container_stack() +@contextmanager +def synced_tables(**kwargs): + """ Links all tables that are immediate children of this container so that they share their state (mostly column sizes). Other children are rendered as is. This is an experimental feature, use with caution. + + Args: + label (str, optional): Overrides 'name' as label. + user_data (Any, optional): User data for callbacks + use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). + tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. + parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + before (Union[int, str], optional): This item will be displayed before the specified item in the parent. + show (bool, optional): Attempt to render widget. + filter_key (str, optional): Used by filter widget. + id (Union[int, str], optional): (deprecated) + Yields: + Union[int, str] + """ + try: + widget = internal_dpg.add_synced_tables(**kwargs) + internal_dpg.push_container_stack(widget) + yield widget + finally: + internal_dpg.pop_container_stack() + @contextmanager def tab(**kwargs): """ Adds a tab to a tab bar. @@ -6343,6 +6367,25 @@ def add_subplots(rows, columns, **kwargs): return internal_dpg.add_subplots(rows, columns, **kwargs) +def add_synced_tables(**kwargs): + """ Links all tables that are immediate children of this container so that they share their state (mostly column sizes). Other children are rendered as is. This is an experimental feature, use with caution. + + Args: + label (str, optional): Overrides 'name' as label. + user_data (Any, optional): User data for callbacks + use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). + tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. + parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + before (Union[int, str], optional): This item will be displayed before the specified item in the parent. + show (bool, optional): Attempt to render widget. + filter_key (str, optional): Used by filter widget. + id (Union[int, str], optional): (deprecated) + Returns: + Union[int, str] + """ + + return internal_dpg.add_synced_tables(**kwargs) + def add_tab(**kwargs): """ Adds a tab to a tab bar. @@ -9359,6 +9402,7 @@ def unstage(item): mvTable=internal_dpg.mvTable mvTableColumn=internal_dpg.mvTableColumn mvTableRow=internal_dpg.mvTableRow +mvSyncedTables=internal_dpg.mvSyncedTables mvDrawLine=internal_dpg.mvDrawLine mvDrawArrow=internal_dpg.mvDrawArrow mvDrawTriangle=internal_dpg.mvDrawTriangle diff --git a/dearpygui/dearpygui.py b/dearpygui/dearpygui.py index aa4c2d846..331c130ab 100644 --- a/dearpygui/dearpygui.py +++ b/dearpygui/dearpygui.py @@ -2468,6 +2468,34 @@ def subplots(rows : int, columns : int, *, label: str =None, user_data: Any =Non finally: internal_dpg.pop_container_stack() +@contextmanager +def synced_tables(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, before: Union[int, str] =0, show: bool =True, filter_key: str ='', **kwargs) -> Union[int, str]: + """ Links all tables that are immediate children of this container so that they share their state (mostly column sizes). Other children are rendered as is. This is an experimental feature, use with caution. + + Args: + label (str, optional): Overrides 'name' as label. + user_data (Any, optional): User data for callbacks + use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). + tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. + parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + before (Union[int, str], optional): This item will be displayed before the specified item in the parent. + show (bool, optional): Attempt to render widget. + filter_key (str, optional): Used by filter widget. + id (Union[int, str], optional): (deprecated) + Yields: + Union[int, str] + """ + try: + + if 'id' in kwargs.keys(): + warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) + tag=kwargs['id'] + widget = internal_dpg.add_synced_tables(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, before=before, show=show, filter_key=filter_key, **kwargs) + internal_dpg.push_container_stack(widget) + yield widget + finally: + internal_dpg.pop_container_stack() + @contextmanager def tab(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', drop_callback: Callable =None, show: bool =True, filter_key: str ='', delay_search: bool =False, tracked: bool =False, track_offset: float =0.5, closable: bool =False, no_tooltip: bool =False, order_mode: int =0, **kwargs) -> Union[int, str]: """ Adds a tab to a tab bar. @@ -7140,6 +7168,29 @@ def add_subplots(rows : int, columns : int, *, label: str =None, user_data: Any return internal_dpg.add_subplots(rows, columns, label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, width=width, height=height, indent=indent, parent=parent, before=before, callback=callback, show=show, pos=pos, filter_key=filter_key, delay_search=delay_search, tracked=tracked, track_offset=track_offset, row_ratios=row_ratios, column_ratios=column_ratios, no_title=no_title, no_menus=no_menus, no_resize=no_resize, no_align=no_align, share_series=share_series, link_rows=link_rows, link_columns=link_columns, link_all_x=link_all_x, link_all_y=link_all_y, column_major=column_major, **kwargs) +def add_synced_tables(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, parent: Union[int, str] =0, before: Union[int, str] =0, show: bool =True, filter_key: str ='', **kwargs) -> Union[int, str]: + """ Links all tables that are immediate children of this container so that they share their state (mostly column sizes). Other children are rendered as is. This is an experimental feature, use with caution. + + Args: + label (str, optional): Overrides 'name' as label. + user_data (Any, optional): User data for callbacks + use_internal_label (bool, optional): Use generated internal label instead of user specified (appends ### uuid). + tag (Union[int, str], optional): Unique id used to programmatically refer to the item.If label is unused this will be the label. + parent (Union[int, str], optional): Parent to add this item to. (runtime adding) + before (Union[int, str], optional): This item will be displayed before the specified item in the parent. + show (bool, optional): Attempt to render widget. + filter_key (str, optional): Used by filter widget. + id (Union[int, str], optional): (deprecated) + Returns: + Union[int, str] + """ + + if 'id' in kwargs.keys(): + warnings.warn('id keyword renamed to tag', DeprecationWarning, 2) + tag=kwargs['id'] + + return internal_dpg.add_synced_tables(label=label, user_data=user_data, use_internal_label=use_internal_label, tag=tag, parent=parent, before=before, show=show, filter_key=filter_key, **kwargs) + def add_tab(*, label: str =None, user_data: Any =None, use_internal_label: bool =True, tag: Union[int, str] =0, indent: int =-1, parent: Union[int, str] =0, before: Union[int, str] =0, payload_type: str ='$$DPG_PAYLOAD', drop_callback: Callable =None, show: bool =True, filter_key: str ='', delay_search: bool =False, tracked: bool =False, track_offset: float =0.5, closable: bool =False, no_tooltip: bool =False, order_mode: int =0, **kwargs) -> Union[int, str]: """ Adds a tab to a tab bar. @@ -10342,6 +10393,7 @@ def unstage(item : Union[int, str], **kwargs) -> None: mvTable=internal_dpg.mvTable mvTableColumn=internal_dpg.mvTableColumn mvTableRow=internal_dpg.mvTableRow +mvSyncedTables=internal_dpg.mvSyncedTables mvDrawLine=internal_dpg.mvDrawLine mvDrawArrow=internal_dpg.mvDrawArrow mvDrawTriangle=internal_dpg.mvDrawTriangle diff --git a/src/mvAppItem.cpp b/src/mvAppItem.cpp index 4815f8797..9e6ee8551 100644 --- a/src/mvAppItem.cpp +++ b/src/mvAppItem.cpp @@ -566,6 +566,7 @@ CanItemTypeBeVisible(mvAppItemType type) case mvAppItemType::mvTable: case mvAppItemType::mvTableColumn: case mvAppItemType::mvTableRow: + case mvAppItemType::mvSyncedTables: case mvAppItemType::mvButton: return true; default: return false; } @@ -1008,6 +1009,7 @@ DearPyGui::GetEntityDesciptionFlags(mvAppItemType type) case mvAppItemType::mvTable: case mvAppItemType::mvTableCell: case mvAppItemType::mvTableRow: + case mvAppItemType::mvSyncedTables: case mvAppItemType::mv2dHistogramSeries: case mvAppItemType::mvAreaSeries: case mvAppItemType::mvBarSeries: @@ -3350,6 +3352,24 @@ DearPyGui::GetEntityParser(mvAppItemType type) setup.createContextManager = true; break; } + case mvAppItemType::mvSyncedTables: + { + AddCommonArgs(args, (CommonParserArgs)( + MV_PARSER_ARG_ID | + MV_PARSER_ARG_PARENT | + MV_PARSER_ARG_BEFORE | + MV_PARSER_ARG_FILTER | + MV_PARSER_ARG_SHOW) + ); + + setup.about = + "Links all tables that are immediate children of this container so that they share " + "their state (mostly column sizes). Other children are rendered as is. This is " + "an experimental feature, use with caution."; + setup.category = { "Tables", "Containers", "Widgets" }; + setup.createContextManager = true; + break; + } case mvAppItemType::mvDrawLine: { AddCommonArgs(args, (CommonParserArgs)( diff --git a/src/mvAppItem.h b/src/mvAppItem.h index aa125b789..938ff0fa5 100644 --- a/src/mvAppItem.h +++ b/src/mvAppItem.h @@ -338,6 +338,7 @@ GetEntityCommand(mvAppItemType type) case mvAppItemType::mvTable: return "add_table"; case mvAppItemType::mvTableColumn: return "add_table_column"; case mvAppItemType::mvTableRow: return "add_table_row"; + case mvAppItemType::mvSyncedTables: return "add_synced_tables"; case mvAppItemType::mvDrawLine: return "draw_line"; case mvAppItemType::mvDrawArrow: return "draw_arrow"; case mvAppItemType::mvDrawTriangle: return "draw_triangle"; diff --git a/src/mvAppItemTypes.inc b/src/mvAppItemTypes.inc index 5ecffb450..88c48f07e 100644 --- a/src/mvAppItemTypes.inc +++ b/src/mvAppItemTypes.inc @@ -51,6 +51,7 @@ X( mvTable ) \ X( mvTableColumn ) \ X( mvTableRow ) \ + X( mvSyncedTables ) \ X( mvDrawLine ) \ X( mvDrawArrow ) \ X( mvDrawTriangle ) \ diff --git a/src/mvTables.cpp b/src/mvTables.cpp index 9b952b05e..20d8e30af 100644 --- a/src/mvTables.cpp +++ b/src/mvTables.cpp @@ -226,7 +226,14 @@ void mvTable::draw(ImDrawList* drawlist, float x, float y) handleImmediateScroll(); - if (ImGui::BeginTable(info.internalLabel.c_str(), _columns, _flags, + const char* table_id = info.internalLabel.c_str(); + // If this table is a part of a synced group, use that group's internalLabel + // instead. In most cases, it will contain UUID so that all groups are distinct + // but tables within the group are synced. + if (info.parentPtr && info.parentPtr->type == mvAppItemType::mvSyncedTables) + table_id = info.parentPtr->info.internalLabel.c_str(); + + if (ImGui::BeginTable(table_id, _columns, _flags, ImVec2((float)config.width, (float)config.height), (float)_inner_width)) { state.lastFrameUpdate = GContext->frame; @@ -636,3 +643,51 @@ void mvTable::setPyValue(PyObject* value) _imguiFilter.InputBuf[i] = 0; _imguiFilter.Build(); } + +void mvSyncedTables::draw(ImDrawList* drawlist, float x, float y) +{ + //----------------------------------------------------------------------------- + // pre draw + //----------------------------------------------------------------------------- + + // show/hide + if (!config.show) + return; + + // push font if a font object is attached + if (font) + { + ImFont* fontptr = static_cast(font.get())->getFontPtr(); + ImGui::PushFont(fontptr); + } + + // themes + apply_local_theming(this); + + //----------------------------------------------------------------------------- + // draw + //----------------------------------------------------------------------------- + { + ScopedID id(uuid); + + for (auto& child : childslots[1]) + { + child->draw(drawlist, ImGui::GetCursorPosX(), ImGui::GetCursorPosY()); + } + UpdateAppItemState(state); + } + + //----------------------------------------------------------------------------- + // post draw + //----------------------------------------------------------------------------- + + // handle popping themes + cleanup_local_theming(this); + + // pop font off stack + if (font) + ImGui::PopFont(); + + if (handlerRegistry) + handlerRegistry->checkEvents(&state); +} diff --git a/src/mvTables.h b/src/mvTables.h index 6bf137060..8d5d3330a 100644 --- a/src/mvTables.h +++ b/src/mvTables.h @@ -92,4 +92,11 @@ class mvTable : public mvAppItem int direction; }; -}; \ No newline at end of file +}; + +class mvSyncedTables : public mvAppItem +{ +public: + explicit mvSyncedTables(mvUUID uuid) : mvAppItem(uuid) {} + void draw(ImDrawList* drawlist, float x, float y) override; +};