Подключите определенный сигнал и слот один раз, а затем отключите их - PullRequest
0 голосов
/ 20 февраля 2019

Я собираюсь сохранить изображение с камеры, которая непрерывно захватывает изображения.Для камеры создается один сигнал, который продолжает отправлять изображения, и для главного окна создается слот, который сохраняет изображение в реальном времени, нажав кнопку сохранения изображения.Я имею в виду следующее:

//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 голосов
/ 20 февраля 2019

Это проблема 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государство.Конечный автомат позволяет сохранять изображение только тогда, когда это необходимо для сохранения: в противном случае копия не сохраняется.

Приятная особенность конечных автоматов состоит в том, что они позволяют вам выразить сложные шаблоны поведения в ясной и понятной форме.стандартизированный способ.Они определенно недостаточно используются во многих интерфейсах.

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 = {};
  });
}

Хорошая идея - одновременное сохранение изображения: в противном случае пользователь убивает работу пользователя, выполняя медленный дисковый ввод-вывод в потоке графического интерфейса.

Наконец: клонирование cv::Mat обычно не требуется: весь смысл этого типа в том, что он подсчитывает ссылки и неявно разделяет их - это тип копирования при записи.Это как QImage в этом отношении.

0 голосов
/ 20 февраля 2019

Несмотря на то, что я не вижу ничего явно неправильного или неудобного с используемым в настоящее время методом подключения / отключения, вот пара мозговых штурмов и предложений.

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

Но, насколько я знаю, это будет блокировать all сигналы, излучаемые изcamera.(И, возможно, оттуда исходят другие сигналы.) Так что это может быть не вполне жизнеспособным вариантом.

Логический флаг

Другой возможностью будет использование закрытой переменной-члена 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;
}

Мне кажется, это может быть довольно грубым, но ¯ \ _ (ツ) _ / ¯ идея - это идея.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...