连接特定的信号和插槽,然后断开它们



我将从连续捕获图像的相机中保存图像。为相机创建一个信号,该信号继续发送图像,并为MainWindow创建一个插槽,该插槽通过单击"保存"按钮来保存实时图像。我所拥有的如下:

//connection is triggered by button clicking
connect(camera, SIGNAL(sendImage(Mat)), this, SLOT(saveImage(Mat)), Qt::UniqueConnection); 
qDebug()<<"Image saved";
//Here is my slot definition. An image of OpenCV Mat is saved, followed by signal-slot disconnection. 
void MainWindow::saveImage(Mat cvimg)
{
    Mat savedImage;
    savedImage =cvimg.clone();
    imwrite("C:/Debug/Data/Save.jpg", savedImage);
    imshow("SavedImage", savedImage);
    disconnect(camera, SIGNAL(sendImage(Mat)),this, SLOT(saveImage(Mat)));
}

上面的代码使我可以保存一个图像,然后断开插槽和插槽。我的问题除了使用断开方法外,还有其他任何方法,最好是更整洁的方法,可以暂时收到一张图像并保存。

这是一个XY问题:您不应该弄乱短暂的连接,因为它们并不是要使用这种方式。想到了几种解决方案。

您可以缓存相机的图像,并保存缓存的值:

Q_DECLARE_METATYPE(cv::Mat)
const char kImageCache[] = "imageCache";
// connect once, as soon as you have the camera available
connect(camera, &Camera::sendImage, [camera](const cv::Mat &image){
  camera->setProperty(kImageCache, image);
});
connect(saveAction, &QAction::triggered, camera, [camera]{
  auto const image = camera->property(kImageCache).value<cv::Mat>();
  cv::imshow("Saved Image", image);
  QtConcurrent::run([image]{
    static std::atomic_bool saving;
    static bool not_saving;
    if (saving.compare_exchange_strong(not_saving, true)) {
      cv::imwrite("foo", image);
      saving.store(not_saving);
    }
  });
});

您还可以具有对s_waiting状态中图像的反应的状态计算机。状态计算机只能在需要保存时才保留图像:否则,不保留副本。

关于州机器的好处是,它们允许您以清晰和标准化的方式表达复杂的行为模式。在许多UI中,它们肯定不足。

class MyWindow : public QWidget {
  …
  QStateMachine m_machine{this};
  QState s_idle{&m_machine}, s_waiting{&m_machine}, s_saving{&m_machine};
  cv::Mat m_saveImage;
  Q_SIGNAL void saved();
};
MyWindow::MyWindow(…):… {
  m_machine.setInitialState(&s_idle);
  m_machine.start();
  s_idle.addTransition(saveAction, &QAction::triggered, &s_waiting);
  s_waiting.addTransition(camera, &Camera::sendImage, &s_saving);
  s_saving.addTransition(this, &decltype(*this)::saved, &s_idle);
  connect(camera, &Camera::sendImage, &s_waiting, [this](const auto &image){
    if (s_waiting.isActive())
      m_saveImage = image; 
  });
  connect(&s_saving, &QState::entered, [this]{
    cv::imshow(…, m_saveImage);
    QtConcurrent::run([image = m_saveImage, this]{
      cv::imwrite(…, image);
      emit saved();
    });
    m_saveImage = {};
  });
}

同时保存图像是一个好主意:否则,您是通过在GUI线程中执行慢速磁盘I/O杀死用户体验。

最后:cv::Mat的克隆通常是不必要的:该类型的全部要点是它的参考计数并隐式共享 - 它是抄写的类型。在这方面就像QImage

尽管我并没有真正看到任何明显的错误或对连接/断开连接的方法感到不适,但这里有几个头脑风暴和建议。

Qobject :: BlockSignals

一种可能性可能是使用QObject::blockSignals()并在camera上调用。您可以在构造函数中调用connect,然后每次单击按钮并完成插槽完成时切换blockSignals

MainWindow::MainWindow(/* QWidget* parent or whatever */)
{
    //  connect called once in constructor
    connect(camera, SIGNAL(sendImage(Mat)), this, SLOT(saveImage(Mat)), Qt::UniqueConnection);
}
void MainWindow::on_saveImageButton_clicked()
{
    camera.blockSignals(false);
}
void MainWindow::saveImage(Mat cvimg)
{
    //  save/write operations
    //  ...
    //  instead of disconnecting...
    camera.blockSignals(true);
}

,但据我所知,这将阻止allcamera发出的信号。(而且您可能从那里发出其他信号。)因此,这可能不是一个完全可行的选择。

布尔旗

另一种可能性是使用私有成员变量bool saveNextImage过滤出尚未单击按钮之前的信号。

MainWindow::MainWindow(/* QWidget* parent or whatever */)
{
    //  connect called once in constructor
    connect(camera, SIGNAL(sendImage(Mat)), this, SLOT(saveImage(Mat)), Qt::UniqueConnection);
}
void MainWindow::on_saveImageButton_clicked()
{
    //  toggle flag
    saveNextImage = true;
}
void MainWindow::saveImage(Mat cvimg)
{
    //  check flag was set
    if (!saveNextImage)
        return;
    //  save/write operations 
    //  ...
    //  instead of disconnecting...
    //  toggle flag
    saveNextImage = false;
}

我觉得这可能是很原始的,但是 _(ツ)_/a是一个想法。

最新更新