一、解决方案架构
1.1 技术选型比较
| 方案 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| qrcode (nayuki) | 纯C++, 轻量, 无依赖 | 功能基础 | ★★★★★ |
| QZXing | 功能强大, 支持多种格式 | 体积大, 依赖多 | ★★★★ |
| libqrencode | 性能好, 广泛使用 | 需要额外编译 | ★★★★ |
| ZXing-C++ | Google出品, 功能全 | 编译复杂 | ★★★ |
推荐方案:qrcode (nayuki) - 最适合Qt集成
二、核心实现方案
2.1 方案一:使用 qrcode (nayuki) - 推荐
这是最轻量、最易集成的方案,纯C++实现,无任何依赖。
2.1.1 下载qrcode库
bash
# 下载qrcode库
git clone https://github.com/nayuki/QR-Code-generator.git
2.1.2 Qt项目集成
文件结构:
QRCodeDemo/
├── main.cpp
├── QRCodeWidget.h
├── QRCodeWidget.cpp
├── qrcode/
│ ├── BitBuffer.hpp
│ ├── QrCode.hpp
│ ├── QrSegment.hpp
│ └── README.md
└── QRCodeDemo.pro
QRCodeWidget.h:
cpp
#ifndef QRCODEWIDGET_H
#define QRCODEWIDGET_H
#include <QWidget>
#include <QImage>
#include <QString>
#include <QPen>
#include <QBrush>
class QRCodeWidget : public QWidget
{
Q_OBJECT
public:
explicit QRCodeWidget(QWidget *parent = nullptr);
// 生成二维码
bool generateQRCode(const QString& text,
int version = 5, // 二维码版本(1-40)
int eccLevel = 1, // 纠错等级(0-3)
int border = 4); // 边距
// 获取二维码图片
QImage getQRCodeImage() const { return m_qrCodeImage; }
// 保存二维码图片
bool saveToFile(const QString& filename,
int scale = 4, // 缩放倍数
const QString& format = "PNG");
// 设置样式
void setForegroundColor(const QColor& color) { m_foregroundColor = color; }
void setBackgroundColor(const QColor& color) { m_backgroundColor = color; }
void setDrawStyle(bool rounded = false, bool useGradient = false);
// 添加Logo
bool addLogo(const QImage& logo, int logoSizePercentage = 20);
// 二维码信息
int getVersion() const { return m_version; }
int getEccLevel() const { return m_eccLevel; }
QString getEccLevelName() const;
int getSize() const { return m_qrSize; }
protected:
void paintEvent(QPaintEvent *event) override;
signals:
void qrCodeGenerated(bool success, const QString& error = QString());
private:
// 绘制二维码
void drawQRCode(QPainter& painter, int x, int y, int size);
// 绘制Logo
void drawLogo(QPainter& painter, int x, int y, int size);
// 计算版本容量
static int getMaxCapacity(int version, int eccLevel);
private:
QImage m_qrCodeImage;
QImage m_logoImage;
QColor m_foregroundColor = Qt::black;
QColor m_backgroundColor = Qt::white;
int m_version = 5;
int m_eccLevel = 1; // 1 = LOW, 0 = MEDIUM, 2 = QUARTILE, 3 = HIGH
int m_border = 4;
int m_qrSize = 0;
bool m_roundedModules = false;
bool m_useGradient = false;
std::vector<uint8_t> m_qrData;
};
#endif // QRCODEWIDGET_H
QRCodeWidget.cpp:
cpp
#include "QRCodeWidget.h"
#include "qrcode/QrCode.hpp"
#include <QPainter>
#include <QPainterPath>
#include <QLinearGradient>
#include <QFile>
#include <QDebug>
using qrcodegen::QrCode;
using qrcodegen::QrSegment;
QRCodeWidget::QRCodeWidget(QWidget *parent)
: QWidget(parent) {
setMinimumSize(200, 200);
}
bool QRCodeWidget::generateQRCode(const QString& text, int version, int eccLevel, int border) {
if (text.isEmpty()) {
emit qrCodeGenerated(false, "文本不能为空");
return false;
}
if (version < 1 || version > 40) {
emit qrCodeGenerated(false, "版本必须在1-40之间");
return false;
}
if (eccLevel < 0 || eccLevel > 3) {
emit qrCodeGenerated(false, "纠错等级必须在0-3之间");
return false;
}
m_version = version;
m_eccLevel = eccLevel;
m_border = border;
try {
// 将QString转换为UTF-8字节
QByteArray utf8Data = text.toUtf8();
std::vector<uint8_t> data(utf8Data.begin(), utf8Data.end());
// 创建QR码
QrCode::Ecc ecc = static_cast<QrCode::Ecc>(eccLevel);
QrCode qr = QrCode::encodeBytes(data, ecc, version, version);
m_qrSize = qr.getSize();
m_qrData.clear();
// 复制二维码数据
for (int y = 0; y < m_qrSize; y++) {
for (int x = 0; x < m_qrSize; x++) {
m_qrData.push_back(qr.getModule(x, y) ? 1 : 0);
}
}
// 创建图片
int imgSize = m_qrSize + 2 * m_border;
m_qrCodeImage = QImage(imgSize, imgSize, QImage::Format_ARGB32);
m_qrCodeImage.fill(m_backgroundColor);
QPainter painter(&m_qrCodeImage);
painter.setRenderHint(QPainter::Antialiasing);
drawQRCode(painter, m_border, m_border, m_qrSize);
// 如果有Logo,绘制Logo
if (!m_logoImage.isNull()) {
drawLogo(painter, m_border, m_border, m_qrSize);
}
painter.end();
update(); // 触发重绘
emit qrCodeGenerated(true);
return true;
} catch (const std::exception& e) {
qWarning() << "生成二维码失败:" << e.what();
emit qrCodeGenerated(false, QString("生成失败: %1").arg(e.what()));
return false;
}
}
void QRCodeWidget::drawQRCode(QPainter& painter, int x, int y, int size) {
int moduleSize = size / m_qrSize;
painter.setPen(Qt::NoPen);
if (m_useGradient) {
QLinearGradient gradient(x, y, x + size, y + size);
gradient.setColorAt(0, m_foregroundColor);
gradient.setColorAt(1, m_foregroundColor.darker(150));
painter.setBrush(gradient);
} else {
painter.setBrush(m_foregroundColor);
}
for (int row = 0; row < m_qrSize; row++) {
for (int col = 0; col < m_qrSize; col++) {
if (m_qrData[row * m_qrSize + col]) {
int px = x + col * moduleSize;
int py = y + row * moduleSize;
if (m_roundedModules) {
painter.drawRoundedRect(px, py, moduleSize, moduleSize,
moduleSize / 3, moduleSize / 3);
} else {
painter.drawRect(px, py, moduleSize, moduleSize);
}
}
}
}
}
void QRCodeWidget::drawLogo(QPainter& painter, int x, int y, int size) {
if (m_logoImage.isNull()) return;
int logoSize = size * 0.2; // Logo大小为二维码的20%
int centerX = x + (size - logoSize) / 2;
int centerY = y + (size - logoSize) / 2;
QPixmap logoPixmap = QPixmap::fromImage(m_logoImage)
.scaled(logoSize, logoSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
// 绘制白色背景
painter.setBrush(Qt::white);
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(centerX - 2, centerY - 2,
logoSize + 4, logoSize + 4, 8, 8);
// 绘制Logo
painter.drawPixmap(centerX, centerY, logoPixmap);
}
bool QRCodeWidget::saveToFile(const QString& filename, int scale, const QString& format) {
if (m_qrCodeImage.isNull()) {
return false;
}
QImage scaledImage = m_qrCodeImage.scaled(
m_qrCodeImage.width() * scale,
m_qrCodeImage.height() * scale,
Qt::KeepAspectRatio,
Qt::SmoothTransformation
);
return scaledImage.save(filename, format.toUpper().toUtf8());
}
void QRCodeWidget::setDrawStyle(bool rounded, bool useGradient) {
m_roundedModules = rounded;
m_useGradient = useGradient;
update();
}
bool QRCodeWidget::addLogo(const QImage& logo, int logoSizePercentage) {
if (logo.isNull()) {
m_logoImage = QImage();
return false;
}
m_logoImage = logo;
update();
return true;
}
QString QRCodeWidget::getEccLevelName() const {
switch (m_eccLevel) {
case 0: return "LOW (7%)";
case 1: return "MEDIUM (15%)";
case 2: return "QUARTILE (25%)";
case 3: return "HIGH (30%)";
default: return "UNKNOWN";
}
}
int QRCodeWidget::getMaxCapacity(int version, int eccLevel) {
// 简化的容量表(数字模式)
static const int capacityTable[4][4] = {
{17, 32, 53, 78}, // 版本1
{32, 61, 100, 127}, // 版本2
{53, 101, 165, 198}, // 版本3
{78, 127, 198, 242} // 版本4
};
if (version >= 1 && version <= 4 && eccLevel >= 0 && eccLevel <= 3) {
return capacityTable[version-1][eccLevel];
}
return 0;
}
void QRCodeWidget::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
if (!m_qrCodeImage.isNull()) {
// 居中显示
int x = (width() - m_qrCodeImage.width()) / 2;
int y = (height() - m_qrCodeImage.height()) / 2;
painter.drawImage(x, y, m_qrCodeImage);
} else {
// 显示提示
painter.setPen(Qt::gray);
painter.drawText(rect(), Qt::AlignCenter, "没有二维码数据");
}
}
main.cpp:
cpp
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 设置应用程序信息
app.setApplicationName("QR Code Generator");
app.setOrganizationName("MyCompany");
app.setApplicationVersion("1.0.0");
MainWindow window;
window.show();
return app.exec();
}
2.2 方案二:使用QZXing库
QZXing是Qt的二维码处理库,支持生成和解码。
2.2.1 安装QZXing
bash
# 方法1:通过vcpkg
vcpkg install qzxing
# 方法2:手动编译
git clone https://github.com/ftylitak/qzxing.git
cd qzxing
mkdir build && cd build
cmake ..
make
make install
2.2.2 使用QZXing生成二维码
cpp
// QZXingGenerator.h
#ifndef QZXINGGENERATOR_H
#define QZXINGGENERATOR_H
#include <QObject>
#include <QImage>
#include <QColor>
#include <QZXing.h>
class QZXingGenerator : public QObject
{
Q_OBJECT
public:
explicit QZXingGenerator(QObject *parent = nullptr);
// 生成二维码
QImage generateQRCode(const QString& data,
const QSize& size = QSize(200, 200),
const QColor& foreground = Qt::black,
const QColor& background = Qt::white,
QZXing::EncoderCorrectionLevel correction = QZXing::EncodeErrorCorrectionLevel_M);
// 生成带Logo的二维码
QImage generateQRCodeWithLogo(const QString& data,
const QImage& logo,
const QSize& size = QSize(200, 200));
// 批量生成
QList<QImage> batchGenerate(const QStringList& dataList,
const QSize& size = QSize(200, 200));
// 保存到文件
bool saveQRCode(const QString& data,
const QString& filename,
const QSize& size = QSize(200, 200));
signals:
void generationComplete(const QImage& qrCode);
void generationError(const QString& error);
private:
QZXing m_qzxing;
};
#endif // QZXINGGENERATOR_H
cpp
// QZXingGenerator.cpp
#include "QZXingGenerator.h"
#include <QPainter>
#include <QBuffer>
#include <QDebug>
QZXingGenerator::QZXingGenerator(QObject *parent)
: QObject(parent) {
// 初始化QZXing
m_qzxing.setDecoder(QZXing::DecoderFormat_QR_CODE);
}
QImage QZXingGenerator::generateQRCode(const QString& data,
const QSize& size,
const QColor& foreground,
const QColor& background,
QZXing::EncoderCorrectionLevel correction) {
if (data.isEmpty()) {
emit generationError("数据不能为空");
return QImage();
}
try {
// 设置编码参数
QZXing::EncodeErrorCorrectionLevel eccLevel = correction;
// 生成二维码
QImage qrCode = m_qzxing.encodeData(data, size, foreground, background, eccLevel);
if (qrCode.isNull()) {
emit generationError("生成二维码失败");
return QImage();
}
emit generationComplete(qrCode);
return qrCode;
} catch (const std::exception& e) {
emit generationError(QString("生成错误: %1").arg(e.what()));
return QImage();
}
}
QImage QZXingGenerator::generateQRCodeWithLogo(const QString& data,
const QImage& logo,
const QSize& size) {
// 先生成基本二维码
QImage qrCode = generateQRCode(data, size);
if (qrCode.isNull() || logo.isNull()) {
return qrCode;
}
// 在二维码中心添加Logo
QImage result = qrCode.copy();
QPainter painter(&result);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
// 计算Logo位置和大小
int logoSize = qMin(size.width(), size.height()) * 0.2; // Logo大小为20%
int x = (size.width() - logoSize) / 2;
int y = (size.height() - logoSize) / 2;
// 绘制白色背景
painter.setBrush(Qt::white);
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(x-2, y-2, logoSize+4, logoSize+4, 5, 5);
// 绘制Logo
QPixmap scaledLogo = QPixmap::fromImage(logo)
.scaled(logoSize, logoSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
painter.drawPixmap(x, y, scaledLogo);
return result;
}
QList<QImage> QZXingGenerator::batchGenerate(const QStringList& dataList,
const QSize& size) {
QList<QImage> result;
for (const QString& data : dataList) {
QImage qrCode = generateQRCode(data, size);
if (!qrCode.isNull()) {
result.append(qrCode);
}
}
return result;
}
bool QZXingGenerator::saveQRCode(const QString& data,
const QString& filename,
const QSize& size) {
QImage qrCode = generateQRCode(data, size);
if (qrCode.isNull()) {
return false;
}
return qrCode.save(filename);
}
2.3 方案三:使用libqrencode
这是一个C语言的二维码生成库,性能优秀。
2.3.1 安装libqrencode
bash
# Ubuntu/Debian
sudo apt-get install libqrencode-dev
# macOS
brew install qrencode
# Windows (vcpkg)
vcpkg install qrencode
2.3.2 libqrencode封装
cpp
// LibQRencodeWrapper.h
#ifndef LIBQRENCODEWRAPPER_H
#define LIBQRENCODEWRAPPER_H
#include <QImage>
#include <QString>
#include <qrencode.h>
class LibQRencodeWrapper
{
public:
enum QRCorrectionLevel {
QR_ECLEVEL_L = 0, // 7% 纠错
QR_ECLEVEL_M = 1, // 15% 纠错
QR_ECLEVEL_Q = 2, // 25% 纠错
QR_ECLEVEL_H = 3 // 30% 纠错
};
struct QRCodeOptions {
int version = 0; // 0 = 自动选择
QRCorrectionLevel level = QR_ECLEVEL_M;
int margin = 4; // 边距
int scale = 4; // 缩放倍数
bool caseSensitive = true; // 大小写敏感
};
static QImage generateQRCode(const QString& text,
const QRCodeOptions& options = QRCodeOptions());
static QString getLibraryVersion();
static int getMaxVersion() { return 40; }
static int getMinVersion() { return 1; }
private:
static QRcode* encodeString(const QString& text,
const QRCodeOptions& options);
};
#endif // LIBQRENCODEWRAPPER_H
cpp
// LibQRencodeWrapper.cpp
#include "LibQRencodeWrapper.h"
#include <QPainter>
#include <QDebug>
QImage LibQRencodeWrapper::generateQRCode(const QString& text,
const QRCodeOptions& options) {
if (text.isEmpty()) {
return QImage();
}
QRcode* qr = encodeString(text, options);
if (!qr) {
qWarning() << "Failed to generate QR code";
return QImage();
}
// 计算图片大小
int qrSize = qr->width;
int imageSize = (qrSize + 2 * options.margin) * options.scale;
QImage image(imageSize, imageSize, QImage::Format_ARGB32);
image.fill(Qt::white);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(Qt::black);
painter.setPen(Qt::NoPen);
// 绘制二维码
for (int y = 0; y < qrSize; y++) {
for (int x = 0; x < qrSize; x++) {
if (qr->data[y * qrSize + x] & 1) {
int px = (options.margin + x) * options.scale;
int py = (options.margin + y) * options.scale;
painter.drawRect(px, py, options.scale, options.scale);
}
}
}
painter.end();
QRcode_free(qr);
return image;
}
QRcode* LibQRencodeWrapper::encodeString(const QString& text,
const QRCodeOptions& options) {
QRecLevel level = static_cast<QRecLevel>(options.level);
QRecLevel* hint = options.caseSensitive ? QR_MODE_8 : QR_MODE_KANJI;
QRcode* qr = nullptr;
if (options.version > 0) {
// 指定版本
qr = QRcode_encodeString(text.toUtf8().constData(),
options.version,
level,
hint,
1);
} else {
// 自动选择版本
qr = QRcode_encodeString(text.toUtf8().constData(),
0,
level,
hint,
1);
}
return qr;
}
QString LibQRencodeWrapper::getLibraryVersion() {
return QString("libqrencode %1").arg(qrencode_version());
}
三、功能丰富的二维码生成器
3.1 主窗口设计
cpp
// MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QImage>
#include "QRCodeWidget.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onGenerateClicked();
void onSaveClicked();
void onClearClicked();
void onAddLogoClicked();
void onRemoveLogoClicked();
void onTextChanged();
void onStyleChanged();
void onQRCodeGenerated(bool success, const QString& error = QString());
void onUpdatePreview();
private:
void setupUI();
void setupConnections();
void updateCapacityInfo();
void loadSettings();
void saveSettings();
private:
Ui::MainWindow *ui;
QRCodeWidget *m_qrCodeWidget;
QImage m_logoImage;
QTimer *m_previewTimer;
};
#endif // MAINWINDOW_H
cpp
// MainWindow.cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QSettings>
#include <QTimer>
#include <QClipboard>
#include <QApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_qrCodeWidget(new QRCodeWidget(this))
, m_previewTimer(new QTimer(this)) {
ui->setupUI(this);
setupUI();
setupConnections();
loadSettings();
m_previewTimer->setSingleShot(true);
m_previewTimer->setInterval(500); // 500ms延迟预览
}
MainWindow::~MainWindow() {
saveSettings();
delete ui;
}
void MainWindow::setupUI() {
// 设置中心窗口
setCentralWidget(m_qrCodeWidget);
// 创建工具栏
QToolBar *toolBar = addToolBar("Main");
toolBar->setMovable(false);
toolBar->addAction("生成", this, &MainWindow::onGenerateClicked);
toolBar->addAction("保存", this, &MainWindow::onSaveClicked);
toolBar->addAction("清除", this, &MainWindow::onClearClicked);
toolBar->addSeparator();
toolBar->addAction("添加Logo", this, &MainWindow::onAddLogoClicked);
toolBar->addAction("移除Logo", this, &MainWindow::onRemoveLogoClicked);
// 创建状态栏
statusBar()->showMessage("就绪");
// 初始化控件
for (int i = 1; i <= 10; i++) {
ui->comboVersion->addItem(QString("V%1").arg(i), i);
}
ui->comboVersion->setCurrentIndex(4); // 默认V5
ui->comboECC->addItem("LOW (7%)", 0);
ui->comboECC->addItem("MEDIUM (15%)", 1);
ui->comboECC->addItem("QUARTILE (25%)", 2);
ui->comboECC->addItem("HIGH (30%)", 3);
ui->comboECC->setCurrentIndex(1); // 默认MEDIUM
ui->spinBorder->setValue(4);
ui->spinScale->setValue(4);
updateCapacityInfo();
}
void MainWindow::setupConnections() {
connect(ui->btnGenerate, &QPushButton::clicked,
this, &MainWindow::onGenerateClicked);
connect(ui->btnSave, &QPushButton::clicked,
this, &MainWindow::onSaveClicked);
connect(ui->btnClear, &QPushButton::clicked,
this, &MainWindow::onClearClicked);
connect(ui->btnAddLogo, &QPushButton::clicked,
this, &MainWindow::onAddLogoClicked);
connect(ui->btnRemoveLogo, &QPushButton::clicked,
this, &MainWindow::onRemoveLogoClicked);
connect(ui->textContent, &QTextEdit::textChanged,
this, &MainWindow::onTextChanged);
connect(ui->checkRounded, &QCheckBox::toggled,
this, &MainWindow::onStyleChanged);
connect(ui->checkGradient, &QCheckBox::toggled,
this, &MainWindow::onStyleChanged);
connect(ui->btnCopy, &QPushButton::clicked, {
QApplication::clipboard()->setPixmap(
QPixmap::fromImage(m_qrCodeWidget->getQRCodeImage())
);
statusBar()->showMessage("二维码已复制到剪贴板", 2000);
});
connect(m_qrCodeWidget, &QRCodeWidget::qrCodeGenerated,
this, &MainWindow::onQRCodeGenerated);
connect(m_previewTimer, &QTimer::timeout,
this, &MainWindow::onUpdatePreview);
}
void MainWindow::onGenerateClicked() {
QString text = ui->textContent->toPlainText().trimmed();
if (text.isEmpty()) {
QMessageBox::warning(this, "警告", "请输入要编码的内容");
return;
}
int version = ui->comboVersion->currentData().toInt();
int eccLevel = ui->comboECC->currentData().toInt();
int border = ui->spinBorder->value();
// 检查内容长度是否超过容量限制
int maxCapacity = m_qrCodeWidget->getMaxCapacity(version, eccLevel);
if (text.length() > maxCapacity) {
QMessageBox::warning(this, "警告",
QString("内容过长!最大容量为%1字符,当前%2字符")
.arg(maxCapacity).arg(text.length()));
return;
}
statusBar()->showMessage("正在生成二维码...");
m_qrCodeWidget->generateQRCode(text, version, eccLevel, border);
}
void MainWindow::onSaveClicked() {
if (m_qrCodeWidget->getQRCodeImage().isNull()) {
QMessageBox::warning(this, "警告", "没有可保存的二维码");
return;
}
QString filter = "PNG 图片 (*.png);;JPEG 图片 (*.jpg *.jpeg);;BMP 图片 (*.bmp)";
QString filename = QFileDialog::getSaveFileName(this, "保存二维码",
"qrcode.png", filter);
if (!filename.isEmpty()) {
int scale = ui->spinScale->value();
QString format = QFileInfo(filename).suffix().toUpper();
if (m_qrCodeWidget->saveToFile(filename, scale, format)) {
statusBar()->showMessage(QString("保存成功: %1").arg(filename), 3000);
} else {
QMessageBox::critical(this, "错误", "保存失败");
}
}
}
void MainWindow::onClearClicked() {
ui->textContent->clear();
m_qrCodeWidget->addLogo(QImage()); // 移除Logo
statusBar()->showMessage("已清除", 2000);
}
void MainWindow::onAddLogoClicked() {
QString filename = QFileDialog::getOpenFileName(this, "选择Logo图片",
"",
"图片文件 (*.png *.jpg *.jpeg *.bmp)");
if (!filename.isEmpty()) {
m_logoImage.load(filename);
if (m_logoImage.isNull()) {
QMessageBox::warning(this, "警告", "无法加载图片");
return;
}
m_qrCodeWidget->addLogo(m_logoImage, ui->spinLogoSize->value());
statusBar()->showMessage("Logo已添加", 2000);
}
}
void MainWindow::onRemoveLogoClicked() {
m_logoImage = QImage();
m_qrCodeWidget->addLogo(QImage()); // 移除Logo
statusBar()->showMessage("Logo已移除", 2000);
}
void MainWindow::onTextChanged() {
// 延迟预览
m_previewTimer->start();
updateCapacityInfo();
}
void MainWindow::onStyleChanged() {
bool rounded = ui->checkRounded->isChecked();
bool gradient = ui->checkGradient->isChecked();
m_qrCodeWidget->setDrawStyle(rounded, gradient);
}
void MainWindow::onQRCodeGenerated(bool success, const QString& error) {
if (success) {
QString info = QString("版本: V%1, 纠错: %2, 尺寸: %3×%3")
.arg(m_qrCodeWidget->getVersion())
.arg(m_qrCodeWidget->getEccLevelName())
.arg(m_qrCodeWidget->getSize());
statusBar()->showMessage(info);
} else {
QMessageBox::critical(this, "错误", error);
statusBar()->showMessage("生成失败");
}
}
void MainWindow::onUpdatePreview() {
if (ui->checkAutoPreview->isChecked()) {
onGenerateClicked();
}
}
void MainWindow::updateCapacityInfo() {
int version = ui->comboVersion->currentData().toInt();
int eccLevel = ui->comboECC->currentData().toInt();
int currentLength = ui->textContent->toPlainText().length();
int maxCapacity = m_qrCodeWidget->getMaxCapacity(version, eccLevel);
QString info = QString("长度: %1 / %2").arg(currentLength).arg(maxCapacity);
ui->labelCapacity->setText(info);
// 进度条显示
int percent = qMin(100, (currentLength * 100) / maxCapacity);
ui->progressCapacity->setValue(percent);
// 颜色指示
if (percent > 90) {
ui->progressCapacity->setStyleSheet("QProgressBar::chunk { background-color: red; }");
} else if (percent > 70) {
ui->progressCapacity->setStyleSheet("QProgressBar::chunk { background-color: orange; }");
} else {
ui->progressCapacity->setStyleSheet("");
}
}
void MainWindow::loadSettings() {
QSettings settings;
// 加载窗口位置和大小
if (settings.contains("geometry")) {
restoreGeometry(settings.value("geometry").toByteArray());
}
// 加载上次的内容
QString lastContent = settings.value("lastContent").toString();
if (!lastContent.isEmpty()) {
ui->textContent->setPlainText(lastContent);
}
}
void MainWindow::saveSettings() {
QSettings settings;
// 保存窗口位置和大小
settings.setValue("geometry", saveGeometry());
// 保存当前内容
settings.setValue("lastContent", ui->textContent->toPlainText());
}
3.2 UI界面设计 (ui_MainWindow.h)
cpp
// UI设计文件 (通过Qt Designer创建)
#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QWidget>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QSpinBox>
#include <QtWidgets/QLabel>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QProgressBar>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QDockWidget>
class Ui_MainWindow {
public:
QWidget *centralWidget;
QVBoxLayout *verticalLayout;
QHBoxLayout *horizontalLayout;
QVBoxLayout *leftLayout;
QTextEdit *textContent;
QHBoxLayout *buttonLayout;
QPushButton *btnGenerate;
QPushButton *btnSave;
QPushButton *btnClear;
QPushButton *btnCopy;
QVBoxLayout *rightLayout;
QGroupBox *groupSettings;
QVBoxLayout *settingsLayout;
QHBoxLayout *layoutVersion;
QLabel *labelVersion;
QComboBox *comboVersion;
QHBoxLayout *layoutECC;
QLabel *labelECC;
QComboBox *comboECC;
QHBoxLayout *layoutBorder;
QLabel *labelBorder;
QSpinBox *spinBorder;
QHBoxLayout *layoutScale;
QLabel *labelScale;
QSpinBox *spinScale;
QHBoxLayout *layoutLogo;
QLabel *labelLogo;
QSpinBox *spinLogoSize;
QPushButton *btnAddLogo;
QPushButton *btnRemoveLogo;
QGroupBox *groupStyle;
QVBoxLayout *styleLayout;
QCheckBox *checkRounded;
QCheckBox *checkGradient;
QCheckBox *checkAutoPreview;
QGroupBox *groupInfo;
QVBoxLayout *infoLayout;
QLabel *labelCapacity;
QProgressBar *progressCapacity;
void setupUI(QMainWindow *MainWindow) {
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(800, 600);
centralWidget = new QWidget(MainWindow);
centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
verticalLayout = new QVBoxLayout(centralWidget);
verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
horizontalLayout = new QHBoxLayout();
horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
// 左侧:输入区域
leftLayout = new QVBoxLayout();
leftLayout->setObjectName(QString::fromUtf8("leftLayout"));
textContent = new QTextEdit(centralWidget);
textContent->setObjectName(QString::fromUtf8("textContent"));
textContent->setPlaceholderText("请输入要编码的内容...");
leftLayout->addWidget(textContent);
buttonLayout = new QHBoxLayout();
buttonLayout->setObjectName(QString::fromUtf8("buttonLayout"));
btnGenerate = new QPushButton(centralWidget);
btnGenerate->setObjectName(QString::fromUtf8("btnGenerate"));
btnGenerate->setText("生成二维码");
buttonLayout->addWidget(btnGenerate);
btnSave = new QPushButton(centralWidget);
btnSave->setObjectName(QString::fromUtf8("btnSave"));
btnSave->setText("保存");
buttonLayout->addWidget(btnSave);
btnClear = new QPushButton(centralWidget);
btnClear->setObjectName(QString::fromUtf8("btnClear"));
btnClear->setText("清除");
buttonLayout->addWidget(btnClear);
btnCopy = new QPushButton(centralWidget);
btnCopy->setObjectName(QString::fromUtf8("btnCopy"));
btnCopy->setText("复制");
buttonLayout->addWidget(btnCopy);
leftLayout->addLayout(buttonLayout);
horizontalLayout->addLayout(leftLayout, 2);
// 右侧:设置区域
rightLayout = new QVBoxLayout();
rightLayout->setObjectName(QString::fromUtf8("rightLayout"));
// 设置组
groupSettings = new QGroupBox(centralWidget);
groupSettings->setObjectName(QString::fromUtf8("groupSettings"));
groupSettings->setTitle("设置");
settingsLayout = new QVBoxLayout(groupSettings);
// 版本
layoutVersion = new QHBoxLayout();
labelVersion = new QLabel(groupSettings);
labelVersion->setText("版本:");
layoutVersion->addWidget(labelVersion);
comboVersion = new QComboBox(groupSettings);
layoutVersion->addWidget(comboVersion);
settingsLayout->addLayout(layoutVersion);
// 纠错等级
layoutECC = new QHBoxLayout();
labelECC = new QLabel(groupSettings);
labelECC->setText("纠错等级:");
layoutECC->addWidget(labelECC);
comboECC = new QComboBox(groupSettings);
layoutECC->addWidget(comboECC);
settingsLayout->addLayout(layoutECC);
// 边距
layoutBorder = new QHBoxLayout();
labelBorder = new QLabel(groupSettings);
labelBorder->setText("边距:");
layoutBorder->addWidget(labelBorder);
spinBorder = new QSpinBox(groupSettings);
spinBorder->setRange(0, 20);
spinBorder->setValue(4);
layoutBorder->addWidget(spinBorder);
settingsLayout->addLayout(layoutBorder);
// 缩放
layoutScale = new QHBoxLayout();
labelScale = new QLabel(groupSettings);
labelScale->setText("保存缩放:");
layoutScale->addWidget(labelScale);
spinScale = new QSpinBox(groupSettings);
spinScale->setRange(1, 10);
spinScale->setValue(4);
layoutScale->addWidget(spinScale);
settingsLayout->addLayout(layoutScale);
// Logo设置
layoutLogo = new QHBoxLayout();
labelLogo = new QLabel(groupSettings);
labelLogo->setText("Logo大小:");
layoutLogo->addWidget(labelLogo);
spinLogoSize = new QSpinBox(groupSettings);
spinLogoSize->setRange(5, 50);
spinLogoSize->setValue(20);
spinLogoSize->setSuffix("%");
layoutLogo->addWidget(spinLogoSize);
btnAddLogo = new QPushButton(groupSettings);
btnAddLogo->setText("添加");
layoutLogo->addWidget(btnAddLogo);
btnRemoveLogo = new QPushButton(groupSettings);
btnRemoveLogo->setText("移除");
layoutLogo->addWidget(btnRemoveLogo);
settingsLayout->addLayout(layoutLogo);
rightLayout->addWidget(groupSettings);
// 样式组
groupStyle = new QGroupBox(centralWidget);
groupStyle->setObjectName(QString::fromUtf8("groupStyle"));
groupStyle->setTitle("样式");
styleLayout = new QVBoxLayout(groupStyle);
checkRounded = new QCheckBox(groupStyle);
checkRounded->setText("圆角模块");
styleLayout->addWidget(checkRounded);
checkGradient = new QCheckBox(groupStyle);
checkGradient->setText("渐变颜色");
styleLayout->addWidget(checkGradient);
checkAutoPreview = new QCheckBox(groupStyle);
checkAutoPreview->setText("实时预览");
styleLayout->addWidget(checkAutoPreview);
rightLayout->addWidget(groupStyle);
// 信息组
groupInfo = new QGroupBox(centralWidget);
groupInfo->setObjectName(QString::fromUtf8("groupInfo"));
groupInfo->setTitle("信息");
infoLayout = new QVBoxLayout(groupInfo);
labelCapacity = new QLabel(groupInfo);
labelCapacity->setText("长度: 0/0");
infoLayout->addWidget(labelCapacity);
progressCapacity = new QProgressBar(groupInfo);
progressCapacity->setRange(0, 100);
infoLayout->addWidget(progressCapacity);
rightLayout->addWidget(groupInfo);
rightLayout->addStretch();
horizontalLayout->addLayout(rightLayout, 1);
verticalLayout->addLayout(horizontalLayout);
MainWindow->setCentralWidget(centralWidget);
}
};
#endif // UI_MAINWINDOW_H
四、高级功能扩展
4.1 批量生成工具
cpp
// BatchQRCodeGenerator.h
#ifndef BATCHQRCODEGENERATOR_H
#define BATCHQRCODEGENERATOR_H
#include <QObject>
#include <QRunnable>
#include <QThreadPool>
#include <QMap>
class BatchQRCodeGenerator : public QObject, public QRunnable
{
Q_OBJECT
public:
struct BatchItem {
QString id;
QString content;
QString filename;
int version = 5;
int eccLevel = 1;
int scale = 4;
};
explicit BatchQRCodeGenerator(QObject *parent = nullptr);
void addBatch(const QList<BatchItem>& items);
void clear();
void run() override;
void setOutputDir(const QString& dir) { m_outputDir = dir; }
void setThreadCount(int count);
signals:
void progress(int current, int total, const QString& id);
void finished(int successCount, int failedCount);
void error(const QString& id, const QString& error);
private:
QList<BatchItem> m_items;
QString m_outputDir;
QThreadPool m_threadPool;
};
#endif // BATCHQRCODEGENERATOR_H
4.2 二维码解码功能
cpp
// QRCodeDecoder.h
#ifndef QRCODEDECODER_H
#define QRCODEDECODER_H
#include <QObject>
#include <QImage>
#include <QZXing.h>
class QRCodeDecoder : public QObject
{
Q_OBJECT
public:
explicit QRCodeDecoder(QObject *parent = nullptr);
// 从图片文件解码
QString decodeFromFile(const QString& filename);
// 从QImage解码
QString decodeFromImage(const QImage& image);
// 从剪贴板解码
QString decodeFromClipboard();
// 从摄像头捕获
void startCameraCapture();
void stopCameraCapture();
signals:
void decoded(const QString& result);
void cameraFrameCaptured(const QImage& frame);
private:
QZXing m_decoder;
};
#endif // QRCODEDECODER_H
4.3 样式化二维码
cpp
// StyledQRCodeGenerator.h
#ifndef STYLEDQRCODEGENERATOR_H
#define STYLEDQRCODEGENERATOR_H
#include <QImage>
class StyledQRCodeGenerator
{
public:
enum PatternStyle {
StyleDefault,
StyleDots,
StyleRounded,
StyleStars,
StyleHearts
};
struct StyleOptions {
PatternStyle patternStyle = StyleDefault;
QColor primaryColor = Qt::black;
QColor secondaryColor = Qt::white;
QColor gradientStart = Qt::black;
QColor gradientEnd = Qt::darkGray;
bool useGradient = false;
int cornerRadius = 5;
};
static QImage generateStyledQRCode(const QString& text,
const QSize& size,
const StyleOptions& options);
private:
static void applyPattern(QImage& image, const QImage& pattern,
const QColor& color, int moduleSize);
static QImage createDotPattern(int size);
static QImage createStarPattern(int size);
static QImage createHeartPattern(int size);
};
#endif // STYLEDQRCODEGENERATOR_H
五、项目配置
5.1 CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.16)
project(QRCodeGenerator)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找Qt
find_package(Qt6 COMPONENTS Core Widgets REQUIRED)
# 包含目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/qrcode)
# 源文件
set(SOURCES
main.cpp
QRCodeWidget.cpp
MainWindow.cpp
QRCodeGenerator.cpp
)
# 头文件
set(HEADERS
QRCodeWidget.h
MainWindow.h
QRCodeGenerator.h
)
# UI文件
set(FORMS
MainWindow.ui
)
# 资源文件
set(RESOURCES
resources.qrc
)
# 生成MOC文件
qt6_wrap_cpp(MOC_SOURCES ${HEADERS})
qt6_wrap_ui(UI_SOURCES ${FORMS})
qt6_add_resources(RES_SOURCES ${RESOURCES})
# 可执行文件
add_executable(${PROJECT_NAME}
${SOURCES}
${MOC_SOURCES}
${UI_SOURCES}
${RES_SOURCES}
)
# 链接库
target_link_libraries(${PROJECT_NAME}
Qt6::Core
Qt6::Widgets
)
5.2 .pro 文件 (qmake)
qmake
# QRCodeGenerator.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = QRCodeGenerator
TEMPLATE = app
# 源文件
SOURCES += \
main.cpp \
QRCodeWidget.cpp \
MainWindow.cpp
HEADERS += \
QRCodeWidget.h \
MainWindow.h
FORMS += \
MainWindow.ui
# 包含qrcode库
INCLUDEPATH += $$PWD/qrcode
# 编译选项
QMAKE_CXXFLAGS += -std=c++17
CONFIG += c++17
# 发布配置
CONFIG(release, debug|release) {
DEFINES += QT_NO_DEBUG_OUTPUT
QMAKE_CXXFLAGS += -O2
}
# 调试配置
CONFIG(debug, debug|release) {
DEFINES += QT_DEBUG
QMAKE_CXXFLAGS += -g
}
5.3 资源文件
xml
<!-- resources.qrc -->
<RCC>
<qresource prefix="/">
<!-- 图标 -->
<file>icons/app.png</file>
<file>icons/generate.png</file>
<file>icons/save.png</file>
<file>icons/clear.png</file>
<file>icons/logo.png</file>
<!-- 样式表 -->
<file>styles/style.qss</file>
<!-- Logo示例 -->
<file>logo/default.png</file>
</qresource>
</RCC>
css
/* styles/style.qss */
QMainWindow {
background-color: #f5f5f5;
}
QGroupBox {
font-weight: bold;
border: 1px solid #ccc;
border-radius: 5px;
margin-top: 10px;
padding-top: 10px;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
}
QPushButton {
padding: 8px 16px;
border-radius: 4px;
font-weight: bold;
}
QPushButton:hover {
background-color: #e0e0e0;
}
QTextEdit {
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px;
font-family: "Microsoft YaHei", "SimSun";
}
QProgressBar {
border: 1px solid #ccc;
border-radius: 4px;
text-align: center;
}
QProgressBar::chunk {
background-color: #4CAF50;
border-radius: 4px;
}
参考代码 Qt非常实用的二维码生成代码 www.youwenfan.com/contentcsv/103314.html
六、使用示例
6.1 基本使用
cpp
#include "QRCodeGenerator.h"
#include <QApplication>
#include <QDebug>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建二维码生成器
QRCodeGenerator generator;
// 生成基本二维码
QImage qrCode = generator.generateQRCode("https://www.example.com",
QSize(200, 200));
if (!qrCode.isNull()) {
// 保存到文件
qrCode.save("qrcode.png");
// 显示二维码
QLabel label;
label.setPixmap(QPixmap::fromImage(qrCode));
label.show();
return app.exec();
}
return 0;
}
6.2 高级功能
cpp
// 高级使用示例
void advancedExample() {
QRCodeGenerator generator;
// 1. 生成带Logo的二维码
QImage logo(":/logo/company.png");
QImage qrWithLogo = generator.generateQRCodeWithLogo(
"https://www.company.com", logo, QSize(300, 300));
// 2. 生成样式化二维码
StyledQRCodeGenerator::StyleOptions options;
options.patternStyle = StyledQRCodeGenerator::StyleDots;
options.useGradient = true;
options.gradientStart = QColor("#FF5722");
options.gradientEnd = QColor("#FF9800");
QImage styledQR = generator.generateStyledQRCode(
"特殊样式", QSize(250, 250), options);
// 3. 批量生成
QList<BatchQRCodeGenerator::BatchItem> items;
for (int i = 0; i < 10; i++) {
BatchQRCodeGenerator::BatchItem item;
item.id = QString("QR%1").arg(i+1);
item.content = QString("https://example.com/item/%1").arg(i+1);
item.filename = QString("qrcode_%1.png").arg(i+1);
items.append(item);
}
BatchQRCodeGenerator batchGenerator;
batchGenerator.addBatch(items);
batchGenerator.setOutputDir("./qrcodes");
QObject::connect(&batchGenerator, &BatchQRCodeGenerator::progress,
int current, int total, const QString& id {
qDebug() << "进度:" << current << "/" << total << id;
});
batchGenerator.run();
}
6.3 命令行工具
cpp
// QRCodeCLI.cpp - 命令行工具
#include "QRCodeGenerator.h"
#include <QCoreApplication>
#include <QCommandLineParser>
#include <QFileInfo>
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
app.setApplicationName("QR Code CLI");
app.setApplicationVersion("1.0.0");
QCommandLineParser parser;
parser.setApplicationDescription("命令行二维码生成器");
parser.addHelpOption();
parser.addVersionOption();
// 定义参数
parser.addPositionalArgument("content", "要编码的内容");
parser.addPositionalArgument("output", "输出文件", "[output.png]");
QCommandLineOption sizeOption("s", "图片大小", "size", "200");
parser.addOption(sizeOption);
QCommandLineOption versionOption("v", "二维码版本", "version", "5");
parser.addOption(versionOption);
QCommandLineOption eccOption("e", "纠错等级", "ecc", "1");
parser.addOption(eccOption);
QCommandLineOption logoOption("l", "Logo文件", "logo");
parser.addOption(logoOption);
parser.process(app);
// 获取参数
QStringList args = parser.positionalArguments();
if (args.isEmpty()) {
parser.showHelp(1);
}
QString content = args.at(0);
QString output = args.value(1, "qrcode.png");
int size = parser.value(sizeOption).toInt();
int version = parser.value(versionOption).toInt();
int ecc = parser.value(eccOption).toInt();
QString logoFile = parser.value(logoOption);
// 生成二维码
QRCodeGenerator generator;
QImage qrCode;
if (!logoFile.isEmpty()) {
QImage logo(logoFile);
if (logo.isNull()) {
qCritical() << "无法加载Logo文件:" << logoFile;
return 1;
}
qrCode = generator.generateQRCodeWithLogo(content, logo, QSize(size, size));
} else {
qrCode = generator.generateQRCode(content, QSize(size, size), version, ecc);
}
if (qrCode.isNull()) {
qCritical() << "生成二维码失败";
return 1;
}
if (qrCode.save(output)) {
qInfo() << "二维码已保存到:" << output;
return 0;
} else {
qCritical() << "保存失败:" << output;
return 1;
}
}