-
-
Notifications
You must be signed in to change notification settings - Fork 465
Improvements to Qt/QML interface #945
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
#include <QDebug> | ||
#include <QMetaType> | ||
#include <QScopeGuard> | ||
#include <QtQmlIntegration/qqmlintegration.h> | ||
|
||
#ifdef QT_MULTIMEDIA_LIB | ||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||
|
@@ -27,11 +28,12 @@ | |
|
||
namespace ZXingQt { | ||
|
||
Q_NAMESPACE | ||
|
||
//TODO: find a better way to export these enums to QML than to duplicate their definition | ||
// #ifdef Q_MOC_RUN produces meta information in the moc output but it does end up working in qml | ||
#ifdef QT_QML_LIB | ||
namespace QML { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why a sub-namespace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Advantage of moving the redefinition of the enums in order to expose it to QML, to a separate namespace is that the enums in ZXingQt can be a typedef to their ZXing versions. This allows your Nobody should uses this QML namespace directly, as from C++, one should use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, thanks for the explanation. I'm wondering whether there would be any benefit to be had on the QWidgets side if we simply used those There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, not much. Almost the same as using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be one advantage. Another would be that offering a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does that imply that you also want to create a Qt wrapper for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is an excellent point, which I've overlooked. While this obviously worsens the cost-benefit ratio, I'd argue that it would still be worth it. First I thought: simply subclass If you have general comments about the fluid API idea (which is still in 'flow'...), please add them below the above linked comment for better discoverability. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So, you would like to implement all use cases inside Concerning the fluent approach, I'm not sure how well it works in case of a |
||
Q_NAMESPACE | ||
enum class BarcodeFormat | ||
{ | ||
None = 0, ///< Used as a return value if no valid barcode has been detected | ||
|
@@ -64,20 +66,24 @@ enum class ContentType { Text, Binary, Mixed, GS1, ISO15434, UnknownECI }; | |
|
||
enum class TextMode { Plain, ECI, HRI, Hex, Escaped }; | ||
|
||
#else | ||
Q_DECLARE_FLAGS(BarcodeFormats, BarcodeFormat) | ||
Q_DECLARE_OPERATORS_FOR_FLAGS(BarcodeFormats) | ||
Q_FLAG_NS(BarcodeFormats) | ||
Q_ENUM_NS(BarcodeFormat) | ||
|
||
Q_ENUM_NS(ContentType) | ||
Q_ENUM_NS(TextMode) | ||
} // namespace QML | ||
#endif | ||
|
||
using ZXing::BarcodeFormat; | ||
using ZXing::ContentType; | ||
using ZXing::TextMode; | ||
#endif | ||
|
||
using ZXing::ReaderOptions; | ||
using ZXing::Binarizer; | ||
using ZXing::BarcodeFormats; | ||
|
||
Q_ENUM_NS(BarcodeFormat) | ||
Q_ENUM_NS(ContentType) | ||
Q_ENUM_NS(TextMode) | ||
|
||
template<typename T, typename = decltype(ZXing::ToString(T()))> | ||
QDebug operator<<(QDebug dbg, const T& v) | ||
{ | ||
|
@@ -129,15 +135,16 @@ class Barcode : private ZXing::Barcode | |
|
||
using ZXing::Barcode::isValid; | ||
|
||
BarcodeFormat format() const { return static_cast<BarcodeFormat>(ZXing::Barcode::format()); } | ||
ContentType contentType() const { return static_cast<ContentType>(ZXing::Barcode::contentType()); } | ||
BarcodeFormat format() const { return ZXing::Barcode::format(); } | ||
ContentType contentType() const { return ZXing::Barcode::contentType(); } | ||
QString formatName() const { return QString::fromStdString(ZXing::ToString(ZXing::Barcode::format())); } | ||
QString contentTypeName() const { return QString::fromStdString(ZXing::ToString(ZXing::Barcode::contentType())); } | ||
const QString& text() const { return _text; } | ||
const QByteArray& bytes() const { return _bytes; } | ||
const Position& position() const { return _position; } | ||
}; | ||
|
||
namespace Detail { | ||
inline QList<Barcode> ZXBarcodesToQBarcodes(ZXing::Barcodes&& zxres) | ||
{ | ||
QList<Barcode> res; | ||
|
@@ -149,6 +156,7 @@ inline QList<Barcode> ZXBarcodesToQBarcodes(ZXing::Barcodes&& zxres) | |
#endif | ||
return res; | ||
} | ||
} // namespace Detail | ||
|
||
inline QList<Barcode> ReadBarcodes(const QImage& img, const ReaderOptions& opts = {}) | ||
{ | ||
|
@@ -172,7 +180,7 @@ inline QList<Barcode> ReadBarcodes(const QImage& img, const ReaderOptions& opts | |
}; | ||
|
||
auto exec = [&](const QImage& img) { | ||
return ZXBarcodesToQBarcodes(ZXing::ReadBarcodes( | ||
return Detail::ZXBarcodesToQBarcodes(ZXing::ReadBarcodes( | ||
{img.bits(), img.width(), img.height(), ImgFmtFromQImg(img), static_cast<int>(img.bytesPerLine())}, opts)); | ||
}; | ||
|
||
|
@@ -282,7 +290,7 @@ inline QList<Barcode> ReadBarcodes(const QVideoFrame& frame, const ReaderOptions | |
} | ||
QScopeGuard unmap([&] { img.unmap(); }); | ||
|
||
return ZXBarcodesToQBarcodes(ZXing::ReadBarcodes( | ||
return Detail::ZXBarcodesToQBarcodes(ZXing::ReadBarcodes( | ||
{img.bits(FIRST_PLANE) + pixOffset, img.width(), img.height(), fmt, img.bytesPerLine(FIRST_PLANE), pixStride}, opts)); | ||
} | ||
else { | ||
|
@@ -330,6 +338,9 @@ class BarcodeReader : public QObject, private ReaderOptions | |
{ | ||
Q_OBJECT | ||
|
||
Q_PROPERTY(QML::BarcodeFormats formats READ formatsQML WRITE setFormatsQML NOTIFY formatsChanged) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I'm overlooking something but does this mean the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, The Technically, you can access the properties exposed by the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That is what I was talking about. My point is, that the meta-object-system from Qt which is used for signal dispatching and also cross-thread-boundary message passing and I forgot for what else more, has an application outside (and predates) QML. Advertising the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
To be honest, I only use |
||
Q_PROPERTY(QML::TextMode textMode READ textModeQML WRITE setTextModeQML NOTIFY textModeChanged) | ||
|
||
public: | ||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||
BarcodeReader(QObject* parent = nullptr) : QAbstractVideoFilter(parent) {} | ||
|
@@ -346,35 +357,23 @@ class BarcodeReader : public QObject, private ReaderOptions | |
|
||
#endif | ||
|
||
// TODO: find out how to properly expose QFlags to QML | ||
// simply using ZQ_PROPERTY(BarcodeFormats, formats, setFormats) | ||
// results in the runtime error "can't assign int to formats" | ||
Q_PROPERTY(int formats READ formats WRITE setFormats NOTIFY formatsChanged) | ||
int formats() const noexcept | ||
{ | ||
auto fmts = ReaderOptions::formats(); | ||
return *reinterpret_cast<int*>(&fmts); | ||
} | ||
Q_SLOT void setFormats(int newVal) | ||
BarcodeFormats formats() const noexcept { return ReaderOptions::formats(); } | ||
Q_SLOT void setFormats(BarcodeFormats newVal) | ||
{ | ||
if (formats() != newVal) { | ||
ReaderOptions::setFormats(static_cast<ZXing::BarcodeFormat>(newVal)); | ||
ReaderOptions::setFormats(newVal); | ||
emit formatsChanged(); | ||
qDebug() << ReaderOptions::formats(); | ||
} | ||
} | ||
Q_SIGNAL void formatsChanged(); | ||
|
||
Q_PROPERTY(TextMode textMode READ textMode WRITE setTextMode NOTIFY textModeChanged) | ||
TextMode textMode() const noexcept { return static_cast<TextMode>(ReaderOptions::textMode()); } | ||
TextMode textMode() const noexcept { return ReaderOptions::textMode(); } | ||
Q_SLOT void setTextMode(TextMode newVal) | ||
{ | ||
if (textMode() != newVal) { | ||
ReaderOptions::setTextMode(static_cast<ZXing::TextMode>(newVal)); | ||
ReaderOptions::setTextMode(newVal); | ||
emit textModeChanged(); | ||
} | ||
} | ||
Q_SIGNAL void textModeChanged(); | ||
|
||
ZQ_PROPERTY(bool, tryRotate, setTryRotate) | ||
ZQ_PROPERTY(bool, tryHarder, setTryHarder) | ||
|
@@ -408,6 +407,20 @@ public slots: | |
void failedRead(); | ||
void foundBarcode(ZXingQt::Barcode barcode); | ||
|
||
void formatsChanged(); | ||
axxel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
void textModeChanged(); | ||
|
||
private: | ||
QML::BarcodeFormats formatsQML() const noexcept | ||
{ | ||
auto fmts = formats(); | ||
return QML::BarcodeFormats(*reinterpret_cast<int*>(&fmts)); | ||
} | ||
void setFormatsQML(QML::BarcodeFormats newVal) { setFormats(static_cast<ZXing::BarcodeFormat>(newVal.operator int())); } | ||
|
||
QML::TextMode textModeQML() const noexcept { return static_cast<QML::TextMode>(textMode()); } | ||
void setTextModeQML(QML::TextMode newVal) { setTextMode(static_cast<ZXing::TextMode>(newVal)); } | ||
|
||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||
public: | ||
QVideoFilterRunnable *createFilterRunnable() override; | ||
|
@@ -489,6 +502,8 @@ Q_DECLARE_METATYPE(ZXingQt::Barcode) | |
|
||
namespace ZXingQt { | ||
|
||
namespace Detail { | ||
|
||
inline void registerQmlAndMetaTypes() | ||
{ | ||
qRegisterMetaType<ZXingQt::BarcodeFormat>("BarcodeFormat"); | ||
|
@@ -497,14 +512,24 @@ inline void registerQmlAndMetaTypes() | |
|
||
// supposedly the Q_DECLARE_METATYPE should be used with the overload without a custom name | ||
// but then the qml side complains about "unregistered type" | ||
qRegisterMetaType<ZXingQt::Position>("Position"); | ||
qRegisterMetaType<ZXingQt::Barcode>("Barcode"); | ||
qmlRegisterType<ZXingQt::Position>("ZXing", 1, 0, "position"); | ||
qmlRegisterType<ZXingQt::Barcode>("ZXing", 1, 0, "barcode"); | ||
|
||
qmlRegisterUncreatableMetaObject( | ||
ZXingQt::staticMetaObject, "ZXing", 1, 0, "ZXing", "Access to enums & flags only"); | ||
ZXingQt::QML::staticMetaObject, "ZXing", 1, 0, "ZXing", "Access to enums & flags only"); | ||
#ifdef QT_MULTIMEDIA_LIB | ||
qmlRegisterType<ZXingQt::BarcodeReader>("ZXing", 1, 0, "BarcodeReader"); | ||
#endif // QT_MULTIMEDIA_LIB | ||
} | ||
|
||
struct ZXingQtInitializer | ||
{ | ||
ZXingQtInitializer() {registerQmlAndMetaTypes();} | ||
} inline zxingQtInitializer; | ||
|
||
} // namespace Detail | ||
|
||
|
||
} // namespace ZXingQt | ||
|
||
#endif // QT_QML_LIB |
Uh oh!
There was an error while loading. Please reload this page.