From 55c947bfb70b3d910e462b9f975f81d68dd2fdc8 Mon Sep 17 00:00:00 2001 From: Lorenz Neureuter Date: Sat, 4 Jul 2026 09:02:23 -0400 Subject: [PATCH 1/3] Measure elapsed time render and erase 400 shapes --- tests/test_app.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/test_app.py b/tests/test_app.py index dcc3d82c..817f79b8 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,5 +1,6 @@ from path import Path import os, sys, asyncio +from time import perf_counter if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) @@ -70,6 +71,19 @@ code_nested_top = """import test_nested_bottom """ +code_many_shapes = """from cadquery.func import * + +num = 20 + +for i in range(1, num + 1, 1): + for j in range(1, num + 1, 1): + globals()[f"b_{i}_{j}"] = box(1, 1, 1).move( + x=1.1 * i, + y=1.1 * j, + z=0, + ) +""" + code_nested_bottom = """a=1 """ @@ -272,6 +286,34 @@ def test_render(main): assert obj_tree_comp.CQ.childCount() == 3 +def test_many_shapes_render_and_erase_time(main_clean): + qtbot, win = main_clean + + editor = win.components["editor"] + debugger = win.components["debugger"] + object_tree = win.components["object_tree"] + viewer = win.components["viewer"] + + debugger.preferences["Reload CQ"] = False + object_tree.preferences["Clear all before each run"] = True + viewer.preferences["Fit automatically"] = True + editor.set_text(code_many_shapes) + + start = perf_counter() + debugger._actions["Run"][0].triggered.emit() + render_elapsed = perf_counter() - start + + assert object_tree.CQ.childCount() == 20 * 20 + + start = perf_counter() + object_tree.removeObjects() + erase_elapsed = perf_counter() - start + + assert object_tree.CQ.childCount() == 0 + assert render_elapsed < 4, f"400-shape render took {render_elapsed:.3f}s" + assert erase_elapsed < 4, f"400-shape erase took {erase_elapsed:.3f}s" + + def test_export(main, mocker): qtbot, win = main From cb46daf15fc67ca53d20a53688a45412582e635b Mon Sep 17 00:00:00 2001 From: Lorenz Neureuter Date: Sat, 4 Jul 2026 09:45:19 -0400 Subject: [PATCH 2/3] Batch AIS display and erase viewer updates --- .github/workflows/test_linux.yml | 2 +- cq_editor/widgets/viewer.py | 8 ++++++-- tests/test_app.py | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index c8b904cc..833e6572 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -43,4 +43,4 @@ jobs: QT_QPA_PLATFORM: "xcb" uses: coactions/setup-xvfb@v1.0.1 with: - run: pytest + run: pytest -s diff --git a/cq_editor/widgets/viewer.py b/cq_editor/widgets/viewer.py index 6fc6110e..05cdad12 100644 --- a/cq_editor/widgets/viewer.py +++ b/cq_editor/widgets/viewer.py @@ -306,12 +306,14 @@ def display(self, ais): def display_many(self, ais_list, fit=None): context = self._get_context() for ais in ais_list: - context.Display(ais, True) + context.Display(ais, False) if self.preferences["Fit automatically"] and fit is None: self.fit() elif fit: self.fit() + elif ais_list: + context.UpdateCurrentViewer() @pyqtSlot(QTreeWidgetItem, int) def update_item(self, item, col): @@ -334,7 +336,9 @@ def remove_items(self, ais_items): ctx = self._get_context() for ais in ais_items: - ctx.Erase(ais, True) + ctx.Erase(ais, False) + if ais_items: + ctx.UpdateCurrentViewer() @pyqtSlot() def redraw(self): diff --git a/tests/test_app.py b/tests/test_app.py index 817f79b8..21cff216 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -312,6 +312,8 @@ def test_many_shapes_render_and_erase_time(main_clean): assert object_tree.CQ.childCount() == 0 assert render_elapsed < 4, f"400-shape render took {render_elapsed:.3f}s" assert erase_elapsed < 4, f"400-shape erase took {erase_elapsed:.3f}s" + print(f"400-shape render took {render_elapsed:.3f}s") + print(f"400-shape erase took {erase_elapsed:.3f}s") def test_export(main, mocker): From f60ff79e4c0003321791ca69d68da1488d4ce48e Mon Sep 17 00:00:00 2001 From: Lorenz Neureuter Date: Sat, 4 Jul 2026 09:56:52 -0400 Subject: [PATCH 3/3] Update coarse limits on render and erase time --- .github/workflows/test_linux.yml | 2 +- tests/test_app.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 833e6572..c8b904cc 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -43,4 +43,4 @@ jobs: QT_QPA_PLATFORM: "xcb" uses: coactions/setup-xvfb@v1.0.1 with: - run: pytest -s + run: pytest diff --git a/tests/test_app.py b/tests/test_app.py index 21cff216..76deda64 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -310,10 +310,8 @@ def test_many_shapes_render_and_erase_time(main_clean): erase_elapsed = perf_counter() - start assert object_tree.CQ.childCount() == 0 - assert render_elapsed < 4, f"400-shape render took {render_elapsed:.3f}s" - assert erase_elapsed < 4, f"400-shape erase took {erase_elapsed:.3f}s" - print(f"400-shape render took {render_elapsed:.3f}s") - print(f"400-shape erase took {erase_elapsed:.3f}s") + assert render_elapsed < 2, f"400-shape render took {render_elapsed:.3f}s" + assert erase_elapsed < 2, f"400-shape erase took {erase_elapsed:.3f}s" def test_export(main, mocker):