头文件、链接库和运行库各负责一段
跳转到“头文件、链接库和运行库各负责一段”使用 QCustomPlot 的预编译库时,通常仍需包含 qcustomplot.h:编译器要知道类型、成员和调用接口。库文件不能替代这些声明;某些模板和内联实现本来就位于头文件中。
| 文件或目标 | 主要阶段 | 内容与用途 |
|---|---|---|
| 头文件 | 编译 | 声明、类定义及可能的内联/模板实现 |
| 静态实现库 | 链接 | 目标代码按链接规则进入最终产物,仍可能依赖动态 Qt 等库 |
| Windows 导入库 | 链接 | 描述动态库导出入口,帮助生成动态导入关系 |
| DLL | 运行 | 提供被导入的实现,并继续加载它自己的依赖 |
MSVC 的 .lib 既可能是静态实现库,也可能是 DLL 的导入库;MinGW 常见静态 .a 与导入 .dll.a。不能只凭后缀判断内容,更不能因为二者都参与链接就说导入库包含了全部实现。
头文件、库和 Qt Kit 应来自兼容的版本、编译器、架构和构建配置。已经链接 QCustomPlot 动态实现时,不应又把同一份 qcustomplot.cpp 编入应用;选择直接编译源码、建立静态目标或消费动态库中的一种路线。
先用源码目标复现原正弦图
跳转到“先用源码目标复现原正弦图”以下使用 QCustomPlot 2.1.1 的 qcustomplot.h/.cpp。把原包中的两文件放到 thirdparty/qcustomplot/;不修改其实现。QCustomPlot 有自己的许可条件,实际项目应核对该版本的许可选项。
原稿两套 Test 类代码依赖未提供的 .ui 文件,现用代码布局保留全部绘图意图:1001 点、x 从 −5 到 5、sin(x)、蓝色折线、尺寸 3 的空心圆点、坐标范围、网格和 replot()。
main.cpp:
#include "qcustomplot.h"#include <QApplication>#include <QImage>#include <QVBoxLayout>#include <QVector>#include <QWidget>#include <cassert>#include <cmath>#include <iostream>
class Test final : public QWidget {public: explicit Test(QWidget *parent = nullptr) : QWidget(parent) { auto *customPlot = new QCustomPlot(this); auto *layout = new QVBoxLayout(this); layout->addWidget(customPlot); QCPGraph *graph = customPlot->addGraph(); QVector<double> x(1001), y(1001); for (int i = 0; i < 1001; ++i) { x[i] = i / 100.0 - 5.0; y[i] = std::sin(x[i]); } graph->setData(x, y); graph->setPen(QPen(Qt::blue)); graph->setLineStyle(QCPGraph::lsLine); graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 3)); customPlot->xAxis->setLabel(QStringLiteral("x")); customPlot->yAxis->setLabel(QStringLiteral("sin(x)")); customPlot->xAxis->setRange(-5, 5); customPlot->yAxis->setRange(-1.5, 1.5); customPlot->xAxis->grid()->setVisible(true); customPlot->yAxis->grid()->setVisible(true); customPlot->replot(); assert(graph->dataCount() == 1001); assert(graph->data()->at(0)->key == -5.0); assert(graph->data()->at(1000)->key == 5.0); assert(std::abs(graph->data()->at(500)->value) < 1e-12); assert(graph->pen().color() == QColor(Qt::blue)); assert(graph->scatterStyle().shape() == QCPScatterStyle::ssCircle); assert(graph->scatterStyle().size() == 3); assert(customPlot->xAxis->range().lower == -5); assert(customPlot->yAxis->range().upper == 1.5); assert(customPlot->xAxis->grid()->visible()); assert(customPlot->yAxis->grid()->visible()); }};
int main(int argc, char **argv) { QApplication app(argc, argv); Test widget; widget.resize(640, 400); if (app.arguments().contains(QStringLiteral("--interactive"))) { widget.show(); return app.exec(); } QImage frame(widget.size(), QImage::Format_ARGB32); frame.fill(Qt::transparent); widget.render(&frame); assert(!frame.isNull()); std::cout << "QCustomPlot " << QCUSTOMPLOT_VERSION_STR << ": 1001-point sine plot passed\n";}CMakeLists.txt 可以分别构建静态和动态 QCustomPlot:
cmake_minimum_required(VERSION 3.20)project(QcpIntegration LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(Qt6 REQUIRED COMPONENTS Widgets PrintSupport)option(QCP_SHARED "Build QCustomPlot as a shared library" OFF)if(QCP_SHARED) add_library(qcustomplot SHARED thirdparty/qcustomplot/qcustomplot.cpp thirdparty/qcustomplot/qcustomplot.h) target_compile_definitions(qcustomplot PRIVATE QCUSTOMPLOT_COMPILE_LIBRARY INTERFACE QCUSTOMPLOT_USE_LIBRARY)else() add_library(qcustomplot STATIC thirdparty/qcustomplot/qcustomplot.cpp thirdparty/qcustomplot/qcustomplot.h)endif()set_target_properties(qcustomplot PROPERTIES AUTOMOC ON)target_include_directories(qcustomplot SYSTEM PUBLIC thirdparty/qcustomplot)target_link_libraries(qcustomplot PUBLIC Qt6::Widgets Qt6::PrintSupport)if(MINGW) target_compile_options(qcustomplot PRIVATE -Wa,-mbig-obj)endif()add_executable(qcp_demo main.cpp)target_link_libraries(qcp_demo PRIVATE qcustomplot)QCustomPlot 内部的 QObject 类需要 moc,所以对库目标启用 AUTOMOC;示例 Test 自己没有增加元对象功能,不需要 Q_OBJECT。PrintSupport 是这个源码版本的依赖。MinGW 的大对象选项用于这份较大的合并源码,不能不分编译器地把该选项传给 MSVC。
默认分支是静态 QCustomPlot,QCP_SHARED=ON 是动态版本;使用不同构建目录验证两者。这里“静态 QCustomPlot”不表示整个程序已静态链接 Qt。动态构建中的 QCUSTOMPLOT_COMPILE_LIBRARY 只用于生成库,QCUSTOMPLOT_USE_LIBRARY 传给消费端;这与 2.1.1 头文件的导出/导入条件一致。
已有 DLL 时只消费它
跳转到“已有 DLL 时只消费它”如果供应方已经提供匹配的 DLL、导入库和头文件,应用不再编译 qcustomplot.cpp。下面为 Windows 动态库消费项目的完整 CMake 文件,仍使用上面的 main.cpp。配置时通过 QCP_INCLUDE_DIR、QCP_IMPORT_LIBRARY 和 QCP_DLL 提供真实文件路径;用对应 Debug/Release 库,不把占位路径当成已安装依赖。
cmake_minimum_required(VERSION 3.20)project(QcpBinaryConsumer LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)find_package(Qt6 REQUIRED COMPONENTS Widgets PrintSupport)set(QCP_INCLUDE_DIR "" CACHE PATH "Directory containing qcustomplot.h")set(QCP_IMPORT_LIBRARY "" CACHE FILEPATH "Matching Windows import library")set(QCP_DLL "" CACHE FILEPATH "Matching QCustomPlot DLL")if(NOT EXISTS "${QCP_INCLUDE_DIR}/qcustomplot.h" OR NOT EXISTS "${QCP_IMPORT_LIBRARY}" OR NOT EXISTS "${QCP_DLL}") message(FATAL_ERROR "Supply matching QCustomPlot headers, import library and DLL")endif()add_library(qcp_binary SHARED IMPORTED)set_target_properties(qcp_binary PROPERTIES IMPORTED_IMPLIB "${QCP_IMPORT_LIBRARY}" IMPORTED_LOCATION "${QCP_DLL}" INTERFACE_INCLUDE_DIRECTORIES "${QCP_INCLUDE_DIR}" INTERFACE_COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY INTERFACE_LINK_LIBRARIES "Qt6::Widgets;Qt6::PrintSupport")add_executable(qcp_demo main.cpp)target_link_libraries(qcp_demo PRIVATE qcp_binary)add_custom_command(TARGET qcp_demo POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:qcp_binary>" "$<TARGET_FILE_DIR:qcp_demo>" VERBATIM)这只复制 QCustomPlot DLL,Qt 和编译器运行库仍按Windows 发布处理。qmake 项目中的 INCLUDEPATH 与 LIBS += -L... -l... 也分别解决头文件和链接库;动态消费端同样要设置相应导入宏。运行时找不到 DLL 与链接时找不到导入库是不同故障。
原第三方清单如何补齐
跳转到“原第三方清单如何补齐”原短笔记还列出了 qrcode 和 QHotkey:
| 项目线索 | 可以保留的用途 | 接入前仍需确认 |
|---|---|---|
| qrcode | 生成二维码 | 名称不足以唯一确定库;补项目地址、版本、编码接口及许可后再决定构建方式 |
| Skycoder42/QHotkey | 应用不在前台时仍接收全局热键 | 原笔记未给地址,此处提供同名上游供核对;查平台后端、注册失败结果、系统占用及 Qt 版本 |
QHotkey 上游说明支持 Windows、macOS 和 X11,并说明它的现有实现不支持 Wayland;不要把普通窗口内快捷键与跨应用全局热键混为一谈,也不据此断言所有 Wayland 热键方案都不可能。本页没有执行全局热键注册或确定原 qrcode 所指项目。
本页实际验证聚焦 QCustomPlot 源码静态目标、源码动态目标及分离的预编译库消费流程;绘图程序默认进行数据/样式断言和一次绘制后结束,--interactive 可显示窗口。进一步的轴刻度设置见QCustomPlot 坐标轴刻度。