Как нарисовать QR-код с Qt в родном C/С++
QR в Qt
Как сопутствующий вопрос Как сканировать QR-коды с Qt, я хочу знать, как нарисовать QR-код из собственного кода C/С++ в моем настольном приложении на основе Qt5, но я не смог найти пример того, как это сделать.
Я знаю QtQR существует, но он имеет зависимости от python-qrtools, который, на мой взгляд, отчасти поражает цель использования Qt в первую очередь. Я хочу проворное, эффективное и бесплатное решение, которое будет компилироваться вместе с моим приложением, где бы я решил его принять.
Как я могу это сделать?
Ответы
Ответ 1
Если вы чувствуете, что библиотека Fukuchi слишком велика [0] для вас, рассмотрите возможность просмотра библиотеки генератора QR-кода Nayuki С++ [1]: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
Библиотека Nayuki требует С++ 11 и переносима без необходимости использования Autotools. Использование примера:
#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 файлов, ~ 7200 строк среди основных .c и .h файлов (исключая сборку и тестовый код).
[1]: Nayuki: 6 файлов, ~ 1400 строк среди основных файлов .cpp и .hpp(исключая демонстрационный код).
EDIT 2016-12-08 by OP
Я решил с разрешения добавить мою собственную адаптацию к Qt. Этот код компилируется и работает отлично в моей системе. И я думаю, что он должен быть достаточно независимым, чтобы работать в другом месте, не слишком много настроек.
#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);
}
}
}
}
Для использования см. этот painter класс.
Ответ 2
ОБНОВЛЕНИЕ 3/3-2016: Мне пришло в голову, что есть небольшой проект библиотеки, который делает то, что мой ответ делает, но более "расфасованным" способом. Вы можете проверить здесь.
QR в Qt
Существует небольшая библиотека генераторов QR-кода в чистом C и без зависимостей, называемая libqrencode.
Шаг 1: установите
Прежде чем вы сможете использовать его, вам придется его установить. На моем Ubuntu 13.10 это означало ввод следующего в оболочке:
sudo aptitude install libqrencode-dev
На других платформах вам, возможно, придется самостоятельно создавать его из источника. Просто загрузите tarball и следуйте инструкциям из загрузки исходного кода.
Шаг 2: Файл проекта
Затем вам нужно будет добавить библиотеку в свой проект. В моем файле проекта Qt5.2.0 (myproject.pro или аналогичном), что означало добавление следующей строки:
LIBS += -lqrencode
Это должно быть похоже на большинство версий Qt, которые я знаю.
Шаг 3: кодирование
Далее нужно написать код, который на самом деле использует библиотеку для кодирования некоторой входной строки в формате QR. Это одна строка кода:
QRcode *qr=QRcode_encodeString("my string", 1, QR_ECLEVEL_L, QR_MODE_8,0);
ПРИМЕЧАНИЕ: После экспериментов с параметрами, которые я передал этой функции, я узнал, что нужно быть осторожным. Некоторые комбинации параметров потерпели неудачу без уважительной причины. Например, передача 0 в качестве версии или с использованием QR_MODE_AN завершилась с "Недопустимыми параметрами". Это могут быть ошибки в древней версии библиотеки, которую я использую. Вы были предупреждены.
Шаг 4: рендеринг изображения
Наконец, перед очисткой вам нужно преобразовать вывод в растровое изображение, чтобы оно отображалось на экране. Это проще, чем кажется. Вместо того, чтобы перечислять кучу предположений, вместо этого я включу свою полную рабочую минималистическую реализацию QRWidget. Интересные биты находятся в переопределенном методе paintEvent().
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;
}
Резюме
В этом небольшом посте я обобщил свой опыт получения генератора QR-кода, работающего с Qt.