Fix QThread lifecycle crash: 'Destroyed while thread is still running'
- Add _cleanup_worker() helper: calls wait() then deleteLater() before dropping the Python reference. This ensures the C++ thread is fully cleaned up before the QObject is destroyed. - Replace all 'self.worker_thread = None' with _cleanup_worker() - Add running thread guards to all handlers: prevent starting a new WorkerThread while one is already running - Add timeout to wait() calls (3000ms) to avoid hanging on close - WorkerThread.stop() now calls self.wait() to ensure clean shutdown
This commit is contained in:
parent
4c41cba428
commit
56a88cf420
39
src/main.py
39
src/main.py
@ -201,6 +201,7 @@ class WorkerThread(QThread):
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
@ -222,6 +223,18 @@ class MainWindow(QMainWindow):
|
|||||||
self._scan_library()
|
self._scan_library()
|
||||||
self._on_refresh_devices_clicked()
|
self._on_refresh_devices_clicked()
|
||||||
|
|
||||||
|
def _cleanup_worker(self):
|
||||||
|
"""Safely clean up the worker thread after it finishes.
|
||||||
|
|
||||||
|
Calls wait() to ensure the C++ thread is fully cleaned up before
|
||||||
|
dropping the Python reference. This prevents the Qt crash:
|
||||||
|
'QThread: Destroyed while thread is still running'
|
||||||
|
"""
|
||||||
|
if self.worker_thread:
|
||||||
|
self.worker_thread.wait(3000)
|
||||||
|
self.worker_thread.deleteLater()
|
||||||
|
self.worker_thread = None
|
||||||
|
|
||||||
def _setup_ui(self):
|
def _setup_ui(self):
|
||||||
main_widget = QWidget()
|
main_widget = QWidget()
|
||||||
main_layout = QVBoxLayout(main_widget)
|
main_layout = QVBoxLayout(main_widget)
|
||||||
@ -632,6 +645,10 @@ class MainWindow(QMainWindow):
|
|||||||
QMessageBox.warning(self, "Error", "Please enter a valid URL")
|
QMessageBox.warning(self, "Error", "Please enter a valid URL")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.worker_thread and self.worker_thread.isRunning():
|
||||||
|
QMessageBox.information(self, "Info", "Please wait for the current task to finish")
|
||||||
|
return
|
||||||
|
|
||||||
self.download_button.setEnabled(False)
|
self.download_button.setEnabled(False)
|
||||||
self.download_status.setText("Downloading...")
|
self.download_status.setText("Downloading...")
|
||||||
self.download_progress.setValue(0)
|
self.download_progress.setValue(0)
|
||||||
@ -669,7 +686,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.downloaded_tracks = []
|
self.downloaded_tracks = []
|
||||||
self.track_list.clear()
|
self.track_list.clear()
|
||||||
|
|
||||||
self.worker_thread = None
|
self._cleanup_worker()
|
||||||
|
|
||||||
def _on_library_convert_clicked(self):
|
def _on_library_convert_clicked(self):
|
||||||
source_files = self._get_selected_source_files()
|
source_files = self._get_selected_source_files()
|
||||||
@ -682,6 +699,10 @@ class MainWindow(QMainWindow):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.worker_thread and self.worker_thread.isRunning():
|
||||||
|
QMessageBox.information(self, "Info", "Please wait for the current task to finish")
|
||||||
|
return
|
||||||
|
|
||||||
output_dir = self.output_dir_input.text().strip()
|
output_dir = self.output_dir_input.text().strip()
|
||||||
if not output_dir:
|
if not output_dir:
|
||||||
QMessageBox.warning(self, "Error", "Please set an output directory")
|
QMessageBox.warning(self, "Error", "Please set an output directory")
|
||||||
@ -718,7 +739,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.library_status.setText(f"Error: {message}")
|
self.library_status.setText(f"Error: {message}")
|
||||||
QMessageBox.warning(self, "Conversion Error", message)
|
QMessageBox.warning(self, "Conversion Error", message)
|
||||||
|
|
||||||
self.worker_thread = None
|
self._cleanup_worker()
|
||||||
|
|
||||||
def _on_library_transfer_clicked(self):
|
def _on_library_transfer_clicked(self):
|
||||||
selected_tracks = self._get_selected_ready_tracks()
|
selected_tracks = self._get_selected_ready_tracks()
|
||||||
@ -730,6 +751,10 @@ class MainWindow(QMainWindow):
|
|||||||
QMessageBox.warning(self, "Error", "No iPod device mounted. Go to iPod tab and mount first.")
|
QMessageBox.warning(self, "Error", "No iPod device mounted. Go to iPod tab and mount first.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.worker_thread and self.worker_thread.isRunning():
|
||||||
|
QMessageBox.information(self, "Info", "Please wait for the current task to finish")
|
||||||
|
return
|
||||||
|
|
||||||
self.library_transfer_btn.setEnabled(False)
|
self.library_transfer_btn.setEnabled(False)
|
||||||
self.library_status.setText("Transferring...")
|
self.library_status.setText("Transferring...")
|
||||||
self.library_progress.setValue(0)
|
self.library_progress.setValue(0)
|
||||||
@ -759,9 +784,12 @@ class MainWindow(QMainWindow):
|
|||||||
self.library_status.setText(f"Error: {message}")
|
self.library_status.setText(f"Error: {message}")
|
||||||
QMessageBox.warning(self, "Transfer Error", message)
|
QMessageBox.warning(self, "Transfer Error", message)
|
||||||
|
|
||||||
self.worker_thread = None
|
self._cleanup_worker()
|
||||||
|
|
||||||
def _on_refresh_devices_clicked(self):
|
def _on_refresh_devices_clicked(self):
|
||||||
|
if self.worker_thread and self.worker_thread.isRunning():
|
||||||
|
return
|
||||||
|
|
||||||
self.device_combo.clear()
|
self.device_combo.clear()
|
||||||
self.device_info_label.setText("Detecting devices...")
|
self.device_info_label.setText("Detecting devices...")
|
||||||
|
|
||||||
@ -807,7 +835,7 @@ class MainWindow(QMainWindow):
|
|||||||
self._set_device_not_found()
|
self._set_device_not_found()
|
||||||
self.statusBar().showMessage(message)
|
self.statusBar().showMessage(message)
|
||||||
|
|
||||||
self.worker_thread = None
|
self._cleanup_worker()
|
||||||
|
|
||||||
def _set_device_mounted(self, device: dict, mount_point: str):
|
def _set_device_mounted(self, device: dict, mount_point: str):
|
||||||
self.current_mount_point = mount_point
|
self.current_mount_point = mount_point
|
||||||
@ -1010,7 +1038,8 @@ class MainWindow(QMainWindow):
|
|||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
if self.worker_thread and self.worker_thread.isRunning():
|
if self.worker_thread and self.worker_thread.isRunning():
|
||||||
self.worker_thread.stop()
|
self.worker_thread.stop()
|
||||||
self.worker_thread.wait()
|
self.worker_thread.wait(5000)
|
||||||
|
self.worker_thread.deleteLater()
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user