Skip to content

Commit

Permalink
Improvements (#19) (#20)
Browse files Browse the repository at this point in the history
* Added the QRtextarea

* Added the QrLabel from designs

* Should use the right camera and resolution when wasm

* Added normal cmake when using qml

* Added foo namespace to force linking

* Updated the readmes

* Only build shared libraries on actions

* Use 6.5.0 because of aqt-install not finding 6.6.0
  • Loading branch information
EddyTheCo authored Sep 18, 2023
1 parent c36c339 commit 9093ea6
Show file tree
Hide file tree
Showing 14 changed files with 355 additions and 49 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/build-test-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest,macos-latest,windows-latest]
sharedLib: [true,false]

uses: EddyTheCo/Common/.github/workflows/build-test-install.yml@main
permissions:
contents: write
with:
os: ${{ matrix.os }}
sharedLib: ${{ matrix.sharedLib }}
qtVersion: '6.6.0'
sharedLib: true
qtVersion: '6.5.0'
qtModules: 'qtshadertools qtmultimedia'
release:
if: startsWith(github.ref, 'refs/tags/v')
Expand Down
10 changes: 2 additions & 8 deletions QtQrDec/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,10 @@ if (Qt6_FOUND)
FetchContent_MakeAvailable(qtmultimedia)
endif()
endif(Qt6_FOUND)
FetchContent_Declare(
MyDesigns
GIT_REPOSITORY https://github.com/EddyTheCo/MyDesigns.git
GIT_TAG v0.2.3
FIND_PACKAGE_ARGS 0.2 CONFIG
)
FetchContent_MakeAvailable(MyDesigns)

if (Qt6_FOUND AND TARGET QrDec)

if (Qt6_FOUND AND TARGET QrDec)
list(APPEND qml_files "qml/QrTextArea.qml")
if(TARGET Qt6::Multimedia)
list(APPEND qml_files "qml/QrQmlCamera.qml")
else()
Expand Down
66 changes: 39 additions & 27 deletions QtQrDec/Qrimagedecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,33 +27,45 @@ EMSCRIPTEN_BINDINGS(qrdecoder) {
}

EM_JS(void, call_start, (), {
var elemDiv = document.createElement('div');
elemDiv.style.cssText = 'display:none; position:absolute;width:100%;height:100%;';
elemDiv.innerHTML += '<video id="qrvideo" width="500px" height="300px"></video><canvas id="qrcanvas" width="500px" height="300px" ></canvas></div>';
document.body.appendChild(elemDiv);

let video = document.querySelector("#qrvideo");
let canvas = document.querySelector("#qrcanvas");
stream = navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then((stream) => {
video.srcObject = stream;
window.localStream = stream;
video.addEventListener("loadedmetadata", () => {
video.play();
});
getimage=setInterval(function() {
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
let imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
var data=imageData.data.buffer;
var uint8Arr = new Uint8Array(data);
const numBytes = uint8Arr.length * uint8Arr.BYTES_PER_ELEMENT;
const dataPtr = qtClient.module()._malloc(numBytes);
const dataOnHeap = new Uint8Array(qtClient.module().HEAPU8.buffer, dataPtr, numBytes);
dataOnHeap.set(uint8Arr);
qtClient.module().QRImageDecoder.getdecoder().reload(dataOnHeap.byteOffset,video.width,video.height);
qtClient.module()._free(dataPtr);
}, 100);

}).catch(alert);

if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) {
stream = navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, audio: false }).then((stream) => {
let settings = stream.getVideoTracks()[0].getSettings();
let width = settings.width;
let height = settings.height;

if(document.querySelector("#qrvideo")=== null)
{
var elemDiv = document.createElement('div');
elemDiv.style.cssText = 'display:none; position:absolute;width:100%;height:100%;';
elemDiv.innerHTML += '<video controls autoplay id="qrvideo" width="'+width+'px" height="'+height+'px"></video><canvas id="qrcanvas" width="'+width+'px" height="'+height+'px" ></canvas></div>';
document.body.appendChild(elemDiv);
}
let video = document.querySelector("#qrvideo");
let canvas = document.querySelector("#qrcanvas");

video.srcObject = stream;
window.localStream = stream;
getimage=setInterval(function() {
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
let imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
var data=imageData.data.buffer;
var uint8Arr = new Uint8Array(data);
const numBytes = uint8Arr.length * uint8Arr.BYTES_PER_ELEMENT;
const dataPtr = qtClient.module()._malloc(numBytes);
const dataOnHeap = new Uint8Array(qtClient.module().HEAPU8.buffer, dataPtr, numBytes);
dataOnHeap.set(uint8Arr);
qtClient.module().QRImageDecoder.getdecoder().reload(dataOnHeap.byteOffset,video.width,video.height);
qtClient.module()._free(dataPtr);
}, 100);

}).catch(alert);


}




});

Expand Down
58 changes: 56 additions & 2 deletions QtQrDec/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,61 @@
# QtQrDec

This repo produce a library with a qml type object capable of detect and decode QRCodes on a picture.
This is in development, but you could follow the folder [test](tests/) as a use case example.
This repo produce a library that can detect and decode QRCODEs.
The detection and decoding is performed by [OpenCV](https://opencv.org/) libraries.
In case OpenCV is not found on your system CMake will download precompiled libraries from [my action releases](https://github.com/EddyTheCo/install-OpenCV-action).

The library use [QtMultimedia](https://doc.qt.io/qt-6/qtmultimedia-index.html) if not compiling for wasm.
If compiling for wasm the library creates a custom ImageProvider that communicates with the browser.

You can play with the decoder on [this page](https://eddytheco.github.io/qmlonline/?example_url=qt_qr_dec)

An example on how to add the decoder to your project can be found on [this repository](https://github.com/EddyTheCo/qmlonline) (Only the parts enclosed in the USE_QtQr macros).

In general CMake produce the target 'QtQrDec' so one can link to this library like
```
target_link_libraries(<target> <PRIVATE|PUBLIC|INTERFACE> QtQrDec)
```


## Using the decoder on QML aplications will be as simple as
```
QrTextArea
{
id:popup_
width:300
height:425
anchors.centerIn: Overlay.overlay
description: "Get data from QRCODE"
}
```

For this to work one has to add the custom ImageProvider to the QML engine like in the following main.cpp
```
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "Qrimageprovider.hpp"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
#ifdef USE_EMSCRIPTEN
engine.addImageProvider(QLatin1String("wasm"), new WasmImageProvider());
#endif
const QUrl url(u"qrc:/app/main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
```




Expand Down
2 changes: 2 additions & 0 deletions QtQrDec/qml/QrQmlCamera.qml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Item
timer.start();
startcamera.visible=false;
}
width:100
height:width*0.5
}
}

Expand Down
69 changes: 69 additions & 0 deletions QtQrDec/qml/QrTextArea.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import QtQuick 2.0
import QtQuick.Controls
import QtQuick.Layouts
import MyDesigns
import QtQrDec
Popup
{
id:popup_
property string description;
property string placeholder;
signal clicked();

onClosed: qrscanner.stop();
background: Rectangle
{
id:bck
color:CustomStyle.backColor1
border.width:1
border.color:CustomStyle.frontColor1
radius:Math.min(width,height)*0.05
}
ColumnLayout
{
anchors.fill: parent
MyTextArea
{
id:recaddress
label.text: (popup_.description)?popup_.description:""
textarea.placeholderText: (popup_.placeholder)?popup_.placeholder:""
Layout.alignment: Qt.AlignHCenter
Layout.margins: 20
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth:400
Layout.maximumHeight: 200
Layout.minimumHeight: 100
focus:true
}
QrQmlCamera
{
id:qrscanner
Layout.alignment: Qt.AlignCenter
Layout.margins: 5
Layout.fillHeight: true
Layout.fillWidth: true
Layout.minimumWidth:100
Layout.minimumHeight: 200
onGotdata: (data)=> {
qrscanner.stop();
recaddress.textarea.text=data;
}
}
MyButton
{
id:send
Layout.alignment: Qt.AlignRight
Layout.margins: 15
enabled: recaddress.textarea.text!==""
onClicked:
{
popup_.close();
popup_.clicked();
}
text:qsTr("Ok")
}

}
}

28 changes: 25 additions & 3 deletions QtQrGen/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
find_package(Qt6 COMPONENTS Core Gui Qml Quick OPTIONAL_COMPONENTS Svg)
FetchContent_Declare(
MyDesigns
GIT_REPOSITORY https://github.com/EddyTheCo/MyDesigns.git
GIT_TAG v0.2.3
FIND_PACKAGE_ARGS 0.2 CONFIG
)
FetchContent_MakeAvailable(MyDesigns)

if (Qt6_FOUND)
qt_standard_project_setup()
add_library(QtQrGen Qrimageprovider.cpp include/Qrimageprovider.hpp)
qt6_add_qml_module(QtQrGen
URI QtQrGen
VERSION 1.0
SOURCES Qrimageprovider.cpp include/Qrimageprovider.hpp
QML_FILES qml/AddressQr.qml qml/PayQrPop.qml qml/QrLabel.qml
RESOURCE_PREFIX
"/esterVtech.com/imports"
OUTPUT_TARGETS out_targets_var
OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/QtQrGen
IMPORT_PATH ${QML_IMPORT_PATH}
)


add_library(qrCode::QtQrGen ALIAS QtQrGen)
target_include_directories(QtQrGen PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/qrCode>")
if(NOT TARGET Qt6::Svg)
Expand All @@ -26,9 +44,10 @@ target_link_libraries(QtQrGen PRIVATE
Qt6::Gui
Qt6::Qml
Qt6::Svg
MyDesigns
)

install(TARGETS QtQrGen
install(TARGETS QtQrGen ${out_targets_var}
EXPORT qrCodeTargets
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT QtQr
Expand All @@ -42,3 +61,6 @@ if(BUILD_DOCS)
get_target_property(build_docs cmake_build_docs SOURCES)
include(${build_docs})
endif()
list(APPEND QML_IMPORT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/qml ${CMAKE_CURRENT_BINARY_DIR})
list(REMOVE_DUPLICATES QML_IMPORT_PATH)
set(QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE STRING "" FORCE)
8 changes: 8 additions & 0 deletions QtQrGen/Qrimageprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
#include "qrcodegen.hpp"
#include "Qrimageprovider.hpp"
#include <QDebug>

namespace fooQtQrGen
{
QString fooPrint(void)
{
return "fooPrint";
}
}
using namespace qrcodegen;

QPixmap QRImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
Expand Down
2 changes: 1 addition & 1 deletion QtQrGen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This code produce a library with a custom ImageProvider of Qt. The image provider print a QRCODE from a string.
You can play with the resulting ImageProvider on [this page](https://eddytheco.github.io/qmlonline/?example_url=qt_qr_gen).
An example on how to add the ImageProvider to your project can be found on [this repository](https://github.com/EddyTheCo/qmlonline) (Only the parts enclosed in the USE_QtQrGen macros).
An example on how to add the ImageProvider to your project can be found on [this repository](https://github.com/EddyTheCo/qmlonline) (Only the parts enclosed in the USE_QtQr macros).

In general CMake produce the target 'QtQrGen' so one can link to this library like
```
Expand Down
6 changes: 5 additions & 1 deletion QtQrGen/include/Qrimageprovider.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#include <qquickimageprovider.h>



//foo namespace to force the linker to link the backing library composed only of qml files
namespace fooQtQrGen
{
QString fooPrint(void);
};

class QRImageProvider : public QQuickImageProvider
{
Expand Down
46 changes: 46 additions & 0 deletions QtQrGen/qml/AddressQr.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import QtQuick.Controls
import QtQuick
import MyDesigns

Rectangle {
id:root
required property string address
property string url
radius: 10

TextClipboard
{
id:tclip
text:root.address
}

Image {
id:img
anchors.centerIn:parent
sourceSize.width: root.width-10
source: "image://qrcodeblack/"+root.address
MouseArea {
anchors.fill: img
hoverEnabled :true
onEntered: tooltip.visible=!tooltip.visible
onExited: tooltip.visible=!tooltip.visible
onClicked:
{

tclip.copy();
if(root.url)
{
Qt.openUrlExternally(root.url)
}
}
}
}
ToolTip
{
id:tooltip
visible: false
text:qsTr("Copy")
}


}
Loading

0 comments on commit 9093ea6

Please sign in to comment.