Базовая реализация интеграции гистограмм в ChargeExchangeMC (входящая в состав последней версии Geant4) была основана исключительно на вводе пользователем команд в командной строке Geant4. Но недавно я подумал, а почему бы не создать специальное меню, в котором будут перечислены все имеющиеся гистограммы в естественном древовидном порядке. При клике на имени гистограммы в меню она будет отображаться в канвасе: это намного проще и естественнее, чем набирать вручную команду листинга гистограмм, затем выбирать из полученного списка нужную гистограмму и вводить новую команду для ее отображения.
На следующей картинке изображена сессия Qt Geant4 c одной из гистограмм, изображающей пучок, прошедший сквозь входной мониторный счетчик. Гистограмма была открыта из меню Histo, находящемся в верхней левой части окна справа от меню Viewer. Все картинки ниже кликабельны.
А это меню Histo в развернутом виде:
Одна из поддиректорий развернута, внутри нее находятся десять гистограмм. Для удобства восприятия перед именами двух- и трехмерных гистограмм стоят соответствующие значки 2: и 3:.
Самой главной технической сложностью здесь было построение подменю. Интерфейсы GUI Geant4 их просто не поддерживают. Один из вариантов реализации поддержки подменю - субклассинг G4UIQt - мне не понравился, так как из пушки по воробьям. В итоге я решил добавить новую функцию AddSubmenu() прямо в класс CexmcHistoManager. Вот реализация функции BuildMenuTree(), которая рекурсивно строит меню гистограмм, и функции AddSubmenu():
void CexmcHistoManager::BuildMenuTree( G4UIQt * session, const G4String & menu, TList * ls ) { TIter objs( ls ); TObject * obj( NULL ); while ( ( obj = ( TObject * )objs() ) ) { G4String name( obj->GetName() ); G4String title( obj->GetTitle() ); if ( obj->IsFolder() ) { AddSubmenu( session, menu, name, title ); BuildMenuTree( session, name, ( ( TDirectory * )obj )->GetList() ); } else { G4String options( name ); do { if ( obj->InheritsFrom( TH3::Class() ) && ! drawOptions3D.empty() ) { title = G4String( "3: " ) + title; options += G4String( " " ) + drawOptions3D; break; } if ( obj->InheritsFrom( TH2::Class() ) && ! drawOptions2D.empty() ) { title = G4String( "2: " ) + title; options += G4String( " " ) + drawOptions2D; break; } if ( obj->InheritsFrom( TH1::Class() ) && ! drawOptions1D.empty() ) { options += G4String( " " ) + drawOptions1D; break; } } while ( false ); G4String cmd( CexmcMessenger::histoDirName + "draw " + options ); session->AddButton( menu, title.c_str(), cmd ); } } } void CexmcHistoManager::AddSubmenu( G4UIQt * session, const G4String & parent, const G4String & name, const G4String & label ) { QMenu * menu( new QMenu( label.c_str() ) ); QMenu * parentMenu( ( QMenu * )session->GetInteractor( parent ) ); parentMenu->addMenu( menu ); session->AddInteractor( name, ( G4Interactor )menu ); }Предполагается, что BuildMenuTree() будет вызвана в момент инициализации менеджера гистограмм примерно таким образом:
G4UIQt * qtSession( dynamic_cast< G4UIQt * >( session ) ); if ( qtSession ) { qtSession->AddMenu( histoMenuHandle, histoMenuLabel ); BuildMenuTree( qtSession, histoMenuHandle, gDirectory->GetList() ); }Здесь session - указатель на объект типа G4UIsession, histoMenuHandle и histoMenuLabel - строки, первая задается произвольно - это дескриптор нового главного меню в строке меню (menu bar), вторая - имя этого меню, в нашем случае она равна Histo.
Этого кода еще нет в составе ChargeExchangeMC, но я думаю добавить его к следующему релизу Geant4.
Спасибо, очень полезно!
ОтветитьУдалить