Skip to content

Commit faada24

Browse files
yashhzdamilcarlucas
authored andcommitted
fix(ui): center all windows on the active screen on Windows
Add BaseWindow.center_window_on_screen() calls to all 8 main windows that were missing screen centering after setting their geometry. On Windows with multi-monitor setups, these windows would appear at arbitrary positions instead of centering on the active screen (the monitor where the mouse pointer is located). Windows now centered: - Connection Selection - Software Update Dialog - Flight Controller Info - Project Opener (Directory Selection) - Parameter Editor - Component Editor - Template Overview - Project Creator The existing center_window_on_screen() method uses the screeninfo library to detect the monitor containing the mouse pointer and centers the window on that monitor, with proper bounds clamping. On Windows, a withdrawn tk.Tk() root can report winfo_width() > 1, causing ProgressWindow to use parent-relative centering instead of screen centering. This placed the "Initializing Flight Controller" progress bar in the top-left corner instead of screen center. Added winfo_viewable() check alongside the existing winfo_width() check to reliably detect withdrawn parent windows on all platforms. Added test to verify screen centering is used when parent is withdrawn. - Fix center_window_on_screen() to use winfo_width()/winfo_height() (actual rendered size) instead of winfo_reqwidth()/winfo_reqheight() (natural content size). The previous approach placed the top-left corner at screen center because winfo_reqwidth() returned a value much smaller than the geometry-set size. Falls back to reqwidth for unmapped windows (which return width=1). - Extract duplicated centering condition in ProgressWindow into _center_progress_window() helper to keep logic in one place. - Rename test method for clarity. Fixes #1346 Signed-off-by: Yash Goel <yashhzd@users.noreply.github.com>
1 parent a40418c commit faada24

11 files changed

Lines changed: 65 additions & 14 deletions

ardupilot_methodic_configurator/frontend_tkinter_base_window.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,14 @@ def center_window_on_screen(window: Union[tk.Toplevel, tk.Tk]) -> None:
331331
window.update_idletasks()
332332

333333
# Get the window dimensions
334-
window_width = window.winfo_reqwidth()
335-
window_height = window.winfo_reqheight()
334+
# Use winfo_width/height (actual rendered size) like center_window() does.
335+
# Fall back to winfo_reqwidth/height for windows not yet mapped (returns 1).
336+
window_width = window.winfo_width()
337+
window_height = window.winfo_height()
338+
if window_width <= 1:
339+
window_width = window.winfo_reqwidth()
340+
if window_height <= 1:
341+
window_height = window.winfo_reqheight()
336342

337343
# Get pointer position to determine which monitor it's on
338344
pointer_x = window.winfo_pointerx()

ardupilot_methodic_configurator/frontend_tkinter_component_editor_base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ def _setup_window(self) -> None:
153153
_("Amilcar Lucas's - ArduPilot methodic configurator ") + self.version + _(" - Vehicle Component Editor")
154154
)
155155
self.root.geometry(f"{WINDOW_WIDTH_PIX}x600")
156+
BaseWindow.center_window_on_screen(self.root)
156157
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
157158

158159
def _setup_styles(self) -> None:

ardupilot_methodic_configurator/frontend_tkinter_connection_selection.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ def __init__(
317317
super().__init__()
318318
self.root.title(_("AMC {version} - Flight controller connection").format(version=__version__)) # Set the window title
319319
self.root.geometry("520x462") # Set the window size
320+
BaseWindow.center_window_on_screen(self.root)
320321
self.default_baudrate = default_baudrate
321322

322323
# Explain why we are here

ardupilot_methodic_configurator/frontend_tkinter_flightcontroller_info.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def __init__(self, flight_controller: FlightController, vehicle_dir: Path) -> No
7676
super().__init__()
7777
self.root.title(_("AMC {version} - Flight Controller Info").format(version=__version__)) # Set the window title
7878
self.root.geometry("500x420") # Adjust the window size as needed
79+
BaseWindow.center_window_on_screen(self.root)
7980

8081
self.presenter = FlightControllerInfoPresenter(flight_controller, vehicle_dir)
8182

ardupilot_methodic_configurator/frontend_tkinter_parameter_editor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ def __init__(
254254
_("Amilcar Lucas's - ArduPilot methodic configurator ") + __version__ + _(" - Parameter file editor and uploader")
255255
)
256256
self.root.geometry("990x630") # Set the window width and height
257+
BaseWindow.center_window_on_screen(self.root)
257258

258259
# Bind the close_connection_and_quit function to the window close event
259260
self.root.protocol("WM_DELETE_WINDOW", self.close_connection_and_quit)

ardupilot_methodic_configurator/frontend_tkinter_progress_window.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,27 @@ def __init__( # pylint: disable=too-many-arguments, too-many-positional-argumen
6262
if not self.only_show_when_update_progress_called:
6363
self.progress_window.deiconify() # needs to be done before centering, but it does flicker :(
6464

65-
# Center the progress window on screen or on parent
66-
# Use screen centering if parent is a minimal temp root (like in FlightControllerConnectionProgress)
67-
if isinstance(self.parent, tk.Tk) and self.parent.winfo_width() <= 1:
68-
BaseWindow.center_window_on_screen(self.progress_window)
69-
else:
70-
BaseWindow.center_window(self.progress_window, self.parent)
65+
self._center_progress_window()
7166

7267
if not self.only_show_when_update_progress_called:
7368
# Show the window now that it's properly positioned
7469
self.progress_window.lift()
7570
self._shown = True
7671
self.progress_bar.update()
7772

73+
def _center_progress_window(self) -> None:
74+
"""
75+
Center the progress window on screen or relative to its parent.
76+
77+
Uses screen centering when the parent is not viewable (e.g., a withdrawn temp root
78+
in FlightControllerConnectionProgress). On Windows, withdrawn windows can report
79+
winfo_width() > 1, so winfo_viewable() is checked as well.
80+
"""
81+
if isinstance(self.parent, tk.Tk) and (self.parent.winfo_width() <= 1 or not self.parent.winfo_viewable()):
82+
BaseWindow.center_window_on_screen(self.progress_window)
83+
else:
84+
BaseWindow.center_window(self.progress_window, self.parent)
85+
7886
def update_progress_bar_300_pct(self, percent: int) -> None:
7987
self.message = _("Please be patient, {:.1f}% of {}% complete")
8088
self.update_progress_bar(int(percent / 3), max_value=100)
@@ -99,12 +107,7 @@ def update_progress_bar(self, current_value: int, max_value: int) -> None:
99107

100108
if self.only_show_when_update_progress_called and not self._shown:
101109
self.progress_window.deiconify()
102-
# Re-center the window when it's first shown
103-
# Use screen centering if parent is a minimal temp root
104-
if isinstance(self.parent, tk.Tk) and self.parent.winfo_width() <= 1:
105-
BaseWindow.center_window_on_screen(self.progress_window)
106-
else:
107-
BaseWindow.center_window(self.progress_window, self.parent)
110+
self._center_progress_window()
108111
self.progress_window.lift()
109112
self._shown = True
110113
elif not self.only_show_when_update_progress_called:

ardupilot_methodic_configurator/frontend_tkinter_project_creator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def template_selection_callback(_widget: "DirectorySelectionWidgets") -> str:
136136
# Set dynamic window size based on number of settings
137137
window_height = 250 + (len(settings_metadata) * 23)
138138
self.root.geometry(f"800x{window_height}") # Set the window size
139+
BaseWindow.center_window_on_screen(self.root)
139140

140141
for setting_name, metadata in settings_metadata.items():
141142
checkbox = ttk.Checkbutton(

ardupilot_methodic_configurator/frontend_tkinter_project_opener.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def __init__(self, project_manager: VehicleProjectManager) -> None:
5656
)
5757

5858
self.root.geometry("600x450") # Set the window size
59+
BaseWindow.center_window_on_screen(self.root)
5960

6061
# Explain why we are here
6162
introduction_text = self.project_manager.get_introduction_message()

ardupilot_methodic_configurator/frontend_tkinter_software_update.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def __init__(self, version_info: str, download_callback: Optional[Callable[[], b
2828
)
2929
)
3030
self.root.geometry("700x600")
31+
BaseWindow.center_window_on_screen(self.root)
3132
self.download_callback = download_callback
3233
self.root.protocol("WM_DELETE_WINDOW", self.on_cancel)
3334

ardupilot_methodic_configurator/frontend_tkinter_template_overview.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def _configure_window(self) -> None:
116116
scaled_width = int(1200 * self.dpi_scaling_factor)
117117
scaled_height = int(600 * self.dpi_scaling_factor)
118118
self.root.geometry(f"{scaled_width}x{scaled_height}")
119+
BaseWindow.center_window_on_screen(self.root)
119120

120121
def _initialize_ui_components(self) -> None:
121122
"""Initialize UI components with proper scaling."""

0 commit comments

Comments
 (0)