Skip to content

Commit ce587cb

Browse files
authored
Merge branch 'master' into cy/gentl-cti-lock-fix
2 parents 0d5e1dc + 5cdaa09 commit ce587cb

13 files changed

Lines changed: 890 additions & 151 deletions

.github/workflows/testing-ci.yml

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ jobs:
6161
libxcb-cursor0
6262
6363
- name: Run tests (exclude hardware) with coverage via tox
64+
shell: bash -eo pipefail {0}
6465
run: |
65-
tox -q | tee tox-output.log
66+
tox -q 2>&1 | tee tox-output.log
6667
6768
6869
- name: Append Coverage Summary to Job
@@ -85,3 +86,43 @@ jobs:
8586
token: ${{ secrets.CODECOV_TOKEN }}
8687
files: ./.coverage.py312.xml
8788
fail_ci_if_error: false
89+
90+
dlclive-compat:
91+
name: DLCLive Compatibility • ${{ matrix.label }}
92+
runs-on: ubuntu-latest
93+
strategy:
94+
fail-fast: false
95+
matrix:
96+
include:
97+
- label: pypi-1.1
98+
tox_env: dlclive-pypi
99+
- label: github-main
100+
tox_env: dlclive-github
101+
102+
steps:
103+
- uses: actions/checkout@v6
104+
105+
- uses: actions/setup-python@v6
106+
with:
107+
python-version: '3.12'
108+
cache: 'pip'
109+
110+
- name: Install Qt/OpenGL runtime deps (Ubuntu)
111+
run: |
112+
sudo apt-get update
113+
sudo apt-get install -y \
114+
libegl1 \
115+
libgl1 \
116+
libopengl0 \
117+
libxkbcommon-x11-0 \
118+
libxcb-cursor0
119+
120+
- name: Install tox
121+
run: |
122+
python -m pip install -U pip wheel
123+
python -m pip install -U tox tox-gh-actions
124+
125+
- name: Run DLCLive compatibility tests via tox
126+
shell: bash -eo pipefail {0}
127+
run: |
128+
tox -e ${{ matrix.tox_env }} -q

dlclivegui/gui/main_window.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,17 @@ def __init__(self, config: ApplicationSettings | None = None):
196196

197197
# Validate cameras from loaded config (deferred to allow window to show first)
198198
# NOTE IMPORTANT (tests/CI): This is scheduled via a QTimer and may fire during pytest-qt teardown.
199-
QTimer.singleShot(100, self._validate_configured_cameras)
199+
# NOTE @C-Achard 2026-03-02: Handling this in closeEvent should help
200+
self._camera_validation_timer = QTimer(self)
201+
self._camera_validation_timer.setSingleShot(True)
202+
self._camera_validation_timer.timeout.connect(self._validate_configured_cameras)
203+
self._camera_validation_timer.start(100)
200204
# If validation triggers a modal QMessageBox (warning/error) while the parent window is closing,
201205
# it can cause errors with unpredictable timing (heap corruption / access violations).
202206
#
203207
# Mitigations for tests/CI:
204208
# - Disable this timer by monkeypatching _validate_configured_cameras in GUI tests
205209
# - OR monkeypatch/override _show_warning/_show_error to no-op in GUI tests (easiest)
206-
# - OR use a cancellable QTimer attribute and stop() it in closeEvent
207210

208211
def resizeEvent(self, event):
209212
super().resizeEvent(event)
@@ -1373,7 +1376,7 @@ def _on_multi_frame_ready(self, frame_data: MultiFrameData) -> None:
13731376
dlc_cam_id = selected_id
13741377
else:
13751378
dlc_cam_id = available_ids[0] if available_ids else ""
1376-
if dlc_cam_id is not None:
1379+
if dlc_cam_id:
13771380
self._inference_camera_id = dlc_cam_id
13781381
self._set_dlc_combo_to_id(dlc_cam_id)
13791382
self.statusBar().showMessage(
@@ -2021,6 +2024,8 @@ def closeEvent(self, event: QCloseEvent) -> None: # pragma: no cover - GUI beha
20212024
if self.multi_camera_controller.is_running():
20222025
self.multi_camera_controller.stop(wait=True)
20232026

2027+
if hasattr(self, "_camera_validation_timer") and self._camera_validation_timer.isActive():
2028+
self._camera_validation_timer.stop()
20242029
# Stop all multi-camera recorders
20252030
self._rec_manager.stop_all()
20262031

dlclivegui/gui/recording_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def write_frame(self, cam_id: str, frame: np.ndarray, timestamp: float | None =
139139
if not rec or not rec.is_running:
140140
return
141141
try:
142-
rec.write(frame, timestamp=timestamp or time.time())
142+
rec.write(frame, timestamp=timestamp if timestamp is not None else time.time())
143143
except Exception as exc:
144144
log.warning("Failed to write frame for %s: %s", cam_id, exc)
145145
try:

0 commit comments

Comments
 (0)