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

时间:2019-02-20 14:35:04

标签: qt

我要从连续拍摄图像的相机中保存图像。为摄像机创建一个信号,该信号继续发送图像,并为主窗口创建一个插槽,该插槽通过单击图像保存按钮来保存实时图像。我所拥有的如下:

//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)));
}

以上代码使我可以保存一张图像,然后断开插槽和插槽的连接。除了使用断开连接方法外,我在这里的问题还有其他方法(最好是更整洁)暂时仅接收一次图像并将其保存。

2 个答案:

答案 0 :(得分:2)

这是一个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

答案 1 :(得分:1)

尽管我对目前使用的连接/断开方法并没有发现任何明显的错误或不满意,但这里有一些头脑风暴和建议。

QObject :: blockSignals

一种可能是利用QObject::blockSignals()并在[IPKernelApp] ERROR | Failed to load connection file: 'C:\\Users\\epinheiro\\App Data\\Roaming\\jupyter\\runtime\\kernel-3fbbe97a-8dd5-4e15-8db1-2e7cef385b58.jso n' Traceback (most recent call last): File "c:\users\epinheiro\appdata\local\programs\python\python37\lib\site-packa ges\ipykernel\kernelapp.py", line 224, in init_connection_file self.load_connection_file() File "c:\users\epinheiro\appdata\local\programs\python\python37\lib\site-packa ges\jupyter_client\connect.py", line 494, in load_connection_file info = json.load(f) AttributeError: module 'json' has no attribute 'load' [W 12:14:08.636 NotebookApp] KernelRestarter: restart failed [W 12:14:08.636 NotebookApp] Kernel 3fbbe97a-8dd5-4e15-8db1-2e7cef385b58 died, r emoving from map. [W 12:14:53.844 NotebookApp] Timeout waiting for kernel_info reply from 3fbbe97a -8dd5-4e15-8db1-2e7cef385b58 [E 12:14:53.847 NotebookApp] Error opening stream: HTTP 404: Not Found (Kernel d oes not exist: 3fbbe97a-8dd5-4e15-8db1-2e7cef385b58) 上调用它。您可以在构造函数中调用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); } 发出的all信号。 (而且您可能还会从那里发出其他信号。)因此,这可能不是一个完全可行的选择。

布尔标志

另一种可能性是使用私有成员变量camera来过滤掉没有单击按钮之前的信号。

bool saveNextImage

我觉得这可能还很原始,但是\ _(ツ)_ /¯一个主意就是一个主意。