|
58 | 58 | import pyqtconsole.highlighter as hl |
59 | 59 |
|
60 | 60 |
|
61 | | - |
62 | 61 | class SetRobotModelDialog(QtWidgets.QDialog): |
63 | 62 | def __init__(self, meshcat_provider, parent=None, dataset_loaded=False): |
64 | 63 | # call QMainWindow constructor |
@@ -599,22 +598,53 @@ def update_index(self): |
599 | 598 | ) |
600 | 599 |
|
601 | 600 | def closeEvent(self, event): |
602 | | - # close the window |
603 | | - self.pyconsole.close() |
604 | | - for video_item in self.video_items: |
605 | | - del video_item.media_player |
| 601 | + # ensure update callbacks are not triggered while tearing down the UI |
| 602 | + if self.signal_provider is not None: |
| 603 | + try: |
| 604 | + self.signal_provider.update_index_signal.disconnect(self.update_index) |
| 605 | + except (TypeError, RuntimeError): |
| 606 | + # ignore if already disconnected |
| 607 | + pass |
| 608 | + |
| 609 | + # stop timers/animations before widgets disappear |
| 610 | + for plot_item in self.plot_items: |
| 611 | + plot_item.canvas.quit_animation() |
606 | 612 |
|
607 | | - self.meshcat_provider.state = PeriodicThreadState.closed |
608 | | - self.meshcat_provider.wait() |
| 613 | + # stop the embedded Python console (it owns a QThread) |
| 614 | + self.pyconsole.close() |
609 | 615 |
|
| 616 | + # gracefully stop worker threads before deleting widgets they use |
610 | 617 | if self.signal_provider is not None: |
611 | 618 | self.signal_provider.state = PeriodicThreadState.closed |
612 | 619 | self.signal_provider.wait() |
| 620 | + self.signal_provider = None |
613 | 621 |
|
614 | | - event.accept() |
| 622 | + # Stop the meshcat_provider if exists |
| 623 | + if self.meshcat_provider is not None: |
| 624 | + self.meshcat_provider.state = PeriodicThreadState.closed |
| 625 | + self.meshcat_provider.wait() |
| 626 | + |
| 627 | + # release multimedia resources explicitly to avoid late callbacks |
| 628 | + # while closing |
| 629 | + for video_item in self.video_items: |
| 630 | + media_player = getattr(video_item, "media_player", None) |
| 631 | + if media_player is not None: |
| 632 | + if video_item.media_loaded: |
| 633 | + media_player.stop() |
| 634 | + try: |
| 635 | + media_player.setVideoOutput(None) |
| 636 | + except Exception: |
| 637 | + pass |
| 638 | + media_player.deleteLater() |
| 639 | + video_item.deleteLater() |
| 640 | + self.video_items.clear() |
| 641 | + |
| 642 | + # Disable realtime connection |
615 | 643 | if self.realtime_connection_enabled: |
616 | 644 | self.realtime_connection_enabled = False |
617 | 645 |
|
| 646 | + event.accept() |
| 647 | + |
618 | 648 | def __populate_variable_tree_widget(self, obj, parent) -> QTreeWidgetItem: |
619 | 649 | if not isinstance(obj, dict): |
620 | 650 | return parent |
|
0 commit comments