29

QR in Qt

As a companion question to How to scan for QR codes with Qt, I want to know how to draw a QR code from native C/C++ code in my Qt5 based desktop app, but I could not find an example of how to do this.

I know QtQR exists, but it has dependencies on python-qrtools which in my opinion kind of defeats the purpose of using Qt in the first place. I want a nimble, efficient and dependency-free solution that will compile with my app wherever I decided to take it.

How can I do that?

Community
  • 1
  • 1
Mr. Developerdude
  • 9,118
  • 10
  • 57
  • 95

3 Answers3

31

UPDATE 3/3-2016: It has come to my attention that there is a small library project that does what my answer does but in a more "prepackaged" way. You can check it out here.

QR in Qt

There is a small QR-code generator library in pure C and without dependencies, called libqrencode.

Step 1: Install

Before you can use it, you will have to install it. On my Ubuntu 13.10 that meant typing the following in a shell:

sudo aptitude install libqrencode-dev

On other platforms you may have to build it from source by yourself. Simply download the tarball and follow the instructions from the source code download.

Step 2: Project file

Next, you will have to add the library to your project. In my Qt5.2.0 project file (myproject.pro or similar) that meant appending the following line:

LIBS += -lqrencode

This should be similar for most versions of Qt that I know.

Step 3: encode

Next one must write the code that actually uses the library to encode some input string to QR format. That is one line of code:

QRcode *qr=QRcode_encodeString("my string", 1, QR_ECLEVEL_L, QR_MODE_8,0);

NOTE: After experimenting with the parameters I have passed to this function, I have learned that one needs to be careful. Some combinations of parameters failed for no good reason. For example passing 0 as version or using QR_MODE_AN failed with "Invalid parameters". This might be bugs in the ancient version of the library that I am using You have been warned.

Step 4: render image

Finally, before cleaning up, you need to convert the output to bitmap so that it can be rendered on the screen. This is simpler than it sounds. Instead of listing a bunch of assumptions I will instead included my complete working minimalistic QRWidget implementation here. The interesting bits are in the overridden paintEvent() method.

QRWidget.hpp

#ifndef QRWIDGET_HPP
#define QRWIDGET_HPP

#include <QWidget>

class QRWidget : public QWidget{
    Q_OBJECT
private:
    QString data;
public:
    explicit QRWidget(QWidget *parent = 0);
    void setQRData(QString data);

protected:
    void paintEvent(QPaintEvent *);
};

#endif // QRWIDGET_HPP

QRWidget.cpp

#include "QRWidget.hpp"
#include <QPainter>
#include <QDebug>    
#include <qrencode.h>

QRWidget::QRWidget(QWidget *parent) :
    QWidget(parent),
    data("Hello QR")//Note: The encoding fails with empty string so I just default to something else. Use the setQRData() call to change this.
{
}

void QRWidget::setQRData(QString data){
    this->data=data;
    update();
}

void QRWidget::paintEvent(QPaintEvent *pe){
    QPainter painter(this);
    //NOTE: I have hardcoded some parameters here that would make more sense as variables.
    QRcode *qr = QRcode_encodeString(data.toStdString().c_str(), 1, QR_ECLEVEL_L, QR_MODE_8, 0);
    if(0!=qr){
        QColor fg("black");
        QColor bg("white");
        painter.setBrush(bg);
        painter.setPen(Qt::NoPen);
        painter.drawRect(0,0,width(),height());
        painter.setBrush(fg);
        const int s=qr->width>0?qr->width:1;
        const double w=width();
        const double h=height();
        const double aspect=w/h;
        const double scale=((aspect>1.0)?h:w)/s;
        for(int y=0;y<s;y++){
            const int yy=y*s;
            for(int x=0;x<s;x++){
                const int xx=yy+x;
                const unsigned char b=qr->data[xx];
                if(b &0x01){
                    const double rx1=x*scale, ry1=y*scale;
                    QRectF r(rx1, ry1, scale, scale);
                    painter.drawRects(&r,1);
                }
            }
        }
        QRcode_free(qr);
    }
    else{
        QColor error("red");
        painter.setBrush(error);
        painter.drawRect(0,0,width(),height());
        qDebug()<<"QR FAIL: "<< strerror(errno);
    }
    qr=0;
}

Summary In this little post I have summarized my experience with getting a QR code generator working with Qt.

Mr. Developerdude
  • 9,118
  • 10
  • 57
  • 95
  • 3
    it is better to implement function `paintQrCode(QPainter &painter, const QRect &area, const QByteArray &data)`. Then this one function could be used to paint on `QWidget` or `QImage` or `QGraphicsWidget` avoiding duplication of code. – Marek R Jan 28 '14 at 09:18
  • 1
    You are correct. I thought about doing it this way, but I felt like simplifying my answer as much as possible (it was a bit large). I might update it later! – Mr. Developerdude Feb 02 '14 at 21:19
  • 1
    You likely want `QRcode *qr = QRcode_encodeString(data.toStdString().c_str(), 1, QR_ECLEVEL_L, QR_MODE_8, 0);` to be `QRcode *qr = QRcode_encodeString(data.toStdString().c_str(), 1, QR_ECLEVEL_L, QR_MODE_8, 1);` to have case-insensitive QR Barcodes – ty812 Jan 29 '15 at 10:51
  • Be careful: that library is LGPL, which is too restrictive for a lot of uses, such as use in an iOS app. – Glenn Maynard Sep 13 '15 at 15:52
18

If you feel that Fukuchi's library is too large[0] for you, consider looking at Nayuki's C++ QR Code generator library[1]: https://github.com/nayuki/QR-Code-generator/tree/master/cpp

Nayuki's library requires C++11, and is portable without needing Autotools. Sample usage:

#include <string>
#include <vector>
#include "QrCode.hpp"
using namespace qrcodegen;

// Create the QR Code object
QrCode qr = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM);

// Read the black & white pixels
for (int y = 0; y < qr.size; y++) {
    for (int x = 0; x < qr.size; x++) {
        int color = qr.getModule(x, y);  // 0 for white, 1 for black

        // You need to modify this part
        draw_pixel_onto_QT(x, y, color);
    }
}

[0]: Fukuchi: 20 files, ~7200 lines among the main .c and .h files (excluding build and test code).
[1]: Nayuki: 6 files, ~1400 lines among the main .cpp and .hpp files (excluding demo code).


EDIT 2016-12-08 by OP I decided, with permission, to add my own adaption to Qt. This code compiles and runs fine on my system, And I think it should be independent enough to work elsewhere without too many tweaks as well.

#include "QrCode.hpp"

void paintQR(QPainter &painter, const QSize sz, const QString &data, QColor fg)
{
    // NOTE: At this point you will use the API to get the encoding and format you want, instead of my hardcoded stuff:
    qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(data.toUtf8().constData(), qrcodegen::QrCode::Ecc::LOW);
    const int s=qr.getSize()>0?qr.getSize():1;
    const double w=sz.width();
    const double h=sz.height();
    const double aspect=w/h;
    const double size=((aspect>1.0)?h:w);
    const double scale=size/(s+2);
    // NOTE: For performance reasons my implementation only draws the foreground parts in supplied color.
    // It expects background to be prepared already (in white or whatever is preferred).
    painter.setPen(Qt::NoPen);
    painter.setBrush(fg);
    for(int y=0; y<s; y++) {
        for(int x=0; x<s; x++) {
            const int color=qr.getModule(x, y);  // 0 for white, 1 for black
            if(0!=color) {
                const double rx1=(x+1)*scale, ry1=(y+1)*scale;
                QRectF r(rx1, ry1, scale, scale);
                painter.drawRects(&r,1);
            }
        }
    }
}

For usage, please see this painter class.

parsley72
  • 8,449
  • 8
  • 65
  • 98
Nayuki
  • 17,911
  • 6
  • 53
  • 80
  • 1
    Hi! I integrated your QR Code into my project, and if you want I can edit your answer with the Qt specific code I made, that way it will be a better fit for my question? – Mr. Developerdude Dec 07 '16 at 23:58
  • If you have the actual Qt code that can compile and run, I would be very happy to receive the edit! – Nayuki Dec 08 '16 at 00:20
  • I tried using your library, and i get the QrCode drawing, but when i scan it, its not recognized, the scanner doesn't get anything... – Xsmael Mar 22 '17 at 22:53
  • @Xsmael Please post your input text and a picture of the QR Code you generated. You may comment on this thread, email me privately, or start a new Stack Overflow question. I will test and debug your case. – Nayuki Mar 23 '17 at 01:32
  • @Nayuki, thank you for your help!, i made a new post here: http://stackoverflow.com/questions/42979325/drawing-qr-code-with-qt-in-native-c-c-using – Xsmael Mar 23 '17 at 14:43
  • @LennartRolland: `char *str=data.toUtf8().data();` - I don't think this is safe. The QByteArray returned by toUtf8() is destroyed at the end of the full-expression, so the char* representation of it becomes a dangling pointer. – Stefan Monov Oct 05 '17 at 10:18
  • @LennartRolland: When can `qr.getSize()` be <= 0? – Stefan Monov Oct 05 '17 at 10:50
  • @StefanMonov: Thanks for uncovering that, and sorry for nt replying sooner, I first saw your comment now. I will update it to be safer! – Mr. Developerdude Jan 27 '18 at 02:06
  • I am evaluating @Nayuki's QR library. The JavaScript demo on their site looks attractive, however, I am unable to find 'Scale: pixels per module' option in C version. Is it supported? and if so how. Thanks. – FractalSpace Apr 14 '21 at 14:48
  • 1
    @FractalSpace You will need to do the pixel scaling by yourself in C. You can take ideas from my relevant [Java function](https://github.com/nayuki/QR-Code-generator/blob/8518684c0f33d004fa93971be2c6a8eca3167d1e/java/src/main/java/io/nayuki/qrcodegen/QrCode.java#L288-L313) and [JavaScript function](https://github.com/nayuki/QR-Code-generator/blob/8518684c0f33d004fa93971be2c6a8eca3167d1e/typescript-javascript/qrcodegen.ts#L244-L261). – Nayuki Apr 15 '21 at 01:13
  • Thanks @Nayuki. I will take a look. – FractalSpace Apr 15 '21 at 12:49
4

The following is Qt code I used to update a label (qrCode) on a form with the QR code for "text". The label is set to a fixed size (min and max width and height=256 in my case), and scaledContents true. You might do something more efficient than the RGB32 format, but it really shouldn't matter for occasional updates.

void MyClass::updateQrCode( QString text )
{ using namespace qrcodegen;
  // Create the QR Code object
  QrCode qr = QrCode::encodeText( text.toUtf8().data(), QrCode::Ecc::MEDIUM );
  qint32 sz = qr.getSize();
  QImage im(sz,sz, QImage::Format_RGB32);
    QRgb black = qRgb(  0,  0,  0);
    QRgb white = qRgb(255,255,255);
  for (int y = 0; y < sz; y++)
    for (int x = 0; x < sz; x++)
      im.setPixel(x,y,qr.getModule(x, y) ? black : white );
  ui->qrCode->setPixmap( QPixmap::fromImage(im.scaled(256,256,Qt::KeepAspectRatio,Qt::FastTransformation),Qt::MonoOnly) );
}

using the Nayuki library - just the QrCode.cpp and .hpp files from here: https://github.com/nayuki/QR-Code-generator/tree/master/cpp

incorporated in my project with this trivial .pri file (kept in the same folder as the QrCode files):

INCLUDEPATH += $$PWD
DEPENDPATH  += $$PWD

HEADERS += $$PWD/QrCode.hpp

SOURCES += $$PWD/QrCode.cpp
MangoCat
  • 89
  • 5