diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 69a03ccc..dcfef2ad 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -26,8 +26,6 @@ SET(
${CMAKE_CURRENT_SOURCE_DIR}/translations/Dyt_zh_CN.ts
)
-# 控制是否在构建时自动运行 lupdate 更新 TS 文件。
-# 关闭时仅在 TS 变更后编译生成 QM,避免每次构建都重新创建翻译文件。
option(UPDATE_TRANSLATIONS "Run lupdate to refresh TS files during build" OFF)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
if(UPDATE_TRANSLATIONS)
@@ -205,7 +203,7 @@ endif()
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${ProjectDIR}/bin)
TARGET_LINK_LIBRARIES(${PROJECT_NAME})
-SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:CONSOLE")
+# SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:CONSOLE")
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
diff --git a/src/Dyt.qrc b/src/Dyt.qrc
index 95741fca..d90988dd 100644
--- a/src/Dyt.qrc
+++ b/src/Dyt.qrc
@@ -6,6 +6,7 @@
res/sys_min.png
res/sys_restore.png
res/sys_icon.png
+ res/sys_icon.ico
res/sys_down.png
res/sys_up.png
res/select_file.ico
diff --git a/src/app/Application.cpp b/src/app/Application.cpp
index 12b026e3..bf6563c0 100644
--- a/src/app/Application.cpp
+++ b/src/app/Application.cpp
@@ -18,6 +18,7 @@ Application::Application(int& argc, char** argv, int /*= ApplicationFlags*/)
}
Application::~Application() {
+ Uninit();
}
QString Application::GetWorkSpacePath() {
@@ -30,6 +31,15 @@ QString Application::GetBinPath() {
}
void Application::Init() {
+ // Set application/taskbar icon early so all top-level windows inherit it
+ QIcon appIcon(":/res/sys_icon.ico");
+ if (!appIcon.isNull()) {
+ setWindowIcon(appIcon);
+ } else {
+ // Fallback to PNG if ICO is not embedded
+ setWindowIcon(QIcon(":/res/sys_icon.png"));
+ }
+
Singleton::Create(this);
Singleton::Create(this);
Singleton::Create(this);
@@ -53,8 +63,8 @@ void Application::Uninit() {
timer_.stop();
}
//Singleton::Destory();
- Singleton::Destory();
Singleton::Destory();
+ Singleton::Destory();
Singleton::Destory();
Singleton::Destory();
Singleton::Destory();
diff --git a/src/entities/EntitiesManager.cpp b/src/entities/EntitiesManager.cpp
index 58ca9916..b14cd5db 100644
--- a/src/entities/EntitiesManager.cpp
+++ b/src/entities/EntitiesManager.cpp
@@ -31,6 +31,10 @@ void EntitiesManager::OnDestory() {
}
);
for (auto* entity : entities) {
+ // Detach scene graph nodes before deletion
+ if (auto* root = entity->GetRootComponent()) {
+ root->OnDestroy();
+ }
RemoveEntity(entity);
// Delete entities immediately to release scene graph resources before exit
delete entity;
@@ -139,8 +143,16 @@ bool EntitiesManager::DeleteEntity(Entity* entity) {
return false;
}
+ // Ensure scene graph nodes are detached before deleting the entity
+ if (auto* root = entity->GetRootComponent()) {
+ root->OnDestroy();
+ }
+
RemoveEntity(entity);
- entity->deleteLater();
+ // Immediate deletion to ensure scene graph resources are released
+ // before viewer/context teardown. Using deleteLater() may invoke
+ // QObject cleanup after OSG/GL objects are gone, causing crashes.
+ delete entity;
return true;
}
diff --git a/src/entities/SceneComponent.cpp b/src/entities/SceneComponent.cpp
index 7dcbd70f..39a2caa3 100644
--- a/src/entities/SceneComponent.cpp
+++ b/src/entities/SceneComponent.cpp
@@ -206,13 +206,37 @@ void SceneComponent::RemoveRender() {
if (nullptr == mt_) {
return;
}
- int count = mt_->getNumParents();
- for (int i = 0; i < count; ++i) {
+
+ for (auto child : children_) {
+ child->RemoveRender();
+ }
+
+ // If attached via GeoTransform (osgEarth), remove the GeoTransform from the scene
+#ifndef USE_OCEAN
+ if (geo_.valid()) {
+ for (int i = geo_->getNumParents() - 1; i >= 0; --i) {
+ osg::Group* parent = geo_->getParent(i)->asGroup();
+ if (nullptr != parent) {
+ parent->removeChild(geo_.get());
+ }
+ }
+ // Break the child link to mt_ to avoid lingering references
+ geo_->removeChild(mt_.get());
+ }
+#endif
+
+ for (int i = mt_->getNumParents() - 1; i >= 0; --i) {
osg::Group* parent = mt_->getParent(i)->asGroup();
if (nullptr != parent) {
parent->removeChild(mt_);
}
}
+
+ // Release local references proactively
+#ifndef USE_OCEAN
+ geo_ = nullptr;
+#endif
+ mt_ = nullptr;
}
void SceneComponent::RemoveParent() {
diff --git a/src/main.cpp b/src/main.cpp
index fe1903a2..ea581800 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -12,6 +12,10 @@
#include
#include
#include
+#include
+#include
+#include
+#include
#include
#include
@@ -22,7 +26,6 @@
#include
#include
#include
-// #include
#include "osgqt/GraphicsWindowQt.h"
#include "scene/ui/CompositeWidgetManager.h"
@@ -32,370 +35,48 @@
#define LC "DYT"
#endif
-const unsigned int MASK_2D = 0xF0000000;
-std::string path = "D:\\Project\\DYT\\Source\\bin\\Release\\data\\";
-
-bool scrollWindow(osgWidget::Event& ev) {
- // The first thing we need to do is make sure we have a Frame object...
- osgWidget::Frame* frame = dynamic_cast(ev.getWindow());
-
- if(!frame) return false;
-
- // And now we need to make sure our Frame has a valid internal EmbeddedWindow widget.
- osgWidget::Window::EmbeddedWindow* ew =
- dynamic_cast(frame->getEmbeddedWindow())
- ;
-
- if(!ew) return false;
-
- // Lets get the visible area so that we can use it to make sure our scrolling action
- // is necessary in the first place.
- const osgWidget::Quad& va = ew->getWindow()->getVisibleArea();
-
- // The user wants to scroll up; make sure that the visible area's Y origin isn't already
- // at 0.0f, 0.0f.
- if(ev.getWindowManager()->isMouseScrollingUp() && va[1] != 0.0f)
- ew->getWindow()->addVisibleArea(0, -20)
- ;
-
- else if(va[1] <= (ew->getWindow()->getHeight() - ew->getHeight()))
- ew->getWindow()->addVisibleArea(0, 20)
- ;
-
- // We need to manually call update to make sure the visible area scissoring is done
- // properly.
- frame->update();
-
- return true;
-}
-
-bool changeTheme(osgWidget::Event& ev) {
- std::string theme;
-
- if(ev.key == osgGA::GUIEventAdapter::KEY_Right)
- theme = "osgWidget/theme-1.png"
- ;
-
- else if(ev.key == osgGA::GUIEventAdapter::KEY_Left)
- theme = "osgWidget/theme-2.png"
- ;
-
- else return false;
-
- osgWidget::Frame* frame = dynamic_cast(ev.getWindow());
-
- if(!frame) return false;
-
- // This is just one way to access all our Widgets; we could just as well have used:
- //
- // for(osgWidget::Frame::Iterator i = frame.begin(); i != frame.end() i++) {}
- //
- // ...and it have worked, too.
- for(unsigned int row = 0; row < 3; row++) {
- for(unsigned int col = 0; col < 3; col++) {
- frame->getByRowCol(row, col)->setImage(theme);
- }
- }
-
- return true;
-}
-
-
-osg::Node* createUiExample(osgViewer::View* viewer, osgWidget::WindowManager* wm) {
- if(!wm) return nullptr;
-
- // viewer.setUpViewInWindow(
- // 50,
- // 50,
- // static_cast(wm->getWidth()),
- // static_cast(wm->getHeight())
- // );
-
- osg::Group* group = new osg::Group();
- osg::Camera* camera = wm->createParentOrthoCamera();
-
- group->addChild(camera);
-
- viewer->addEventHandler(new osgWidget::MouseHandler(wm));
- viewer->addEventHandler(new osgWidget::KeyboardHandler(wm));
- viewer->addEventHandler(new osgWidget::ResizeHandler(wm, camera));
- viewer->addEventHandler(new osgWidget::CameraSwitchHandler(wm, camera));
- viewer->addEventHandler(new osgViewer::StatsHandler());
- viewer->addEventHandler(new osgViewer::WindowSizeHandler());
- viewer->addEventHandler(new osgGA::StateSetManipulator(
- viewer->getCamera()->getOrCreateStateSet()
- ));
-
- wm->resizeAllWindows();
- return group;
-}
-
-void creatWidget(osgViewer::View* viewer, osg::Group* root) {
- osgWidget::WindowManager* wm = new osgWidget::WindowManager(
- viewer,
- 1280.0f,
- 1024.0f,
- MASK_2D,
- osgWidget::WindowManager::WM_PICK_DEBUG
- //osgWidget::WindowManager::WM_NO_INVERT_Y
- );
-
- osgWidget::Frame* frame = osgWidget::Frame::createSimpleFrameFromTheme(
- "frame",
- osgDB::readRefImageFile("osgWidget/theme.png"),
- 40.0f,
- 40.0f,
- osgWidget::Frame::FRAME_ALL
- );
- osgWidget::Box* box = new osgWidget::Box("images", osgWidget::Box::VERTICAL);
- osgWidget::Widget* img1 = new osgWidget::Widget("im1", 512.0f, 512.0f);
- osgWidget::Widget* img2 = new osgWidget::Widget("im2", 512.0f, 512.0f);
- osgWidget::Widget* img3 = new osgWidget::Widget("im3", 512.0f, 512.0f);
- osgWidget::Widget* img4 = new osgWidget::Widget("im4", 512.0f, 512.0f);
-
- img1->setImage(path + "osgWidget/scrolled1.jpg", true);
- img2->setImage(path + "osgWidget/scrolled2.jpg", true);
- img3->setImage(path + "osgWidget/scrolled3.jpg", true);
- img4->setImage(path + "osgWidget/scrolled4.jpg", true);
-
- img1->setMinimumSize(10.0f, 10.0f);
- img2->setMinimumSize(10.0f, 10.0f);
- img3->setMinimumSize(10.0f, 10.0f);
- img4->setMinimumSize(10.0f, 10.0f);
-
- box->addWidget(img1);
- box->addWidget(img2);
- box->addWidget(img3);
- box->addWidget(img4);
- box->setEventMask(osgWidget::EVENT_NONE);
-
- //frame->getEmbeddedWindow()->setWindow(box);
- frame->setWindow(box);
- frame->getEmbeddedWindow()->setColor(1.0f, 1.0f, 1.0f, 1.0f);
- frame->resize(300.0f, 300.0f);
- frame->addCallback(new osgWidget::Callback(&scrollWindow, osgWidget::EVENT_MOUSE_SCROLL));
- frame->addCallback(new osgWidget::Callback(&changeTheme, osgWidget::EVENT_KEY_DOWN));
-
- wm->addChild(frame);
-
- osg::Node* ui = createUiExample(viewer, wm);
- root->addChild(ui);
-}
-
-void configureView( osgViewer::View* view )
-{
- // default uniform values:
- // GLUtils::setGlobalDefaults(view->getCamera()->getOrCreateStateSet());
-
- // add some stock OSG handlers:
- view->addEventHandler(new osgViewer::StatsHandler());
- view->addEventHandler(new osgViewer::WindowSizeHandler());
- view->addEventHandler(new osgViewer::ThreadingHandler());
- view->addEventHandler(new osgViewer::LODScaleHandler());
- view->addEventHandler(new osgGA::StateSetManipulator(view->getCamera()->getOrCreateStateSet()));
- view->addEventHandler(new osgViewer::RecordCameraPathHandler());
- view->addEventHandler(new osgViewer::ScreenCaptureHandler());
-}
-
-
-osg::Node* LoadEarth(const std::string& earth, osgViewer::CompositeViewer* viewer) {
- osg::ref_ptr node = osgDB::readNodeFile(earth);
-
- osg::ref_ptr mapNode = osgEarth::MapNode::get(node.get());
- if ( !mapNode.valid() )
- {
- OE_WARN << LC << "Loaded scene graph does not contain a MapNode - aborting" << std::endl;
- return 0L;
- }
-
- // collect the views
- osgViewer::Viewer::Views views;
- if (viewer)
- {
- viewer->getViews(views);
- }
-
- // warn about not having an earth manip
- for (osgViewer::Viewer::Views::iterator view = views.begin(); view != views.end(); ++view)
- {
- osgEarth::Util::EarthManipulator* manip = dynamic_cast((*view)->getCameraManipulator());
- if ( manip == 0L )
- {
- OE_WARN << LC << "Helper used before installing an EarthManipulator" << std::endl;
- }
- }
-
- // a root node to hold everything:
- osg::Group* root = new osg::Group();
-
- root->addChild( node );
-
- // parses common cmdline arguments and apply to the first view:
- // if ( !views.empty() )
- // {
- // parse( mapNode.get(), args, views.front(), root, userContainer );
- //
- // float lodscale;
- // if (args.read("--lodscale", lodscale))
- // {
- // LODScaleGroup* g = new LODScaleGroup();
- // g->setLODScaleFactor(osg::maximum(lodscale, 0.0001f));
- // osgEarth::insertGroup(g, mapNode->getParent(0));
- // OE_NOTICE << "LOD Scale set to: " << lodscale << std::endl;
- // }
- // }
-
- // configures each view with some stock goodies
- for (osgViewer::Viewer::Views::iterator view = views.begin(); view != views.end(); ++view)
- {
- configureView( *view );
- }
-
-#ifdef OSG_GL3_AVAILABLE
- if (viewer)
- {
- viewer->setRealizeOperation(new GL3RealizeOperation());
- }
-#endif
-
- return root;
-}
-
-class ViewerWidget : public QWidget, public osgViewer::CompositeViewer
-{
-public:
- ViewerWidget(QWidget* parent = 0, Qt::WindowFlags f = 0, osgViewer::ViewerBase::ThreadingModel threadingModel=osgViewer::CompositeViewer::SingleThreaded) : QWidget(parent, f)
- {
- setThreadingModel(threadingModel);
-
- // disable the default setting of viewer.done() by pressing Escape.
- setKeyEventSetsDone(0);
-
- std::string earthPath = "D:/Project/DYT/Tool/TritonSample/TritonSample/triton.earth";
- // osg::Node* node = osgDB::readNodeFile();
- osg::Node* node =LoadEarth(earthPath, this);
- if ( !node ) {
- return;
- }
-
-
- // Group to hold all our annotation elements.
- // osg::Group* annoGroup = new osg::Group();
- // osgEarth::MapNode::get(node)->addChild( annoGroup );
-
- QWidget* widget1 = addViewWidget( createGraphicsWindow(0,0,100,100), node);
- // QWidget* widget2 = addViewWidget( createGraphicsWindow(0,0,100,100), osgDB::readRefNodeFile("glider.osgt") );
- // QWidget* widget3 = addViewWidget( createGraphicsWindow(0,0,100,100), osgDB::readRefNodeFile("axes.osgt") );
- // QWidget* widget4 = addViewWidget( createGraphicsWindow(0,0,100,100), osgDB::readRefNodeFile("fountain.osgt") );
- // QWidget* popupWidget = addViewWidget( createGraphicsWindow(900,100,320,240,"Popup window",true), osgDB::readRefNodeFile("dumptruck.osgt") );
- // popupWidget->show();
-
-
-
- QGridLayout* grid = new QGridLayout;
- grid->addWidget( widget1, 0, 0 );
- // grid->addWidget( widget2, 0, 1 );
- // grid->addWidget( widget3, 1, 0 );
- // grid->addWidget( widget4, 1, 1 );
- setLayout( grid );
-
- connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) );
- _timer.start( 10 );
- }
-
- QWidget* addViewWidget( osgQt::GraphicsWindowQt* gw, osg::ref_ptr scene )
- {
- osgViewer::View* view = new osgViewer::View;
- addView( view );
- view->setCameraManipulator( new osgEarth::Util::EarthManipulator() );
- configureView(view);
-
- osg::Camera* camera = view->getCamera();
- camera->setGraphicsContext( gw );
-
- const osg::GraphicsContext::Traits* traits = gw->getTraits();
-
- camera->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) );
- camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
- camera->setProjectionMatrixAsPerspective(30.0f, static_cast(traits->width)/static_cast(traits->height), 1.0f, 10000.0f );
-
- creatWidget(view, scene->asGroup());
-
- osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode(scene);
- if ( !mapNode )
- return gw->getGLWidget();
-
- auto skyDome_ = osgEarth::Util::SkyNode::create(mapNode);
- if (!mapNode) {
- LOG_WARN("eart map node is nullptr");
- return gw->getGLWidget();
- }
- skyDome_->attach(view);
- skyDome_->getSunLight()->setAmbient(osg::Vec4(0.5,0.5,0.5,1.0));
- scene->asGroup()->addChild(skyDome_);
-
- skyDome_->setDateTime(osgEarth::DateTime(2024, 12, 24, 3));
-
- view->setSceneData( scene );
- view->addEventHandler( new osgViewer::StatsHandler );
- // view->setCameraManipulator( new osgGA::MultiTouchTrackballManipulator );
- gw->setTouchEventsEnabled( true );
- return gw->getGLWidget();
- }
-
- osgQt::GraphicsWindowQt* createGraphicsWindow( int x, int y, int w, int h, const std::string& name="", bool windowDecoration=false )
- {
- osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
- osg::ref_ptr traits = new osg::GraphicsContext::Traits;
- traits->windowName = name;
- traits->windowDecoration = windowDecoration;
- traits->x = x;
- traits->y = y;
- traits->width = w;
- traits->height = h;
- traits->doubleBuffer = true;
- traits->alpha = ds->getMinimumNumAlphaBits();
- traits->stencil = ds->getMinimumNumStencilBits();
- traits->sampleBuffers = ds->getMultiSamples();
- traits->samples = ds->getNumMultiSamples();
-
- return new osgQt::GraphicsWindowQt(traits.get());
- }
-
- virtual void paintEvent( QPaintEvent* /*event*/ )
- { frame(); }
-
-protected:
-
- QTimer _timer;
-};
-
int main(int argc, char* argv[]) {
SpdLogger logger("logs/log.txt", 5);
- //
- Application::setAttribute(Qt::AA_EnableHighDpiScaling);
- ////
+
+ Application::setAttribute(Qt::AA_EnableHighDpiScaling);
+
Application app(argc, argv);
- app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
- // InstallCrashHandler();
- //
- RecourceHelper::ChangeSkin("default");
-
- MainFrame mainWindow;
- mainWindow.showMaximized();
- //
+ app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
+ InstallCrashHandler();
+
+ // Single-instance guard to avoid multiple launches from repeated clicks
+ const QString lockPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/Dyt_app.lock";
+ QLockFile lock(lockPath);
+ lock.setStaleLockTime(0);
+ if (!lock.tryLock(1)) {
+ // Another instance is starting or running; exit quietly
+ return 0;
+ }
+
+ // Show splash screen immediately to improve perceived startup speed
+ QString splashPath = RecourceHelper::Get().GetBasePath() + "/resources/splash.png";
+ QPixmap splashPixmap = QPixmap(splashPath).scaled(640, 480, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ QSplashScreen splash(splashPixmap);
+ splash.showMessage(("正在启动..."), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
+ splash.show();
+ app.processEvents();
+
+ RecourceHelper::ChangeSkin("default");
+ splash.showMessage(("正在加载界面皮肤..."), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
+ app.processEvents();
+
+ splash.showMessage(("正在加载数据..."), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
+ app.processEvents();
+
+
+ MainFrame mainWindow;
+ splash.showMessage(("正在创建主窗口..."), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
+ mainWindow.showMaximized();
+ splash.finish(&mainWindow);
+
int ret = app.exec();
- app.Uninit();
+ // app.Uninit();
+ Sleep(200);
return ret;
- //osg::ArgumentParser arguments(&argc, argv);
-
- //QMainWindow* mainWindow = new QMainWindow;
- //OsgWidget* viewWidget = new OsgWidget(nullptr, Qt::Widget);
- //mainWindow->setCentralWidget(viewWidget);
- //// ViewerWidget* viewWidget = new ViewerWidget(nullptr, Qt::Widget, threadingModel);
- ////viewWidget->setGeometry( 100, 100, 800, 600 );
- //viewWidget->Initialize();
- //mainWindow->show();
- //return app.exec();
}
diff --git a/src/scene/MeshManager.cpp b/src/scene/MeshManager.cpp
index 2dd4d561..2d970d40 100644
--- a/src/scene/MeshManager.cpp
+++ b/src/scene/MeshManager.cpp
@@ -41,16 +41,29 @@ MeshManager::~MeshManager(void) {
}
void MeshManager::OnDestory() {
-
+ // Defer destruction of cached nodes to process exit to avoid
+ // shutdown-order crashes inside OSG ref_ptr/Drawable destructors.
+ // We intentionally leak the cache at exit; acceptable for application shutdown.
+ LOG_INFO("MeshManager::OnDestory - deferring cache release to process exit");
+ static NodeMap* leakedCache = nullptr;
+ if (!leakedCache) leakedCache = new NodeMap();
+ leakedCache->swap(nodes_);
}
osg::MatrixTransform* MeshManager::ReadNode(const std::string& file) {
LOG_INFO("load node:{}", file);
const auto iter = nodes_.find(file);
if (nodes_.end() != iter) {
- osg::MatrixTransform* mt = new osg::MatrixTransform;
- mt->addChild(iter->second);
- return mt;
+ // Guard against previously cleared cache entries
+ osg::ref_ptr cached = iter->second;
+ if (cached.valid()) {
+ osg::MatrixTransform* mt = new osg::MatrixTransform;
+ mt->addChild(cached.get());
+ return mt;
+ } else {
+ // Remove invalid entry and fall through to reload
+ nodes_.erase(iter);
+ }
}
osg::Node* node = osgDB::readNodeFile(file);
diff --git a/src/scene/OEScene.cpp b/src/scene/OEScene.cpp
index 6b04f3fc..b2a4d40d 100644
--- a/src/scene/OEScene.cpp
+++ b/src/scene/OEScene.cpp
@@ -59,6 +59,12 @@ void OEScene::DetachView(osgViewer::View* view) {
// Remove sky dome from this group if present; SkyNode has no detach in osgEarth 2.8
if (skyDome_.valid()) {
+ // Proactively disable lighting/effects to reduce interactions with the View
+ skyDome_->setLighting(osg::StateAttribute::OFF);
+ skyDome_->setSunVisible(false);
+ skyDome_->setMoonVisible(false);
+ skyDome_->setStarsVisible(false);
+ skyDome_->setAtmosphereVisible(false);
removeChild(skyDome_.get());
skyDome_ = nullptr;
}
diff --git a/src/scene/OEScene.h b/src/scene/OEScene.h
index f3ba50c6..3f5524f8 100644
--- a/src/scene/OEScene.h
+++ b/src/scene/OEScene.h
@@ -30,6 +30,10 @@ public:
}
OESceneUI* GetOrCreateSceneUI();
+ // Read-only accessor to avoid creating UI during teardown
+ OESceneUI* GetSceneUI() const {
+ return sceneUI_.get();
+ }
osgEarth::Util::EarthManipulator* GetManipulater() const {
return earthManipulator_.get();
}
diff --git a/src/viewer/OsgWidget.cpp b/src/viewer/OsgWidget.cpp
index 1200f786..e7509dc8 100644
--- a/src/viewer/OsgWidget.cpp
+++ b/src/viewer/OsgWidget.cpp
@@ -110,7 +110,13 @@ void OsgWidget::Initialize() {
void OsgWidget::Uninitialize() {
LOG_INFO("OsgWidget::Uninitialize");
if (nullptr != viewUI_) {
- viewUI_->RemoveUI(activeScene_->GetOrCreateSceneUI());
+ // Avoid creating UI during teardown; only remove if it exists
+ if (activeScene_.valid()) {
+ OESceneUI* sceneUI = activeScene_->GetSceneUI();
+ if (sceneUI) {
+ viewUI_->RemoveUI(sceneUI);
+ }
+ }
viewUI_ = nullptr;
}
if (nullptr != activeScene_) {
diff --git a/src/workspace/WorkSpaceManager.cpp b/src/workspace/WorkSpaceManager.cpp
index 4362aae4..f890ad9c 100644
--- a/src/workspace/WorkSpaceManager.cpp
+++ b/src/workspace/WorkSpaceManager.cpp
@@ -25,11 +25,15 @@ void WorkSpaceManager::OnDestory() {
WorkSpace* ws = kv.second;
if (ws) {
ws->Unlaod();
- ws->deleteLater();
+ // Delete workspaces immediately to prevent deferred Qt cleanup
+ // after viewer/context teardown, which can lead to crashes.
+ delete ws;
}
}
workSpaces_.clear();
+ // Clear dangling pointers
+ current_ = nullptr;
scene_ = nullptr;
}