简介
lqLidarCoreManager类是LidarView的基本控制类,其基类是QObject。功能由:初始化并启动python解析器、创建渲染视图、显示隐藏距离网格、camera(视角)设置、保存数据(调用对应的保存类)、获取paraview服务指针、获取主窗口、恢复默认设置。‘
该类是lqLidarViewManager的基类。lqLidarViewManager中只实现了部分python启动的功能,然后调用lqLidarCoreManager中的pythonStartup进行完整初始化。
lqLidarViewManager类在vvMainWindow构造函数中创建,并传入vvMainWindow的this指针,由vvMainWindow进行内存管理。在vvMainWindow类中,多次调用了lqLidarViewManager单例模式进行LidarView的基本控制。
头文件
头文件比较简单,根据功能的不同,声明了不同类型的函数种函。具体函数在源文件代码中进行注释。
/**
* @class lqLidarCoreManager
* @brief Base Class for LidarView-based app singleton manager.
*
*/
class LQAPPLICATIONCOMPONENTS_EXPORT lqLidarCoreManager : public QObject
{
Q_OBJECT
public:
lqLidarCoreManager(QObject* parent = nullptr);
virtual ~lqLidarCoreManager();
// Return the singleton instance, Derived class should return the same and cast it.
static lqLidarCoreManager* instance();
// Python Shell related
void schedulePythonStartup();
void setPythonShell(pqPythonShell* pythonShell);
pqPythonShell* getPythonShell();
void runPython(const QString& statements);
void forceShowShell();
// Main render view Creation
void createMainRenderView();
// Convenience methods
static pqServer* getActiveServer();
static QWidget* getMainWindow();
// WIP Those are common to all ParaView-based Apps, it may change in the future
static void saveFramesToPCAP(vtkSMSourceProxy* proxy,
int startFrame,
int endFrame,
const QString& filename);
static void saveFramesToLAS(vtkLidarReader* reader,
vtkPolyData* position,
int startFrame,
int endFrame,
const QString& filename,
int positionMode);
public Q_SLOTS:
// Perform delayed python shell startup
virtual void pythonStartup();
void onEnableCrashAnalysis(bool crashAnalysisEnabled);
void onCloseAllData();
void onResetDefaultSettings();
void onMeasurementGrid(bool gridVisible);
void onResetCameraLidar();
void onResetCameraToForwardView();
void onResetCenterToLidarCenter();
private:
Q_DISABLE_COPY(lqLidarCoreManager)
static QPointer<lqLidarCoreManager> Instance;
class pqInternal;
pqInternal* Internal;
pqPythonShell* pythonShell;
};
#endif // LQLIDARCOREMANAGER_H
源文件
instance
单例模式调用接口,Instance指针会在lqLidarCoreManager构造函数中进行赋值。并通过Instance指针是否为nullptr进行单例模式判断,当创建多个lqLidarCoreManager时会触发Q_ASSERT断言,退出程序。
QPointer<lqLidarCoreManager> lqLidarCoreManager::Instance = nullptr; // 单例模式
//-----------------------------------------------------------------------------
lqLidarCoreManager* lqLidarCoreManager::instance() // 单例模式
{
return lqLidarCoreManager::Instance;
}
构造函数
在构造函数中,进行单例模式判断。
创建lqSensorListWidget类,该类时LidarView的一个重要部分,用于控制显示隐藏。
lqLidarCoreManager::lqLidarCoreManager(QObject* parent)
: QObject(parent)
{
// Only 1 lqLidarCoreManager instance can be created.
// 确保单例模式,如果创建两个以上的该类实例,会退出程序。
Q_ASSERT(lqLidarCoreManager::Instance == nullptr);
lqLidarCoreManager::Instance = this;
this->Internal = new pqInternal;
// Create lqSensorListWidget, a critical component of LidarView core.
// App can choose wether or not to show it or not, and how to present it (e.g QWidgetDock)
new lqSensorListWidget();
}
析构函数
主要用于单例模式的实例,当析构该类时,将单例模式的指针置为nullptr。
lqLidarCoreManager::~lqLidarCoreManager()
{
if (lqLidarCoreManager::Instance == this)
{ // 只进行置空,有QObject进行内存的析构
lqLidarCoreManager::Instance = nullptr;
} // WIP Paraview has similar code but 'else' case should not happend at all
delete this->Internal; // 该类为空类,因为在构造函数内进行创建,因此需要进行析构
}
schedulePythonStartup
定时启动进行python初始化。使用了QTimer的singleShot,定时器定时为0,即当调用schedulePythonStartup时会直接进行python的初始化。
void lqLidarCoreManager::schedulePythonStartup()
{
// QTimer::singleShot:0毫秒后调用槽函数pythonStartup。
QTimer::singleShot(0, this, SLOT(pythonStartup()));
}
setPythonShell
设置python shell
void lqLidarCoreManager::setPythonShell(pqPythonShell* shell)
{
// pqPythonShell:paraview的python执行器
// pqPythonShell的基类为QWidget
this->pythonShell = shell;
}
runPython
运行python代码。
void lqLidarCoreManager::runPython(const QString& statements)
{
// 执行python代码
if (this->pythonShell)
{
this->pythonShell->executeScript(statements);
}
else
{
qCritical() << "LidarView python shell has not been set, cannot run:\n" << statements;
}
}
forceShowShell
显示python解析器界面。
void lqLidarCoreManager::forceShowShell()
{ // 显示运行python的界面
if (this->pythonShell)
{
this->pythonShell->show();
this->pythonShell->raise();
this->pythonShell->activateWindow();
}
}
createMainRenderView
创建渲染视图。使用工厂模式进行创建视图,然后将视图设置到激活窗口中。视图的颜色设置成黑色。
// 创建渲染视图
void lqLidarCoreManager::createMainRenderView()
{
vtkSMSessionProxyManager* pxm =
vtkSMProxyManager::GetProxyManager()->GetActiveSessionProxyManager();
vtkSMProxy* renderviewsettings = pxm->GetProxy("RenderViewSettings");
assert(renderviewsettings);
vtkSMPropertyHelper(renderviewsettings, "ResolveCoincidentTopology")
.Set(0); // WIP Is This necessary
// 创建视图
pqRenderView* view =
qobject_cast<pqRenderView*>(pqApplicationCore::instance()->getObjectBuilder()->createView(
pqRenderView::renderViewType(), pqActiveObjects::instance().activeServer()));
assert(view);
pqActiveObjects::instance().setActiveView(view);
pqApplicationCore::instance()->getObjectBuilder()->addToLayout(view);
double bgcolor[3] = { 0, 0, 0 };
vtkSMPropertyHelper(view->getProxy(), "Background").Set(bgcolor, 3); // 设置背景颜色
vtkSMPropertyHelper(view->getProxy(), "CenterAxesVisibility").Set(0); // 显示坐标轴
// vtkSMPropertyHelper(view->getProxy(),"MultiSamples").Set(4); //WIP set to 0 1, 4 ?
vi
pythonStartup
python启动函数,设置python搜多路径,然后引入一些python模块。
void lqLidarCoreManager::pythonStartup()
{
// 获取全局ProcessModule
vtkProcessModule* pm = vtkProcessModule::GetProcessModule();
// PrependPythonPath will complete automatically path to right python lib path depending os.
vtkPythonInterpreter::PrependPythonPath(pm->GetSelfDir().c_str(), "lidarview/__init__.py");
// Python Decorators
PythonQt::self()->addDecorators(new lqPythonQtDecorators(this));
// Start applogic
this->runPython(QString("import PythonQt\n"
"QtGui = PythonQt.QtGui\n"
"QtCore = PythonQt.QtCore\n"
"import lidarview.applogic as lv\n"
"lv.start()\n"));
}
onMeasurementGrid
控制三维视图中的网格的显示和隐藏,并将其状态保存到默认设置中。
void lqLidarCoreManager::onMeasurementGrid(bool gridVisible)
{
pqSettings* settings = pqApplicationCore::instance()->settings();
settings->setValue("LidarPlugin/MeasurementGrid/Visibility", gridVisible);
vtkNew<vtkSMParaViewPipelineControllerWithRendering> controller;
pqServerManagerModel* smmodel = pqApplicationCore::instance()->getServerManagerModel();
// pqPipelineSource的基类是pqProxy,最终基类是QObject。
// pqProxy的基类是pqServerManagerModelItem。
// smmodel->findItems返回一个pqServerManagerModelItem列表。
// pqPipelineSource是paraview qt中对vtkSMProxy的封装。
Q_FOREACH (pqPipelineSource* src, smmodel->findItems<pqPipelineSource*>())
{
if (IsProxy<vtkGridSource>(src->getProxy()))
{ // 控制显示和隐藏
controller->SetVisibility(vtkSMSourceProxy::SafeDownCast(src->getProxy()),
0,
pqActiveObjects::instance().activeView()->getViewProxy(),
gridVisible);
}
}
// 进行渲染
pqApplicationCore::instance()->render();
}
onResetCameraLidar
重置观察视角,通过对camera进行操作实现。
设置CameraPosition, CameraFocalPoint 和 CameraViewUp为默认参数,将观察视角重置。
void lqLidarCoreManager::onResetCameraLidar()
{
pqRenderView* view = qobject_cast<pqRenderView*>(pqActiveObjects::instance().activeView());
if (!view)
{
return;
}
// See pqRenderView::resetViewDirection
// Position at 30 degrees [0, -(squareRoot(3)/2)*dist, (1/2)*dist]
vtkSMProxy* proxy = view->getProxy();
constexpr double dist = 100;
double pos[3] = { 0, -0.866025 * dist, (1.0 / 2.0) * dist };
double focal_point[3] = { 0, 0, 0 };
double view_up[3] = { 0, 0, 1 };
vtkSMPropertyHelper(proxy, "CameraPosition").Set(pos, 3);
vtkSMPropertyHelper(proxy, "CameraFocalPoint").Set(focal_point, 3);
vtkSMPropertyHelper(proxy, "CameraViewUp").Set(view_up, 3);
proxy->UpdateVTKObjects();
view->resetCamera();
view->render();
}
onResetCameraToForwardView
前视图观察,观察位置为(0, -72, 18),即位于y轴-72,高度为18出进行观察。此视角默认y轴为前向方向。
// 将观察视角变换为前视图视角
void lqLidarCoreManager::onResetCameraToForwardView()
{
pqRenderView* view = qobject_cast<pqRenderView*>(pqActiveObjects::instance().activeView());
if (!view)
{
return;
}
vtkSMProxy* proxy = view->getProxy();
double pos[3] = { 0, -72, 18.0 };
double focal_point[3] = { 0, 0, 0 };
double view_up[3] = { 0, 0.27, 0.96 };
vtkSMPropertyHelper(proxy, "CameraPosition").Set(pos, 3);
vtkSMPropertyHelper(proxy, "CameraFocalPoint").Set(focal_point, 3);
vtkSMPropertyHelper(proxy, "CameraViewUp").Set(view_up, 3);
proxy->UpdateVTKObjects();
view->setCenterOfRotation(0, 0, 0);
view->render();
}
onResetCenterToLidarCenter
将观察视角旋转中心设置为原定,并设置了两次。
对于setCenterOfRotation,需要有设当的交互方式,比如vtkPVInteractorStyle子类并使用旋转中心的相机操纵器。 默认情况下,它们设置正确。
// 从原点进行观察
void lqLidarCoreManager::onResetCenterToLidarCenter()
{
pqRenderView* view = qobject_cast<pqRenderView*>(pqActiveObjects::instance().activeView());
if (!view)
{
return;
}
// Set Axis Center to 0 0 0
double center[3] = { 0.0, 0.0, 0.0 };
view->setCenterOfRotation(center);
view->render();
// Set Camera Rotation Focal Point
view->setCenterOfRotation(0, 0, 0);
view->render();
}
saveFramesToPCAP
将数据保存为pcap格式。
调用了vtkLidarReader进行操作,vtkLidarReader的SaveFrame函数。SaveFrame函数中使用了vtkPacketFileWriter类,vtkPacketFileWriter类是对pcap库的包装。
// 保存pcap数据包
// 调用vtkLidarReader进行保存
void lqLidarCoreManager::saveFramesToPCAP(vtkSMSourceProxy* proxy,
int startFrame,
int endFrame,
const QString& filename)
{
if (!proxy)
{
return;
}
vtkLidarReader* reader = vtkLidarReader::SafeDownCast(proxy->GetClientSideObject());
if (!reader)
{
return;
}
reader->Open();
reader->SaveFrame(startFrame, endFrame, filename.toUtf8().data());
reader->Close();
}
saveFramesToLAS
将数据保存为LAS格式。使用了LASFileWriter进行保存。
// 保存数据为LAS格式
// 调用LASFileWriter进行保存
void lqLidarCoreManager::saveFramesToLAS(vtkLidarReader* reader,
vtkPolyData* position,
int startFrame,
int endFrame,
const QString& filename,
int positionMode)
{
if (!reader || (positionMode > 0 && !position))
{
return;
}
// initialize origin point
double northing, easting, height;
easting = northing = height = 0;
// projection transform parameters
int gcs, in, out, utmZone;
gcs = in = out = utmZone = 0;
// data accuracy
double neTol, hTol;
hTol = neTol = 1e-3;
bool isLatLon = false;
LASFileWriter writer;
writer.Open(qPrintable(filename));
// not sensor relative; it can be
// relative registered data or
// georeferenced data
if (positionMode > 0)
{
// Georeferenced data
if (positionMode > 1)
{
// Since the data are georeferenced here, we must
// check that a position reader is provided
if (position)
{
vtkDataArray* const zoneData = position->GetFieldData()->GetArray("zone");
vtkDataArray* const eastingData = position->GetPointData()->GetArray("easting");
vtkDataArray* const northingData = position->GetPointData()->GetArray("northing");
vtkDataArray* const heightData = position->GetPointData()->GetArray("height");
if (zoneData && zoneData->GetNumberOfTuples() && eastingData &&
eastingData->GetNumberOfTuples() && northingData && northingData->GetNumberOfTuples() &&
heightData && heightData->GetNumberOfTuples())
{
// We assume that eastingData, norhtingData and heightData are in system reference
// coordinates (srs) of UTM zoneData
utmZone = static_cast<int>(zoneData->GetComponent(0, 0));
// should in some cases use 32700? 32600 is for northern UTM zone, 32700 for southern UTM
// zone
gcs = 32600 + utmZone;
out = gcs;
if (positionMode == 3) // Absolute lat/lon
{
in = gcs; // ...or 32700?
out = 4326; // lat/lon (espg id code for lat-long-alt coordinates)
neTol = 1e-8; // about 1mm;
isLatLon = true;
}
northing = northingData->GetComponent(0, 0);
easting = eastingData->GetComponent(0, 0);
height = heightData->GetComponent(0, 0);
}
}
}
}
std::cout << "origin : [" << northing << ";" << easting << ";" << height << "]" << std::endl;
std::cout << "gcs : " << gcs << std::endl;
writer.SetPrecision(neTol, hTol);
writer.SetGeoConversionUTM(utmZone, isLatLon);
writer.SetOrigin(easting, northing, height);
QProgressDialog progress("Exporting LAS...",
"Abort Export",
startFrame,
startFrame + (endFrame - startFrame) * 2,
getMainWindow());
progress.setWindowModality(Qt::WindowModal);
reader->Open();
for (int frame = startFrame; frame <= endFrame; ++frame)
{
progress.setValue(frame);
if (progress.wasCanceled())
{
reader->Close();
return;
}
const vtkSmartPointer<vtkPolyData>& data = reader->GetFrame(frame);
writer.UpdateMetaData(data.GetPointer());
}
writer.FlushMetaData();
for (int frame = startFrame; frame <= endFrame; ++frame)
{
progress.setValue(endFrame + (frame - startFrame));
if (progress.wasCanceled())
{
reader->Close();
return;
}
const vtkSmartPointer<vtkPolyData>& data = reader->GetFrame(frame);
writer.WriteFrame(data.GetPointer());
}
reader->Close();
}
getActiveServer
通过单例模式获取激活状态的服务指针。
pqServer* lqLidarCoreManager::getActiveServer()
{
pqApplicationCore* app = pqApplicationCore::instance();
pqServerManagerModel* smModel = app->getServerManagerModel();
pqServer* server = smModel->getItemAtIndex<pqServer*>(0);
return server;
}
getMainWindow
通过单例模式获取主窗口指针。
QWidget* lqLidarCoreManager::getMainWindow()
{
Q_FOREACH (QWidget* topWidget, QApplication::topLevelWidgets())
{
if (qobject_cast<QMainWindow*>(topWidget))
{
return topWidget;
}
}
return nullptr;
}
onEnableCrashAnalysis
开启或关闭崩溃分析。
void lqLidarCoreManager::onEnableCrashAnalysis(bool crashAnalysisEnabled)
{
pqSettings* const Settings = pqApplicationCore::instance()->settings();
Settings->setValue("LidarPlugin/MainWindow/EnableCrashAnalysis", crashAnalysisEnabled);
}
onCloseAllData
关闭所有数据,并重置观察视角。
void lqLidarCoreManager::onCloseAllData()
{
// Remove lidars
RemoveAllProxyTypeFromPipelineBrowser<vtkLidarReader>();
RemoveAllProxyTypeFromPipelineBrowser<vtkLidarStream>();
RemoveAllProxyTypeFromPipelineBrowser<vtkPositionOrientationPacketReader>();
// Reset Camera
this->onResetCameraToForwardView();
// WIP Disable actions
this->runPython("lv.disableSaveActions()\n");
}
onResetDefaultSettings
恢复默认设置。
void lqLidarCoreManager::onResetDefaultSettings()
{
QMessageBox messageBox;
messageBox.setIcon(QMessageBox::Warning);
std::stringstream ss;
ss << "This action will reset all settings. "
<< "This action requires " << SOFTWARE_NAME << " to restart to be completly reset. "
<< "Every unsaved changes will be lost. Are you sure you want to reset " << SOFTWARE_NAME
<< " settings?";
messageBox.setText(ss.str().c_str());
messageBox.setStandardButtons(QMessageBox::Cancel | QMessageBox::Ok);
if (messageBox.exec() == QMessageBox::Ok)
{
// Clear Settings, sets the no restore flag "pqApplicationCore.DisableSettings"
pqApplicationCore::instance()->clearSettings();
// Quit the current application instance and restart a new one.
qApp->quit();
QStringList appArguments = qApp->arguments();
QString appName = appArguments.at(0);
appArguments.pop_front();
QProcess::startDetached(appName, appArguments);
}
}