mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-22 19:49:28 +00:00
QrScanner: macOS support
This commit is contained in:
parent
3db3e330d5
commit
a1db9ade6b
8 changed files with 83 additions and 152 deletions
|
@ -42,7 +42,7 @@ RUN git clone -b v3.18.4 --depth 1 https://github.com/Kitware/CMake && \
|
||||||
make -j$THREADS install && \
|
make -j$THREADS install && \
|
||||||
rm -rf $(pwd)
|
rm -rf $(pwd)
|
||||||
|
|
||||||
# freetype2: Required for Qt 5.15
|
# freetype2: Required for Qt 5.15, fontconfig
|
||||||
RUN git clone -b VER-2-10-2 --depth 1 https://git.savannah.gnu.org/git/freetype/freetype2.git && \
|
RUN git clone -b VER-2-10-2 --depth 1 https://git.savannah.gnu.org/git/freetype/freetype2.git && \
|
||||||
cd freetype2 && \
|
cd freetype2 && \
|
||||||
git reset --hard 132f19b779828b194b3fede187cee719785db4d8 && \
|
git reset --hard 132f19b779828b194b3fede187cee719785db4d8 && \
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -59,6 +59,7 @@ depends:
|
||||||
mac-release: CMAKEFLAGS += -DSTATIC=Off
|
mac-release: CMAKEFLAGS += -DSTATIC=Off
|
||||||
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
|
||||||
mac-release: CMAKEFLAGS += -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, Off)
|
mac-release: CMAKEFLAGS += -DCHECK_UPDATES=$(or ${CHECK_UPDATES}, Off)
|
||||||
|
mac-release: CMAKEFLAGS += -DWITH_SCANNER=$(or ${WITH_SCANNER}, On)
|
||||||
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
|
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
|
||||||
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
|
||||||
mac-release:
|
mac-release:
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
|
QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, ui(new Ui::QrCodeScanDialog)
|
, ui(new Ui::QrCodeScanDialog)
|
||||||
, m_scanner(new QrCodeScanner(this))
|
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
this->setWindowTitle("Scan QR Code");
|
this->setWindowTitle("Scan QR Code");
|
||||||
|
@ -31,10 +30,16 @@ QrCodeScanDialog::QrCodeScanDialog(QWidget *parent)
|
||||||
|
|
||||||
connect(ui->combo_camera, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &QrCodeScanDialog::onCameraSwitched);
|
connect(ui->combo_camera, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &QrCodeScanDialog::onCameraSwitched);
|
||||||
|
|
||||||
connect(m_scanner, &QrCodeScanner::decoded, this, &QrCodeScanDialog::onDecoded);
|
|
||||||
connect(m_scanner, &QrCodeScanner::notifyError, this, &QrCodeScanDialog::notifyError);
|
|
||||||
|
|
||||||
this->onCameraSwitched(0);
|
this->onCameraSwitched(0);
|
||||||
|
|
||||||
|
m_thread = new QrScanThread(this);
|
||||||
|
m_thread->start();
|
||||||
|
|
||||||
|
connect(m_thread, &QrScanThread::decoded, this, &QrCodeScanDialog::onDecoded);
|
||||||
|
connect(m_thread, &QrScanThread::notifyError, this, &QrCodeScanDialog::notifyError);
|
||||||
|
|
||||||
|
connect(&m_imageTimer, &QTimer::timeout, this, &QrCodeScanDialog::takeImage);
|
||||||
|
m_imageTimer.start(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QrCodeScanDialog::onCameraSwitched(int index) {
|
void QrCodeScanDialog::onCameraSwitched(int index) {
|
||||||
|
@ -42,24 +47,60 @@ void QrCodeScanDialog::onCameraSwitched(int index) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_scanner->setSource(nullptr);
|
m_camera.reset(new QCamera(m_cameras.at(index)));
|
||||||
delete m_camera;
|
|
||||||
|
|
||||||
m_camera = new QCamera(m_cameras.at(index), this);
|
auto captureMode = QCamera::CaptureStillImage;
|
||||||
connect(m_camera, &QCamera::statusChanged, [this](QCamera::Status status){
|
if (m_camera->isCaptureModeSupported(captureMode)) {
|
||||||
|
m_camera->setCaptureMode(captureMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(m_camera.data(), QOverload<QCamera::Error>::of(&QCamera::error), this, &QrCodeScanDialog::displayCameraError);
|
||||||
|
connect(m_camera.data(), &QCamera::statusChanged, [this](QCamera::Status status){
|
||||||
bool unloaded = (status == QCamera::Status::UnloadedStatus);
|
bool unloaded = (status == QCamera::Status::UnloadedStatus);
|
||||||
ui->frame_unavailable->setVisible(unloaded);
|
ui->frame_unavailable->setVisible(unloaded);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_camera->setCaptureMode(QCamera::CaptureViewfinder);
|
m_imageCapture.reset(new QCameraImageCapture(m_camera.data()));
|
||||||
|
if (!m_imageCapture->isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer)) {
|
||||||
|
qDebug() << "Capture to buffer is NOT supported";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_imageCapture->setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
|
||||||
|
|
||||||
|
connect(m_imageCapture.data(), &QCameraImageCapture::imageAvailable, this, &QrCodeScanDialog::processAvailableImage);
|
||||||
|
connect(m_imageCapture.data(), QOverload<int, QCameraImageCapture::Error, const QString &>::of(&QCameraImageCapture::error),
|
||||||
|
this, &QrCodeScanDialog::displayCaptureError);
|
||||||
|
|
||||||
m_camera->setViewfinder(ui->viewfinder);
|
m_camera->setViewfinder(ui->viewfinder);
|
||||||
|
|
||||||
m_scanner->setSource(m_camera);
|
|
||||||
m_scanner->setEnabled(true);
|
|
||||||
|
|
||||||
m_camera->start();
|
m_camera->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QrCodeScanDialog::displayCaptureError(int id, const QCameraImageCapture::Error error, const QString &errorString)
|
||||||
|
{
|
||||||
|
Q_UNUSED(id);
|
||||||
|
Q_UNUSED(error);
|
||||||
|
QMessageBox::warning(this, "Image Capture Error", errorString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrCodeScanDialog::displayCameraError()
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, "Camera Error", m_camera->errorString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrCodeScanDialog::processAvailableImage(int id, const QVideoFrame &frame) {
|
||||||
|
Q_UNUSED(id);
|
||||||
|
QImage img = frame.image();
|
||||||
|
img.convertTo(QImage::Format_RGB32);
|
||||||
|
m_thread->addImage(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QrCodeScanDialog::takeImage()
|
||||||
|
{
|
||||||
|
if (m_imageCapture->isReadyForCapture()) {
|
||||||
|
m_imageCapture->capture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QrCodeScanDialog::onDecoded(int type, const QString &data) {
|
void QrCodeScanDialog::onDecoded(int type, const QString &data) {
|
||||||
decodedString = data;
|
decodedString = data;
|
||||||
this->accept();
|
this->accept();
|
||||||
|
@ -71,6 +112,12 @@ void QrCodeScanDialog::notifyError(const QString &msg) {
|
||||||
|
|
||||||
QrCodeScanDialog::~QrCodeScanDialog()
|
QrCodeScanDialog::~QrCodeScanDialog()
|
||||||
{
|
{
|
||||||
delete m_camera;
|
m_thread->stop();
|
||||||
|
m_thread->quit();
|
||||||
|
if (!m_thread->wait(5000))
|
||||||
|
{
|
||||||
|
m_thread->terminate();
|
||||||
|
m_thread->wait();
|
||||||
|
}
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
|
@ -6,8 +6,11 @@
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QCamera>
|
#include <QCamera>
|
||||||
|
#include <QCameraImageCapture>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QVideoFrame>
|
||||||
|
|
||||||
#include "QrCodeScanner.h"
|
#include "QrScanThread.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class QrCodeScanDialog;
|
class QrCodeScanDialog;
|
||||||
|
@ -29,11 +32,19 @@ private slots:
|
||||||
void notifyError(const QString &msg);
|
void notifyError(const QString &msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void processAvailableImage(int id, const QVideoFrame &frame);
|
||||||
|
void displayCaptureError(int, QCameraImageCapture::Error, const QString &errorString);
|
||||||
|
void displayCameraError();
|
||||||
|
void takeImage();
|
||||||
|
|
||||||
Ui::QrCodeScanDialog *ui;
|
Ui::QrCodeScanDialog *ui;
|
||||||
|
|
||||||
|
QScopedPointer<QCamera> m_camera;
|
||||||
|
QScopedPointer<QCameraImageCapture> m_imageCapture;
|
||||||
|
|
||||||
|
QrScanThread *m_thread;
|
||||||
|
QTimer m_imageTimer;
|
||||||
QList<QCameraInfo> m_cameras;
|
QList<QCameraInfo> m_cameras;
|
||||||
QCamera *m_camera = nullptr;
|
|
||||||
QrCodeScanner *m_scanner;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_QRCODESCANDIALOG_H
|
#endif //FEATHER_QRCODESCANDIALOG_H
|
|
@ -1,72 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2014-2021, The Monero Project.
|
|
||||||
|
|
||||||
#include "QrCodeScanner.h"
|
|
||||||
#include <WalletManager.h>
|
|
||||||
#include <QVideoProbe>
|
|
||||||
#include <QCamera>
|
|
||||||
|
|
||||||
QrCodeScanner::QrCodeScanner(QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_processTimerId(-1)
|
|
||||||
, m_processInterval(750)
|
|
||||||
, m_enabled(true)
|
|
||||||
{
|
|
||||||
m_probe = new QVideoProbe(this);
|
|
||||||
m_thread = new QrScanThread(this);
|
|
||||||
m_thread->start();
|
|
||||||
|
|
||||||
connect(m_thread, &QrScanThread::decoded, this, &QrCodeScanner::decoded);
|
|
||||||
connect(m_thread, &QrScanThread::notifyError, this, &QrCodeScanner::notifyError);
|
|
||||||
connect(m_probe, &QVideoProbe::videoFrameProbed, this, &QrCodeScanner::processFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QrCodeScanner::setSource(QCamera *camera)
|
|
||||||
{
|
|
||||||
m_probe->setSource((QMediaObject *)camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QrCodeScanner::processFrame(const QVideoFrame &frame)
|
|
||||||
{
|
|
||||||
if (frame.isValid()){
|
|
||||||
m_curFrame = frame;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QrCodeScanner::enabled() const
|
|
||||||
{
|
|
||||||
return m_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QrCodeScanner::setEnabled(bool enabled)
|
|
||||||
{
|
|
||||||
m_enabled = enabled;
|
|
||||||
if(!enabled && (m_processTimerId != -1) )
|
|
||||||
{
|
|
||||||
this->killTimer(m_processTimerId);
|
|
||||||
m_processTimerId = -1;
|
|
||||||
}
|
|
||||||
else if (enabled && (m_processTimerId == -1) )
|
|
||||||
{
|
|
||||||
m_processTimerId = this->startTimer(m_processInterval);
|
|
||||||
}
|
|
||||||
emit enabledChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QrCodeScanner::timerEvent(QTimerEvent *event)
|
|
||||||
{
|
|
||||||
if (event->timerId() == m_processTimerId) {
|
|
||||||
m_thread->addFrame(m_curFrame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QrCodeScanner::~QrCodeScanner()
|
|
||||||
{
|
|
||||||
m_thread->stop();
|
|
||||||
m_thread->quit();
|
|
||||||
if (!m_thread->wait(5000))
|
|
||||||
{
|
|
||||||
m_thread->terminate();
|
|
||||||
m_thread->wait();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// Copyright (c) 2014-2020, The Monero Project.
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef QRCODESCANNER_H_
|
|
||||||
#define QRCODESCANNER_H_
|
|
||||||
|
|
||||||
#include <QImage>
|
|
||||||
#include <QVideoFrame>
|
|
||||||
#include "QrScanThread.h"
|
|
||||||
|
|
||||||
class QVideoProbe;
|
|
||||||
class QCamera;
|
|
||||||
|
|
||||||
class QrCodeScanner : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit QrCodeScanner(QObject *parent = nullptr);
|
|
||||||
~QrCodeScanner() override;
|
|
||||||
void setSource(QCamera*);
|
|
||||||
|
|
||||||
bool enabled() const;
|
|
||||||
void setEnabled(bool enabled);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void processFrame(const QVideoFrame &frame);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void enabledChanged();
|
|
||||||
void decoded(int type, const QString &data);
|
|
||||||
void decode(int type, const QString &data);
|
|
||||||
void notifyError(const QString &error, bool warning = false);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void timerEvent(QTimerEvent *);
|
|
||||||
QrScanThread *m_thread;
|
|
||||||
int m_processTimerId;
|
|
||||||
int m_processInterval;
|
|
||||||
int m_enabled;
|
|
||||||
QVideoFrame m_curFrame;
|
|
||||||
QVideoProbe *m_probe;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -33,7 +33,7 @@ void QrScanThread::processZImage(zbar::Image &image)
|
||||||
|
|
||||||
bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
|
bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
|
||||||
{
|
{
|
||||||
switch( qimg.format() ){
|
switch (qimg.format()) {
|
||||||
case QImage::Format_RGB32 :
|
case QImage::Format_RGB32 :
|
||||||
case QImage::Format_ARGB32 :
|
case QImage::Format_ARGB32 :
|
||||||
case QImage::Format_ARGB32_Premultiplied :
|
case QImage::Format_ARGB32_Premultiplied :
|
||||||
|
@ -69,34 +69,28 @@ void QrScanThread::processQImage(const QImage &qimg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QrScanThread::processVideoFrame(const QVideoFrame &frame)
|
|
||||||
{
|
|
||||||
processQImage(frame.image());
|
|
||||||
}
|
|
||||||
|
|
||||||
void QrScanThread::stop()
|
void QrScanThread::stop()
|
||||||
{
|
{
|
||||||
m_running = false;
|
m_running = false;
|
||||||
m_waitCondition.wakeOne();
|
m_waitCondition.wakeOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QrScanThread::addFrame(const QVideoFrame &frame)
|
void QrScanThread::addImage(const QImage &img)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
m_queue.append(frame);
|
m_queue.append(img);
|
||||||
m_waitCondition.wakeOne();
|
m_waitCondition.wakeOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QrScanThread::run()
|
void QrScanThread::run()
|
||||||
{
|
{
|
||||||
QVideoFrame frame;
|
|
||||||
while (m_running) {
|
while (m_running) {
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
while (m_queue.isEmpty() && m_running) {
|
while (m_queue.isEmpty() && m_running) {
|
||||||
m_waitCondition.wait(&m_mutex);
|
m_waitCondition.wait(&m_mutex);
|
||||||
}
|
}
|
||||||
if (!m_queue.isEmpty()) {
|
if (!m_queue.isEmpty()) {
|
||||||
processVideoFrame(m_queue.takeFirst());
|
processQImage(m_queue.takeFirst());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,7 +8,6 @@
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QVideoFrame>
|
|
||||||
#include <QCamera>
|
#include <QCamera>
|
||||||
#include <zbar.h>
|
#include <zbar.h>
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@ class QrScanThread : public QThread, public zbar::Image::Handler
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QrScanThread(QObject *parent = nullptr);
|
QrScanThread(QObject *parent = nullptr);
|
||||||
void addFrame(const QVideoFrame &frame);
|
void addImage(const QImage &img);
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -27,7 +26,6 @@ signals:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void run();
|
virtual void run();
|
||||||
void processVideoFrame(const QVideoFrame &);
|
|
||||||
void processQImage(const QImage &);
|
void processQImage(const QImage &);
|
||||||
void processZImage(zbar::Image &image);
|
void processZImage(zbar::Image &image);
|
||||||
virtual void image_callback(zbar::Image &image);
|
virtual void image_callback(zbar::Image &image);
|
||||||
|
@ -39,6 +37,6 @@ private:
|
||||||
bool m_running;
|
bool m_running;
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
QWaitCondition m_waitCondition;
|
QWaitCondition m_waitCondition;
|
||||||
QList<QVideoFrame> m_queue;
|
QList<QImage> m_queue;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
Loading…
Reference in a new issue