作者归档:admin

json

一, 需求
Qt本身的json 用起来很麻烦,建议选用nlohmann 这个库来对json格式进行处理。

二, 使用

  1. 个人倾向于直接将源码放到目录下,然后直接引用头文件。
    https://github.com/nlohmann/json
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;

std::ifstream f("example.json");
json data = json::parse(f);
  1. 这个库有个好处,就是可以将结构体或者类序列化和反序列化,直接转成json,或者由json生成。

#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

// 定义一个结构体
struct Person {
    std::string name;
    int age;
    std::string address;

    // 反序列化方法,将 JSON 对象解析为结构体
    static Person from_json(const json& j) {
        Person person;
        person.name = j.at("name").get<std::string>();
        person.age = j.at("age").get<int>();
        person.address = j.at("address").get<std::string>();
        return person;
    }
};

int main() {
    // JSON 字符串,包含 Person 数据
    std::string json_str = R"(
        {
            "name": "John Doe",
            "age": 30,
            "address": "123 Main St, City"
        }
    )";

    // 解析 JSON 字符串
    json j = json::parse(json_str);

    // 将 JSON 反序列化为 Person 结构体
    Person person = Person::from_json(j);

    // 打印反序列化后的 Person 对象
    std::cout << "Name: " << person.name << std::endl;
    std::cout << "Age: " << person.age << std::endl;
    std::cout << "Address: " << person.address << std::endl;

    return 0;
}
#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

// 定义一个结构体
struct Person {
    std::string name;
    int age;
    std::string address;

    // 将结构体序列化为 JSON 对象
    json to_json() const {
        return {
            {"name", name},
            {"age", age},
            {"address", address}
        };
    }
};

int main() {
    // 创建一个 Person 对象
    Person person;
    person.name = "John Doe";
    person.age = 30;
    person.address = "123 Main St, City";

    // 将结构体序列化为 JSON 对象
    json j = person.to_json();

    // 打印序列化后的 JSON 对象
    std::cout << j.dump(4) << std::endl;  // 使用 4 个空格缩进格式化输出

    return 0;
}
  1. 解析一个嵌套的json
    
    //json 长这样
    [
    {
        "type": "abc",
        "template":"1.stl",
        "dataList": [
            {
                "angle": 30,
                "track": "track1"
            },
            {
                "angle": 30,
                "track": "track1"
            },{
                "angle": 30,
                "track": "track1"
            }
        ]
    },
    {
        "type": "cde",
        "template":"2.stl",
        "dataList": [
            {
                "angle": 30,
                "track": "track1"
            },
            {
                "angle": 30,
                "track": "track1"
            },{
                "angle": 30,
                "track": "track1"
            }
        ]
    }
    ]
//读取
//定义结构体
struct Data{
    //产品型号
    QString type;
    //模板文件
    QString templateFile;
    //转台角度 对应的轨迹
    QMap angleToTrack;
};

//数据
QList allData;

//解析
void Widget::initData()
{
    //读取json文件
    QString filePath = QCoreApplication::applicationDirPath()+"/data.json";
    QFile file(filePath);
    if(file.exists()){
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            qDebug() << "Failed to open file:" << file.errorString();
            return;
        }

        QTextStream in(&file);
        QString content = in.readAll();

        json li = json::parse(content.toStdString());
        allData.clear();
        for (const auto& item : li) {
            Data data;
            std::string type = item["type"];
            std::string templateFile = item["template"];
            data.type = QString::fromStdString(type);
            data.templateFile = QString::fromStdString(templateFile);

            json dataList = item["dataList"];
            for (const auto& obj : dataList) {
                QMap angleToTrackMap;
                float angle =  obj["angle"];
                std::string track = obj["track"];
                angleToTrackMap.insert(angle,QString::fromStdString(track));
                data.angleToTrack = angleToTrackMap;
            }
            allData.append(data);
        }

        file.close();
    }
}

日志

一,需求
软件上线后,出现问题再去排查,看日志是第一步,那就需要一个专业的日志框架,这里推荐spdlog,多线程安全,使用简单,基本功能都具备。
二, 使用
1, 将源码放到工程目录下。
https://github.com/gabime/spdlog

2,写一个单列 来配置日志和调用日志。

#pragma once

#include <QCoreApplication>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>
#include <QDateTime>

class XLogger
{
public:
    static XLogger* getInstance()
    {
        static XLogger xlogger;
        return &xlogger;
    }

    std::shared_ptr<spdlog::logger> getLogger()
    {
        return m_logger;
    }
private:
    XLogger()
    {
        try
        {
            //日志等级 可以从配置文件读取
            spdlog::level::level_enum level = spdlog::level::debug;
            const QString logger_name = QCoreApplication::applicationDirPath() + "\\log\\" +QDateTime::currentDateTime().toString("yyyy-MM-dd") + ".log";

            auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
            //文件大小 限制,日志命名
            auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logger_name.toStdString(), 100 * 1024 * 1024, 1000);

            console_sink->set_pattern("%Y-%m-%d %H:%M:%S.%f [%l] [%@] %v");
            file_sink->set_pattern("%Y-%m-%d %H:%M:%S.%f [%l] [%@] %v");

            //这里即输出到控制台 也写到文件
            console_sink->set_level(level);
            file_sink->set_level(level);

            std::vector<spdlog::sink_ptr> sinks;
            sinks.push_back(console_sink);
            sinks.push_back(file_sink);

            m_logger = std::make_shared<spdlog::logger>("multi-sink", begin(sinks), end(sinks));
            m_logger->set_level(spdlog::level::trace);
            m_logger->flush();
        }
        catch (const spdlog::spdlog_ex& ex)
        {
            std::cout << "Log initialization failed: " << ex.what() << std::endl;
        }
    }

    ~XLogger()
    {
        spdlog::drop_all();
    }

    void* operator new(size_t size)
    {}

    XLogger(const XLogger&) = delete;
    XLogger& operator=(const XLogger&) = delete;

private:
    std::shared_ptr<spdlog::logger> m_logger;
};

#define LOGT(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__)
#define LOGD(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__)
#define LOGI(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__)
#define LOGW(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__)
#define LOGE(...) SPDLOG_LOGGER_CALL(XLogger::getInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__)

3, 使用的时候 直接使用宏即可。


//没有参数
LOGI("Camera close!");

//携带参数
int index=0;
LOGI("{} close!",index);

利用QImage 显示TIFF格式图片

一,需求

利用Qt 控件 显示 tiff 图片,由于tiff图像深度位96位,3通道,所以无法直接用QImage 显示,QImage 支持24位,因此需要利用Opencv 进行转换。

二,关键点

  • 96位 深度需要利用 IMREAD_UNCHANGED 模式进行加载
Mat image2Draw_mat = imread("depth.tiff",cv::ImreadModes::IMREAD_UNCHANGED);
  • 加载后进行 规一化,然后进行位深 转换,将32f 转成8u
Mat normalize_mat;
normalize(image2Draw_mat, normalize_mat, 0, 500, NORM_MINMAX);
normalize_mat.convertTo(normalize_mat, CV_8U);
  • 因为 opencv 是 bgr格式,所以需要将bgr转成 rgb
cv::cvtColor(normalize_mat, normalize_mat, COLOR_BGR2RGB);
  • 因为原图是3通道,所以qt这边使用 QImage::Format_RGB888
const uchar pSrc = (const uchar)image2Draw_mat->data;
image2Draw_qt = QImage(pSrc, image2Draw_mat->cols,
image2Draw_mat->rows,image2Draw_mat->step,QImage::Format_RGB888);
  • 最后建议使用 QGraphicsView 显示 QImage 这样可以进行缩放,更利用观看。

三,位深度的理解

以3通道图像为例,位深度 显示96位,意思是 一个像素,可以显示的颜色范围为2的96次方种,每个通道32位,也就是4个字节。 而Qt Image 最多支持24位,也就是每个通道位8位 一个字节。因此需要将mat 进行转换(normalize_mat.convertTo(normalize_mat, CV_8U)) 把32转成8。之后就可以利用Qt QImage::Format_RGB888 进行加载了。

QML中渲染视频

一 ,需求

在qml的Rectangle中渲染视频。

二,实现

  • 继承QQuickPaintedItem 在paint函数中,绘制QImage
#ifndef MyCamera_H
#define MyCamera_H

#include <QQuickPaintedItem>
#include <QImage>
#include <atomic>
class MyCamera : public QQuickPaintedItem
{
    Q_OBJECT
public:
    explicit MyCamera(QQuickItem *parent = nullptr);
    ~MyCamera();

    //开始预览
    Q_INVOKABLE void startPreview();
    //停止预览
    Q_INVOKABLE void stopPreview();
public slots:
    void newData();
signals:
    //通过信号和槽 通知更新数据
    void sinewData();
private:
    std::atomic<bool> isRuning{false};
    cv::Mat curMat;
protected:
    //将curMat绘制到界面上
    void paint(QPainter *painter);
};

#endif // MyCamera_H
#include MyCamera.h
#include <QVector3D>
#include <QPainter>
#include <QtConcurrent>
#include <chrono>
#include <thread>
#include <opencv.hpp>
#include <opencv2/imgproc.hpp>

MyCamera::MyCamera(QQuickItem *parent) : QQuickPaintedItem(parent)
{ connect(this,&MyCamera::sinewData,this,&MyCamera::newData,Qt::QueuedConnection);
}

MyCamera::~MyCamera()
{
    stopPreview();
}

void MyCamera::startPreview()
{
  //此处可打开相机,从相机    
   isRuning=true;
    QtConcurrent::run([&](){
        //相机循环取图
        while(isRuning){
            cv::Mat mat;
            if(camera->grabImage(mat)){
                //降采样
                cv::resize(mat,curMat,cv::Size(mat.cols/8,mat.rows/8),0,0,cv::INTER_NEAREST);
            }
        }
    });
}

void MyCamera::stopPreview()
{
    isRuning=false;
}
void MyCamera::newData(){
    update();
}

void MyCamera::paint(QPainter *painter)
{
    QImage img((const uchar*)(curMat.data),curMat.cols,curMat.rows,curMat.step,   QImage::Format_Grayscale8);
    painter->drawImage(this->boundingRect(),img);
 }
  • main.cpp中注册上述类型
qmlRegisterType<MyCamera>(MyCamera,1,0,MyCamera);
  • qml使用此类型
import MyCamera 1.0
Rectangle{
    id:root
    //视频
    MyCamera{
        id:mycamera
        anchors.fill: parent
        visible: true
        Component.onCompleted: {
            mycamera.startPreview()
        }
}

三,注意事项

利用单独的一个线程去解析相机或者视频流的图像,并通过信号和槽机制去通知更新

集成VTK

一,需求
集成VTK环境到Qt中
二,步骤

  1. 下载vtk源码,并使用cmake 结合vs2019编辑成dll库。
    https://github.com/Kitware/VTK
  2. Qt工程引入,库比较多,偷懒的情况下全部引入。
    pro文件配置如下:
    
    #VTK
    INCLUDEPATH += $PWD/3rd/VTK_8.2/include/vtk-8.2
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkChartsCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonColor-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonComputationalGeometry-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonDataModel-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonExecutionModel-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonMath-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonMisc-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonSystem-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkCommonTransforms-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkDICOMParser-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkDomainsChemistry-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkDomainsChemistryOpenGL2-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkexpat-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersAMR-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersExtraction-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersFlowPaths-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersGeneral-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersGeneric-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersGeometry-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersHybrid-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersHyperTree-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersImaging-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersModeling-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersParallel-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersParallelImaging-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersProgrammable-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersSelection-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersSMP-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersSources-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersStatistics-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersTexture-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkFiltersVerdict-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkfreetype-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkGeovisCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkglew-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkGUISupportQt-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkGUISupportQtSQL-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkhdf5-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkhdf5_hl-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingColor-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingFourier-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingGeneral-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingHybrid-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingMath-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingMorphological-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingSources-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingStatistics-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkImagingStencil-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkInfovisCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkInfovisLayout-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkInteractionImage-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkInteractionStyle-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkInteractionWidgets-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOAMR-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOEnSight-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOExodus-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOExport-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOGeometry-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOImage-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOImport-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOInfovis-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOLegacy-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOLSDyna-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOMINC-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOMovie-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIONetCDF-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOParallel-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOParallelXML-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOPLY-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOSQL-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOVideo-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOXML-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkIOXMLParser-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkjpeg-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkjsoncpp-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtklibxml2-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkmetaio-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkNetCDF-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkParallelCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkpng-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkproj-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingAnnotation-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingContext2D-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingContextOpenGL2-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingFreeType-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingImage-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingLabel-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingLOD-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingOpenGL2-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingQt-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingVolume-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkRenderingVolumeOpenGL2-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtksqlite-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtksys-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtktiff-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkverdict-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkViewsContext2D-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkViewsCore-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkViewsInfovis-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkViewsQt-8.2.lib
    LIBS +=$PWD/3rd/VTK_8.2/lib/vtkzlib-8.2.lib

3. UI文件中,创建一个QWidget,并将其提升为QVTKWidget.

4. 我们以显示一个stl文件为例。

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkSmartPointer.h>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;

    vtkSmartPointer<vtkRenderer> Renderer;
    vtkSmartPointer<vtkRenderWindow> RenderWindow;
};
#endif // WIDGET_H

#include <vtkSTLReader.h>
#include <vtkPolyDataMapper.h>
#include <vtkAutoInit.h>

VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    RenderWindow=vtkSmartPointer<vtkRenderWindow>::New();
    Renderer = vtkSmartPointer<vtkRenderer>::New();
    Renderer->SetBackground(0.2, 0.2, 0.2);
    RenderWindow->AddRenderer(Renderer);
    ui->widget->SetRenderWindow(RenderWindow);


    std::string file =  "C:\\Users\\keiler\\Desktop\\file\\you-hou-che-men.stl";
    vtkSmartPointer<vtkSTLReader> reader = vtkSmartPointer<vtkSTLReader>::New();
    reader->SetFileName(file.c_str());
    reader->Update();

    vtkSmartPointer<vtkPolyDataMapper> mapper =    vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(reader->GetOutputPort());

    vtkNew<vtkActor> partActor;
    partActor->SetMapper(mapper);
    Renderer->AddActor(partActor);
}

Widget::~Widget()
{
    delete ui;
}

5. 效果

6. 备注

Qt的debug模式需要加载VTK的debug库,同样 release模式需要加载release库。不然会出现这个错误“QWidget: Must construct a QApplication before a QWidget”。

集成OSG

一,新建一个 QOsgWidget 类,继承自osgQOpenGLWidget

#ifndef QOSGWIDGET_H
#define QOSGWIDGET_H

#include <QObject>
#include <osgViewer/Viewer>
#include <osgQOpenGL/osgQOpenGLWidget>
class QOsgWidget: public osgQOpenGLWidget
{
public:
explicit QOsgWidget(QWidget *parent = nullptr);
QSize sizeHint() const;

void InitQOsgWidget();

};

#endif // QOSGWIDGET_H
#include "qosgwidget.h"
#include <QDebug>
QOsgWidget::QOsgWidget(QWidget *parent):osgQOpenGLWidget(parent)
{
//多重采样
QSurfaceFormat surfaceFormat;
surfaceFormat.setSamples(6);
setFormat(surfaceFormat);
}

QSize QOsgWidget::sizeHint() const
{
return QSize(this->width(),this->height());
}

二,UI中创建一个QWidget 并提升为QOsgWidget

三,主窗初始化这个QOsgWidget。 这里以一个 网格 节点为例。

#ifndef WIDGET_H
#define WIDGET_H

#include <osg/Node>
#include <QWidget>
#include "qosgwidget.h"

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

private:
Ui::Widget *ui;

void InitQOsgWidget();

osg::ref_ptr<osg::Group> _Root = nullptr;
osg::ref_ptr<osgViewer::Viewer> _Viewer = nullptr;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"

#include <osgGA/TrackballManipulator>
#include <osgUtil/Optimizer>
#include <osgGA/GUIEventHandler>
#include <osgViewer/ViewerEventHandlers>

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);

connect(ui->widget, &osgQOpenGLWidget::initialized, this, &Widget::InitQOsgWidget);
}

Widget::~Widget()
{
delete ui;
}

void Widget::InitQOsgWidget()
{
_Root = new osg::Group();
QSize size = ui->widget->size();

osg::ref_ptr<osgGA::TrackballManipulator> trackball = new osgGA::TrackballManipulator;
trackball->setAllowThrow(false);
trackball->setAutoComputeHomePosition(true);
trackball->setThreadSafeRefUnref(true);

osgUtil::Optimizer optimizer;
optimizer.optimize(_Root.get());
_Viewer = ui->widget->getOsgViewer();

_Viewer->addEventHandler(new osgViewer::StatsHandler);
_Viewer->getCamera()->setClearColor(osg::Vec4(0.2, 0.2, 0.2, 1));
_Viewer->getCamera()->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(size.width())/static_cast<double>(size.height()), 1.0f, 10000.0f );
_Viewer->setCameraManipulator(trackball);
_Viewer->setRunMaxFrameRate(60);


osg::ref_ptr<osg::Geode> grid = new osg::Geode;
osg::Geometry* geom = new osg::Geometry;
grid->addChild(geom);

osg::Vec3Array* vertex = new osg::Vec3Array;
geom->setVertexArray(vertex);
//沿xy平面画线,间隔500米,从-10000,画到100000
for (int i = -10; i <= 10; i += 1)
{
vertex->push_back(osg::Vec3(i, -10, 0));
vertex->push_back(osg::Vec3(i, 10, 0));
vertex->push_back(osg::Vec3(-10, i, 0));
vertex->push_back(osg::Vec3(10, i, 0));
}
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertex->size()));
osg::Vec4Array* color = new osg::Vec4Array();
color->push_back(osg::Vec4(0.7, 0.7, 0.7, 1.0));
geom->setColorArray(color, osg::Array::BIND_OVERALL);
geom->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
_Root->addChild(grid);

_Viewer->setSceneData(_Root.get());
}

四,效果

利用QGraphicsPixmapItem 渲染视频

一,需求

将相机的视频渲染到QGraphicsPixmapItem 里,这样可以利用QGraphicsView这套系统在上边进行绘图

二,实现

  • 利用单独的一个线程取图,并转换为QImage后,放到队列里。然后激发信号,槽函数里,从队列取图,转成QPixmap后设置到QGraphicsPixmapItem上。
#ifndef MYCAMERA_H
#define MYCAMERA_H

#include <QObject>

#include <QObject>
#include <QImage>
#include <opencv2/opencv.hpp>
#include safequeue.h

class MyCamera : public QObject
{
    Q_OBJECT
public:
    explicit MyCamera(QObject *parent = nullptr);
    virtual ~MyCamera(){}

    QImage getImage();

    virtual void init()=0;
protected:
    //线程安全的队列 存入图片
    SafeQueue<QImage> imageQueue{1};

signals:
    void capture();
};

#endif // MYCAMERA_H
#include mycamera.h

MyCamera::MyCamera(QObject *parent)
    : QObject{parent}
{}

QImage MyCamera::getImage()
{
    return imageQueue.dequeue();
}
#ifndef MIPICAMERA_H
#define MIPICAMERA_H

#include mycamera.h

class MIPICamera : public MyCamera
{
    Q_OBJECT
public:
    explicit MIPICamera(QObject *parent = nullptr);
    ~MIPICamera();

    virtual void init() override;

private:
    //打开相机
    int openDevice();

private:
    std::atomic_bool bExit = true;
};

#endif // MIPICAMERA_H
#include mipicamera.h
#include log.h

MIPICamera::MIPICamera(QObject *parent)
    : MyCamera{parent}
{
    bExit=false;
}

MIPICamera::~MIPICamera()
{
    bExit=true;
}

void MIPICamera::init()
{
    std::thread t([&](){
        openDevice();
    });
    t.detach();
}

int MIPICamera::openDevice()
{
    cv::VideoCapture cap(0);
    if (!cap.isOpened()) {
        LOGE(Camera open failed!);
        return -1;
    }

    LOGI(Camera open!);
    cv::Mat frame;
    // 循环取图
    while (true) {
        cap >> frame;
        if (frame.empty()) {
            continue;
        }

        QImage image;
        if(frame.channels()==1){
            image = QImage((const uchar*)(frame.data),frame.cols,frame.rows,frame.step, QImage::Format_Grayscale8);
        }else if(frame.channels()==3){
            image = QImage((const uchar*)(frame.data),frame.cols,frame.rows,frame.step, QImage::Format_BGR888);
        }

        //qDebug()<<image <<image.width()<< <<frame.cols<< <<frame.channels();
        imageQueue.enqueue(image);
        emit capture();

        if(bExit)
        {
            break;
        }
    }
    cap.release();
    LOGI(Camera close!);
}
  • QGraphicsView初始化时,可以绑定信号和槽,从队列里取出QImage。