Search code examples
c++wxwidgetsqr-code

C++ wxWidgets display and generate QR Code


How can I setup and display a custom QR Code in wxWidgets, to encode and display a string?

(Self answered, feel free to contribute better answers)


Solution

  • Using the wxImagePanel example, and the nayuki/QR-Code-generator C++ library, you can pass a string and scale and the QR Code will generate and show.

    The files created are:

    wxImagePanel.hpp
    wxImagePanel.cpp
    wxQRCode.hpp
    wxQRCode.cpp
    

    First is my cut down wxImagePanel, a widget which lets you set and display images on a rectangular surface:

    // wxImagePanel.hpp
    
    #include <wx/wx.h>
    
    class wxImagePanel : public wxPanel {
        private:
            wxBitmap image;
            void updateSize();
    
        public:
            wxImagePanel(wxFrame *parent, wxBitmap bitmap);
            wxImagePanel(wxFrame *parent);
            void paintNow();
            void paintEvent(wxPaintEvent &evt);
            void render(wxDC &dc);
            void setBitmap(wxBitmap bitmap);
    
            DECLARE_EVENT_TABLE()
    };
    
    // wxImagePanel.cpp
    
    #include "wxImagePanel.hpp"
    
    BEGIN_EVENT_TABLE(wxImagePanel, wxPanel)
    EVT_PAINT(wxImagePanel::paintEvent)
    END_EVENT_TABLE()
    
    wxImagePanel::wxImagePanel(wxFrame *parent, wxBitmap bitmap): wxPanel(parent), image(bitmap) 
    {
        updateSize();
    };
    
    wxImagePanel::wxImagePanel(wxFrame *parent): wxPanel(parent) {};
    
    void wxImagePanel::updateSize() 
    {
        SetMinSize(wxSize(image.GetWidth(), image.GetHeight()));
    }
    
    void wxImagePanel::paintEvent(wxPaintEvent &evt)
    {
        wxPaintDC dc(this);
        render(dc);
    }
    
    void wxImagePanel::render(wxDC &dc) {
        int x = (GetSize().GetWidth() - image.GetWidth()) / 2;
        int y = (GetSize().GetHeight() - image.GetHeight()) / 2;
        dc.DrawBitmap(image, x, y, false);
    }
    
    void wxImagePanel::paintNow() {
        wxClientDC dc(this);
        render(dc);
    }
    
    void wxImagePanel::setBitmap(wxBitmap bitmap) {
        image = bitmap;
        updateSize();
        paintNow();
    }
    

    Then a QR Code class which subclasses the wxImagePanel and shows the code:

    // wxQRCode.hpp
    
    #include "wxImagePanel.hpp"
    #include <string>
    
    class wxQRCode : public wxImagePanel 
    {
        public:
            wxQRCode(wxFrame *parent, std::string text, unsigned int scale);
            void setText(std::string text);
            void setScale(int scale);
            int getQRSize();
    
        private:
            void generateQRBitmap();
            std::string qrText;
            unsigned int bitmapScale;
    };
    
    // wxQRCode.cpp
    
    #include "wxQRCode.hpp"
    #include "QrCode.hpp" // qr code lib
    using namespace qrcodegen;
    
    wxQRCode::wxQRCode(wxFrame *parent, std::string text, unsigned int scale)
        : wxImagePanel(parent), qrText(text), bitmapScale(scale)
    {
        generateQRBitmap();
    };
    
    int wxQRCode::getQRSize()
    {
        QrCode qr = QrCode::encodeText(qrText.c_str(), QrCode::Ecc::LOW);
        return qr.getSize();
    }
    
    void wxQRCode::generateQRBitmap() 
    {
        QrCode qr = QrCode::encodeText(qrText.c_str(), QrCode::Ecc::LOW);
        const int scale = bitmapScale;
        const int size = qr.getSize() * scale;
        const int byteWidth = (size + 7) / 8;
        char bitsChar[size*byteWidth];
    
        for (int y=0; y<size; y++) {
            for (int xByte=0; xByte<byteWidth; xByte++) {
                char bitChar = 0x00;
                if (qrText != "") { for (int xBit=0; xBit<8; xBit++) {
                        int x = xByte*8 + xBit;
                        int xModule = x / scale;
                        int yModule = y / scale;
                        bool black = qr.getModule(xModule, yModule);
                        bitChar += black << (xBit % 8);
                    }}
                bitsChar[y * byteWidth + xByte] = bitChar;
            }
        }
        
        wxBitmap *bitmap = new wxBitmap(bitsChar, size, size, 1);
        setBitmap(*bitmap);
    };
    
    void wxQRCode::setText(std::string text) {
        qrText = text;
        if (text != "") {
            Show(true);
            generateQRBitmap();
            paintNow();
        } else {
            Show(false);
        }
    };
    
    void wxQRCode::setScale(int scale) {
        bitmapScale = scale;
        generateQRBitmap();
        paintNow();
    };