Skip to content

Commit 90ab247

Browse files
Copilotfsmosca
andcommitted
Make review mode navigation fully non-blocking
Replace blocking stop_review_analysis() on navigation press with non-blocking search.stop() signal. The join+restart is deferred to the debounce handler which fires 300ms after the last button press, by which time the old thread has already exited. Also skip stale analysis messages from the old position during debounce to prevent incorrect UI updates. Agent-Logs-Url: https://github.com/fsmosca/Python-Easy-Chess-GUI/sessions/0e57255b-5a2d-4189-9dc6-ce5b07d5f43f Co-authored-by: fsmosca <22366935+fsmosca@users.noreply.github.com>
1 parent 60a647e commit 90ab247

1 file changed

Lines changed: 48 additions & 39 deletions

File tree

python_easy_chess_gui.py

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2931,41 +2931,46 @@ def refresh_review_analysis(self, window):
29312931
return
29322932
self.start_review_analysis(window)
29332933

2934-
def poll_review_analysis(self, window):
2935-
"""Consume engine messages for Review mode analysis."""
2936-
updated = False
2937-
while True:
2938-
try:
2939-
msg = self.review_queue.get_nowait()
2940-
except queue.Empty:
2941-
break
2942-
except Exception:
2943-
logging.exception('Failed to read Review mode analysis queue.')
2944-
break
2945-
2946-
msg_str = str(msg)
2947-
if 'multipv_info' in msg_str:
2948-
try:
2949-
line_no, info_line = msg_str.split(' | ', 1)
2950-
line_number = int(line_no.strip())
2951-
if not 1 <= line_number <= REVIEW_ANALYSIS_MULTIPV_LINES:
2952-
raise ValueError('Invalid MultiPV line number')
2953-
line_index = line_number - 1
2954-
info_line = info_line.rsplit(' multipv_info', 1)[0]
2955-
self.review_analysis_lines[line_index] = \
2956-
self.shorten_review_analysis_line(info_line)
2957-
updated = True
2958-
except Exception:
2959-
logging.exception('Failed to parse Review mode analysis info.')
2960-
elif 'bestmove' in msg_str:
2961-
if self.review_analysis_search is not None:
2962-
self.review_analysis_search.join()
2963-
self.review_analysis_engine = \
2964-
self.review_analysis_search.get_engine()
2965-
self.review_analysis_search = None
2966-
if self.review_analysis_enabled:
2967-
self.review_analysis_status = \
2968-
'Analysis ready - {}'.format(self.analysis_id_name)
2934+
def poll_review_analysis(self, window):
2935+
"""Consume engine messages for Review mode analysis."""
2936+
updated = False
2937+
is_debouncing = bool(self.review_nav_last_time)
2938+
while True:
2939+
try:
2940+
msg = self.review_queue.get_nowait()
2941+
except queue.Empty:
2942+
break
2943+
except Exception:
2944+
logging.exception('Failed to read Review mode analysis queue.')
2945+
break
2946+
2947+
msg_str = str(msg)
2948+
if 'multipv_info' in msg_str:
2949+
# Skip stale analysis info from the old position while
2950+
# waiting for the debounce to restart analysis.
2951+
if is_debouncing:
2952+
continue
2953+
try:
2954+
line_no, info_line = msg_str.split(' | ', 1)
2955+
line_number = int(line_no.strip())
2956+
if not 1 <= line_number <= REVIEW_ANALYSIS_MULTIPV_LINES:
2957+
raise ValueError('Invalid MultiPV line number')
2958+
line_index = line_number - 1
2959+
info_line = info_line.rsplit(' multipv_info', 1)[0]
2960+
self.review_analysis_lines[line_index] = \
2961+
self.shorten_review_analysis_line(info_line)
2962+
updated = True
2963+
except Exception:
2964+
logging.exception('Failed to parse Review mode analysis info.')
2965+
elif 'bestmove' in msg_str:
2966+
if self.review_analysis_search is not None:
2967+
self.review_analysis_search.join()
2968+
self.review_analysis_engine = \
2969+
self.review_analysis_search.get_engine()
2970+
self.review_analysis_search = None
2971+
if self.review_analysis_enabled and not is_debouncing:
2972+
self.review_analysis_status = \
2973+
'Analysis ready - {}'.format(self.analysis_id_name)
29692974
updated = True
29702975

29712976
if updated:
@@ -3113,10 +3118,10 @@ def start_review_mode(self, window):
31133118
# poll_review_analysis() called earlier in the loop.
31143119
if button == sg.TIMEOUT_KEY:
31153120
# Restart analysis after debounce delay following navigation.
3116-
if (self.review_nav_last_time
3121+
nav_time = self.review_nav_last_time
3122+
if (nav_time
31173123
and self.review_analysis_enabled
3118-
and self.review_analysis_search is None
3119-
and time.time() - self.review_nav_last_time
3124+
and time.time() - nav_time
31203125
>= REVIEW_NAV_DEBOUNCE_SEC):
31213126
self.review_nav_last_time = 0
31223127
self.start_review_analysis(review_window)
@@ -3213,7 +3218,11 @@ def start_review_mode(self, window):
32133218
if position_changed:
32143219
self.update_review_window(review_window)
32153220
if self.review_analysis_enabled:
3216-
self.stop_review_analysis()
3221+
# Signal the analysis thread to stop without blocking.
3222+
# The actual join and restart happen in the debounce
3223+
# handler after the user stops pressing buttons.
3224+
if self.review_analysis_search is not None:
3225+
self.review_analysis_search.stop()
32173226
self.review_nav_last_time = time.time()
32183227
self.review_analysis_lines = [''] * REVIEW_ANALYSIS_MULTIPV_LINES
32193228
self.review_analysis_status = 'Waiting...'

0 commit comments

Comments
 (0)