2

我正在尝试将VTK回调连接到Qt插槽,因此当回调发生时将触发该插槽。

我正在使用 aQVTKWidget来渲染已添加到 aPCLVisualizer中的点云(来自点云库,PCL)。

让我们展示一些代码:

点云.h

class PointCloud: public QObject {
Q_OBJECT
  private:
    static void loadStartCallback(
      vtkObject *caller,
      unsigned long eventId,
      void *clientData,
      void *callData
    );

    static void loadEndCallback(
      vtkObject *caller,
      unsigned long eventId,
      void *clientData,
      void *callData
    );
    void load(void);
    // more funcs and methods

  private:
   QVTKWidget* widget;
   pcl::visualization::PCLVisualizer* visualizer;
   unsinged long observerStartTag;
   unsinged long observerEndTag;
   // more attributes
}

点云.cpp

  void PointCloud::loadStartCallback(
    vtkObject* caller,
    unsigned long eventId,
    void* clientData,
    void* callData
  ) {
    qDebug() << "\t\tPointCloud - loadCallback started\n";
    if(clientData) {
      PointCloud* self = reinterpret_cast<PointCloud*>( clientData );
      self->widget->GetRenderWindow()->RemoveObserver(self->observerStartTag);
  }

  void PointCloud::loadEndCallback(
    vtkObject* caller,
    unsigned long eventId,
    void* clientData,
    void* callData
  ) {
    qDebug() << "\t\tPointCloud - loadCallback ended\n";
    if(clientData) {
      PointCloud* self = reinterpret_cast<PointCloud*>( clientData );
      self->widget->GetRenderWindow()->RemoveObserver(self->observerEndTag);
    }
  }

 void load(void) {
   vtkSmartPointer<vtkRenderWindow> renderWindow = visualizer->getRenderWindow();

   vtkSmartPointer<vtkCallbackCommand> startCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    startCallback->SetCallback( loadStartCallback );
    startCallback->SetClientData(this);
    observerStartTag = renderWindow->AddObserver(vtkCommand::StartEvent, startCallback );

    vtkSmartPointer<vtkCallbackCommand> endCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    endCallback->SetCallback( loadEndCallback );
    endCallback->SetClientData(this);
    observerEndTag = renderWindow->AddObserver(vtkCommand::EndEvent, endCallback );

    // more processing. local_cloud is already populated 
    // and functional at this point
    widget->SetRenderWindow( renderWindow );
    visualizer->addPointCloud<pcl::PointXYZ>(local_cloud, "local_cloud");
    widget->Show();
    widget->Update();
 }

这很好用,一旦云渲染开始,将打印PointCloud - loadCallback started,当渲染结束并显示云时,将打印消息PointCloud - loadCallback end

现在,除了打印结束消息之外,我还想触发一个Qt插槽。我正在尝试为此使用vtkEventQtSlotConnect该类,因为这似乎是将回调连接到插槽的正确选择:

PointCloud.h 中的新功能

private slots:
  void test(void);

PointCloud.cpp 中的新功能

void PointCloud::test(void) { qDebug() << "\t\tThis is a test\n; }

在调用 Visualizer->addPointCloud 之前添加到 PointCloud::load()

    vtkEventQtSlotConnect* vtk_qt_connector = vtkEventQtSlotConnect::New();
    vtk_qt_connector->Connect(
        renderWindow,
        vtkCommand::EndEvent,
        this,
        SLOT(test(void)),
        0,
        1.0
    );

    // AFTER widget->Update()
    vtk_qt_connector->Disconnect(); // NO PARAM: disconnects ALL slots
    vtk_qt_connector->Delete();
  } // End of PointCloud::load()

通过这些添加,回调中的消息被打印,但test()插槽内的消息永远不会显示。

知道我做错了什么吗?

编辑

VTK我见过的回调示例中,avtkRendeWindowInteractor用于管理回调。但是,如果我向其中添加回调观察者,则不如直接将它们添加到渲染窗口准确。

4

1 回答 1

2

Ok, I have checked again the code and found something new. Some co-worker has added a QThread in the load() method to smooth things, but forgot to document/tell that there was a QThreadthere.

In PointCloud::load()

 QThread* thread = new QThread;
 ThreadedCloud* tcloud = new ThreadedCloud; // computes internal vars and more
 tcloud->moveToThread(thread);

 connect(thread, SIGNAL(started()), tcloud, SLOT(read()), Qt::QueuedConnection );
 connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::QueuedConnection );
 connect(tcloud, SIGNAL(cloudIsLoaded()), this, SLOT(addCloudToViewer()), Qt::QueuedConnection );
 connect(tcloud, SIGNAL(cloudIsLoaded()), thread, SLOT(quit()), Qt::QueuedConnection );
 connect(tcloud, SIGNAL(cloudIsLoaded()), tcloud, SLOT(deleteLater()), Qt::QueuedConnection );
 connect(tcloud, SIGNAL(cloudIsNotLoaded(std::string)), this, SLOT(errorLoadingCloud(std::string)), Qt::QueuedConnection );
 thread->start();

The cloudIsLoaded() is a signal that is emitted when the thread has finished whatever it has to do and we're ready to add the cloud to the PCLVisualizer and render it. That is done in addCloudToViewer.

The key factor here is that once the thread is started, the control flow exits the load()method and because I'm disconnecting the callback/slot before the end of the method, once the cloud is being rendererd that connection isn't there anymore!

So the solution was to move the vtk_qt_connector inside the addCloudToViewer method and do there the callback/slot connection.

于 2013-01-09T12:25:21.017 回答