前言
由於項目需要,需要使用到二維碼的功能。第一時間的想法就是找到第三方的庫,然后調用方法函數就可以了。結果還是遇到了很多問題。第一次接觸,網上搜索到的大多是Linux下的或者windows的VS多一些。綜合一些了博文,有2種方式實現生成二維碼的效果。第一種是源碼編譯生成靜態庫並調用。第二種是直接引入源碼文件(*.c和*.h),然后調用方法。接下來分別說明。
前期准備
libqrencode官網下載:https://fukuchi.org/works/qrencode/。
我下載的是此時最新的qrencode-4.0.2.tar.gz版本(點擊此鏈接可直接下載)。先進行解壓,備份(每次操作的都是文件夾副本,防止出錯又得重新下載)。
源碼編譯成靜態庫
第一步 文件預處理
找到文件夾中的 config.h.in 文件,重命名為 config.h 。會出現彈框,確認即可。這個文件是整個過程中最重要的文件。
文件夾空白處鼠標右鍵-->分組依據類型-->類型。這樣方便操作文件。除了 *.c 和 *.h 文件,其他文件全部刪除。最后應該只剩下下圖的文件。
然后在刪除 qrenc.c 文件。這個文件是用於測試的,我們不需要用到。不刪除的話編譯通不過。
注意刪除的是文件夾的副本文件哦。因為誰都不敢保證一次一定成功的。紅色框的就是剛才重命名后的 config.h 文件。
第二步 新建一個C++庫
新建一個C++的庫項目。雖然qrencode的源碼是.c文件但是沒關系的,可以成功。
最后一步kits選擇第一個還是第二個沒關系的,都可以成功。只要保證和自己Qt軟件的編譯器位數相同就可以了,我這里都是64位。
第三步 源碼添加到項目
在項目目錄下新建libqrencode文件夾,將剛才准備好的 *.c 和 *.h 文件全部復制到libqrencode文件夾下。然后把整個libqrencode文件夾添加到項目中。
第四步 配置config.h
先在.pro文件中添加一句 DEFINES += HAVE_CONFIG_H ;
然后編輯 config.h 文件。在編譯時,編譯器會根據該文件中的編譯條件選項對源碼進行選擇性編譯。剛下載下來的 config.h 可以看到全是undef + 宏名,也就是全部是條件關閉的狀態。我們需要正確修改它才能正確編譯。下面是我的 config.h ,可以作為參考。

/* config.h.in. Generated from configure.ac by autoheader. */ /* Define to 1 if you have the <dlfcn.h> header file. */ #define HAVE_DLFCN_H 1 /* Define if you have the iconv() function and it works. */ #undef HAVE_ICONV /* Define to 1 if you have the <inttypes.h> header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if using pthread is enabled. */ #undef HAVE_LIBPTHREAD /* Define to 1 if you have the <memory.h> header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if using libpng is enabled. */ #undef HAVE_PNG /* Define to 1 if using SDL is enabled. */ #undef HAVE_SDL /* Define to 1 if you have the <stdint.h> header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the <stdlib.h> header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `strdup' function. */ #define HAVE_STRDUP 1 /* Define to 1 if you have the <strings.h> header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the <string.h> header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the <sys/stat.h> header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the <sys/types.h> header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the <unistd.h> header file. */ #define HAVE_UNISTD_H 1 /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Major version number */ #define MAJOR_VERSION 4 /* Micro version number */ #define MICRO_VERSION 0 /* Minor version number */ #define MINOR_VERSION 2 /* Name of package */ #define PACKAGE "qrencode" /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "" /* Define to the full name of this package. */ #define PACKAGE_NAME "QRencode" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "QRencode 4.0.2" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "qrencode" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "4.0.2" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Version number of package */ #define VERSION "4.0.2" /* Define to empty if `const' does not conform to ANSI C. */ #define const /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to 'static' if no test programs will be compiled. */ #define STATIC_IN_RELEASE static #undef WITH_TESTS
特別注意:配置 config.h 是整個過程中最重要的步驟。
配置錯誤可能出現的錯誤:
- 編譯報錯,無法出錯
- 編譯通過,可以生成靜態庫。但添加外部庫后,使用出現問題(包括關鍵函數未定義、參數重復定義等等)
- 編譯通過,可以生成靜態庫。Debug模式下使用正常,Release模式下報錯。
在搜索博文的時候,我們不應該直接復制他們的 config.h ,而是應該一項一項看里面的注釋,自己定義。第一,我們使用的qrencode源碼版本和他們不一定相同。比如3.0.0和4.0.2,對同樣的 static 定義的宏名稱都不一樣。就算同樣是4系列版本,4.0.0和4.0.2也會有一些小差異。不注意這些,很容易就會出現上訴的問題。
我的配置思路:
- *_H可以看成系統自帶的文件,都開啟(define XXX_H 1)
- VERSION有關的變量設置和源碼相同版本號。這里的版本其實就是控制生成二維碼的尺寸、糾錯級別等。詳細了解:http://news.cnblogs.com/n/191671/
- ANSI C有關的都開啟
- 不知道的,就復制全局搜索,找不到的就關閉(undef XXX)
第五步 構建生成靜態庫
清除----執行qmake----重新構建
如果沒有報錯的話,就會在自己的編譯模式下的目錄中可以找到我們需要的靜態庫文件了。它是以lib+項目名命名的,我這里就是 libtest.a 文件了,為了方便,我們重命名為 libqrencode.a 。
文件所在目錄:點擊左側“項目”圖標查看
靜態庫文件是需要和頭文件一起使用的,我們新建一個libqrencode的文件夾,然后把 libqrencode.a 和 qrencode.h 這兩個文件放進去。這樣在項目需要使用時,把文件夾libqrencode復制到項目路徑下,配置一下就可以了。
第六步 使用靜態庫
libqrencode.a 通過添加外部庫的方式加入; qrencode.h 通過添加現有文件的方式加入。
添加外部庫
選擇項目路徑下的libqrencode文件夾下的靜態庫
執行完后,在.pro文件中會自動添加一些代碼。也不知道哪里配置錯了,自動生成的代碼中是-lqrencode。按道理是-llibqrencode (-l 后面加的應該是靜態庫的名字)。這里我們需要改一下,不然會報錯找不到庫qrencode。
改成
添加頭文件
在用到的地方
這樣就可以直接調用API了。
界面
目前只實現了生成二維碼的方法。
實現代碼
mainwindow.h

#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "libqrencode/qrencode.h" #include <QPainter> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); void GernerateQRCode(const QString &text, QPixmap &qrPixmap, int scale); private slots: void on_generateBtn_clicked(); void on_saveBtn_clicked(); void on_analysisBtn_clicked(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
mainwindow.cpp

#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } /** * @brief GernerateQRCode * 生成二維碼函數 * @param text 二維碼內容 * @param qrPixmap 二維碼像素圖 * @param scale 二維碼縮放比例 */ void MainWindow::GernerateQRCode(const QString &text, QPixmap &qrPixmap, int scale) { if(text.isEmpty()) { return; } //二維碼數據 QRcode *qrCode = nullptr; //這里二維碼版本傳入參數是2,實際上二維碼生成后,它的版本是根據二維碼內容來決定的 qrCode = QRcode_encodeString(text.toStdString().c_str(), 2, QR_ECLEVEL_Q, QR_MODE_8, 1); if(nullptr == qrCode) { return; } int qrCode_Width = qrCode->width > 0 ? qrCode->width : 1; int width = scale * qrCode_Width; int height = scale * qrCode_Width; QImage image(width, height, QImage::Format_ARGB32_Premultiplied); QPainter mPainter(&image); QColor background(Qt::white); mPainter.setBrush(background); mPainter.setPen(Qt::NoPen); mPainter.drawRect(0, 0, width, height); QColor foreground(Qt::black); mPainter.setBrush(foreground); for(int y = 0; y < qrCode_Width; ++y) { for(int x = 0; x < qrCode_Width; ++x) { unsigned char character = qrCode->data[y * qrCode_Width + x]; if(character & 0x01) { QRect rect(x * scale, y * scale, scale, scale); mPainter.drawRects(&rect, 1); } } } qrPixmap = QPixmap::fromImage(image); QRcode_free(qrCode); } void MainWindow::on_generateBtn_clicked() { QPixmap qrPixmap; int width = ui->imageLabel->width(); int height = ui->imageLabel->height(); GernerateQRCode(ui->content->toPlainText(), qrPixmap, 2); qrPixmap = qrPixmap.scaled(QSize(width, height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); ui->imageLabel->setPixmap(qrPixmap); qrPixmap.save("qrencode.jpg",nullptr,-1);//這里直接就保存為圖片 } void MainWindow::on_saveBtn_clicked() { } void MainWindow::on_analysisBtn_clicked() { }
效果
手機掃描二維碼的結果是和輸入的信息是一致的。
包含源碼調用API
其實生成靜態庫的方法就是把源碼編譯打包成一個靜態庫文件,等到調用的時候再到靜態庫調用API。那么直接把源碼包含進項目的做法,就是不編譯打包了,直接訪問源文件。缺點是文件多,會很亂(不推薦)。要是用一個第三庫就包含一個源碼文件夾,多了以后就很亂了。我在嘗試過程中,是按照網上靜態庫的方法失敗后,無奈嘗試這種方法。畢竟先弄出效果再說不是?
前期步驟和上面的方法相似,文件預處理--->所有 .c 和 .h 打包進文件夾libqrencode--->復制到項目路徑--->以已存在文件的形式把整個文件夾添加到項目中去--->配置 config.h (注意點和上面一樣)
----->需要使用的地方 #include "libqrencode/qrencode.h" ---->調用API。這里就不再重復敘述了。
參考博文
- https://blog.csdn.net/tanglj86/article/details/79208246(采用包含源碼調用API的方式,用他的 config.h Debug模式下正常,Release模式下報錯多重定義)
- https://www.cnblogs.com/grebamboo/p/12743373.html(他這邊直接提供了靜態庫,直接使用,出現函數已聲明未定義的錯誤。生成二維碼的按鈕槽函數參考的他)
- http://news.cnblogs.com/n/191671/ (有關二維碼的基礎知識,大概了解下就可以了)
后續
第一次發布的隨筆,有存在錯誤或者錯別字請指出,感謝。有疑惑的可以在評論里提出,人不經常在線。
后續還會嘗試解析二維碼。不過我相信有了這個經驗,使用其他庫應該不是問題。
希望可以讓一開始接觸完全不知道怎么着手的同學(說的就是自己啊)看得懂,暫時就先這樣吧~