Skip to content

Reactive GUI#1572

Merged
amilcarlucas merged 2 commits into
masterfrom
reactive_param_editor
Apr 28, 2026
Merged

Reactive GUI#1572
amilcarlucas merged 2 commits into
masterfrom
reactive_param_editor

Conversation

@amilcarlucas
Copy link
Copy Markdown
Collaborator

Description

yield to Tk event loop during table/component rendering

Checklist

  • Run pre-commit checks locally
  • Verified by a human programmer
  • All commits are signed off (use git commit --signoff)
  • Code follows our coding standards
  • Documentation updated if needed
  • No breaking changes or properly documented

Testing

Describe how you tested these changes:

  • Unit tests pass
  • Integration tests pass
  • Manual testing performed
  • Tested on flight controller hardware

- Yield every 20 rendered rows in the parameter editor to keep
  the UI responsive during large table builds
- Yield every 5 components in the component editor for the same reason
Also guard scroll handling when the canvas has no bbox yet.
Copilot AI review requested due to automatic review settings April 28, 2026 21:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Improve Tkinter UI responsiveness and robustness by yielding during long renders and preventing scroll-related crashes when the canvas has no items.

Changes:

  • Prevent ScrollFrame.on_mouse_wheel() from crashing when canvas.bbox("all") is None.
  • Yield periodically during parameter table rendering (update_idletasks() every 20 rows).
  • Yield periodically during component frame population (update_idletasks() every 5 components) and add tests for the new behaviors.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
ardupilot_methodic_configurator/frontend_tkinter_scroll_frame.py Guard against bbox() returning None during mouse wheel scrolling.
ardupilot_methodic_configurator/frontend_tkinter_parameter_editor_table.py Periodically call update_idletasks() while rendering many rows.
ardupilot_methodic_configurator/frontend_tkinter_component_editor_base.py Periodically call update_idletasks() while creating many component widgets.
tests/test_frontend_tkinter_scroll_frame.py Add regression test for scrolling before any canvas items exist.
tests/test_frontend_tkinter_parameter_editor_table.py Add test asserting periodic yielding during rendering.
tests/test_frontend_tkinter_component_editor_base.py Add test asserting periodic yielding during component population.

canvas_height = self.canvas.winfo_height()
rows_height = self.canvas.bbox("all")[3]
bbox = self.canvas.bbox("all")
rows_height = bbox[3] if bbox is not None else 0
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

canvas.bbox('all') returns (x1, y1, x2, y2), so bbox[3] is a coordinate, not the content height. This can mis-detect overflow when y1 is not 0. Compute the height as (bbox[3] - bbox[1]) (and keep the None guard) to ensure scrolling is enabled/disabled correctly.

Suggested change
rows_height = bbox[3] if bbox is not None else 0
rows_height = (bbox[3] - bbox[1]) if bbox is not None else 0

Copilot uses AI. Check for mistakes.
should_try_to_display_bitmask_parameter_editor_usage = True
self._grid_column_widgets(row_widgets, i, show_upload_column)
if i % 20 == 0:
self.view_port.update_idletasks() # yield to the event loop periodically to keep the UI responsive
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update_idletasks() only processes geometry redraw/idle tasks; it typically does not process pending input events, so it may not achieve the stated goal of yielding to the Tk event loop during long renders. If the intent is to keep the UI responsive to user actions, consider chunking the render via after(...) (preferred to avoid re-entrancy) or using update() with care (risk: re-entrant event handling while state is mid-update).

Suggested change
self.view_port.update_idletasks() # yield to the event loop periodically to keep the UI responsive
self.view_port.update() # process pending UI events periodically so the interface remains responsive during long renders

Copilot uses AI. Check for mistakes.
Comment on lines +353 to +356
for i, (key, value) in enumerate(components.items(), 1):
self.add_widget(self.scroll_frame.view_port, key, value, [])
if i % 5 == 0: # yield to the event loop periodically to keep the UI responsive
self.scroll_frame.view_port.update_idletasks()
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling update_idletasks() every 5 components can become a noticeable overhead for large component sets. Consider using a larger batch size and/or throttling based on elapsed time (e.g., yield every N ms) so performance scales better while still maintaining responsiveness.

Copilot uses AI. Check for mistakes.
}

with (
patch.object(parameter_editor_table, "_create_column_widgets", return_value=[MagicMock() for _ in range(7)]),
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch returns the same list instance (and the same widget mocks) for every _create_column_widgets() call, which can make the test brittle if the production code mutates the returned list or expects distinct widget instances per row. Prefer using a side_effect that returns a fresh list of new mocks each call.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
12598 11872 94% 89% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: 3d90a51 by action🐍

@github-actions
Copy link
Copy Markdown
Contributor

Test Results

     4 files  ± 0       4 suites  ±0   41m 43s ⏱️ +23s
 3 641 tests + 3   3 639 ✅ + 3   2 💤 ±0  0 ❌ ±0 
14 356 runs  +12  14 333 ✅ +13  23 💤  - 1  0 ❌ ±0 

Results for commit 3d90a51. ± Comparison against base commit f25a76a.

@amilcarlucas amilcarlucas merged commit 0a2d63b into master Apr 28, 2026
33 of 35 checks passed
@amilcarlucas amilcarlucas deleted the reactive_param_editor branch April 28, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants