手把手带你模仿 海康威视sdk+Qt 的二次开发(下)

上篇是代码之前的工作,这一篇讲解代码。

首先来看操作相机的整个接口流程:
在这里插入图片描述
接下来详细分析:
1.枚举设备:
在这里插入图片描述在这里插入图片描述
注:将MV_GIGE_DEVICE和MV_USB_DEVICE设备信息保存到m_stDevList结构体中,以便后续使用。
2.创建句柄
也就是将设备信息与变量进行绑定,以后只要拿到这个句柄,就可以进行相关设备的操作:
在这里插入图片描述
3.打开设备:
在这里插入图片描述

4.开启一个线程,用于图像数据的取流操作:
4-1.新建一个线程类,其中含有对于widget的引用,便于线程窗口zhong句柄信息:
在这里插入图片描述
打开设备之后就开启线程:

在这里插入图片描述
在线程函数中,完成图像帧数据的读取操作:

void WorkThread::run()
{
    MV_FRAME_OUT stImageInfo = {0};
    int nRet = MV_OK;
    bool f=isRun();
    while(f)
    {
        nRet=m_widget.m_pcMyCamera->GetImageBuffer(&stImageInfo, 1000); 
        if (nRet == MV_OK)
        { 
            m_widget.m_mutex.lock();
            if(NULL ==  m_widget.m_pSaveImageBuf || stImageInfo.stFrameInfo.nFrameLen >  m_widget.m_nSaveImageBufSize)
            {
                if(m_widget.m_pSaveImageBuf)
                {
                    free(m_widget.m_pSaveImageBuf);
                    m_widget.m_pSaveImageBuf=NULL;
                }
                m_widget.m_pSaveImageBuf = (unsigned char *)malloc(sizeof(unsigned char) * stImageInfo.stFrameInfo.nFrameLen);
                  if (m_widget.m_pSaveImageBuf  == NULL)
                  {
                    m_widget.m_mutex.unlock();
                      return;
                  }
                 m_widget.m_nSaveImageBufSize = stImageInfo.stFrameInfo.nFrameLen;
            }
            memcpy(m_widget.m_pSaveImageBuf, stImageInfo.pBufAddr, stImageInfo.stFrameInfo.nFrameLen);
            memcpy(&m_widget.m_stImageInfo, &(stImageInfo.stFrameInfo), sizeof(MV_FRAME_OUT_INFO_EX));
            m_widget.m_mutex.unlock();

            auto pixmap = QPixmap::fromImage(QImage(stImageInfo.pBufAddr,stImageInfo.stFrameInfo.nWidth,stImageInfo.stFrameInfo.nHeight,QImage::Format_Indexed8));
            m_widget.ui->label->setPixmap(pixmap);
            m_widget.m_pcMyCamera->FreeImageBuffer(&stImageInfo);
        }
        else
        {
            if (MV_TRIGGER_MODE_ON == m_widget.m_nTriggerMode)
            {
                   qDebug()<<__FUNCTION__;
                // Sleep(5);
            }
        }
    }
}

之后在主线程中进行取流操作:

void Widget::slot_grabbingBtn()
{
    if (false == m_bOpenDevice || true == m_bStartGrabbing || NULL == m_pcMyCamera)
    {
        return;
    }
    memset(&m_stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));

    m_WorkThread->SetRun(true);
    m_WorkThread->start();

    int nRet = m_pcMyCamera->StartGrabbing();
    if (MV_OK != nRet)
    {
        m_WorkThread->SetRun(false);
        QMessageBox::information(this,"Device","Start grabbing fail");
        return;
    }
    m_bStartGrabbing = true;
    EnableControls();
}

完成了取流操作,其他是一些基本的操作函数,先代码奉上:
widget.h:

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QMutex>
#include "MvCameraControl.h"
#include "MvCamera.h"
#include "workthread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
    friend class WorkThread;
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public slots:
    void slot_searchDevice();                     //查询设备
    void slot_openDevice();                       //打开设备
    void slot_closeDevice();                      //关闭设备
    void slot_continusModeBtn();                  //连续模式
    void slot_triggerModeBtn();                   //触发模式
    void slot_grabbingBtn();                      //开始采集
    void slot_stopGrbbingBtn();                   //停止采集
    void slot_softwareTriggerBtn();               //软触发
    void slot_softwareoOnceBtn();                 //软触发一次
    void slot_saveBmpBtn();                       //保存Bmp图片
    void slot_saveJpgBtn();                       //保存Jpg图片
    void slot_saveTiffBtn();                      //保存Tiff图片
    void slot_savePngBtn();                       //保存Png图片
    void slot_getParameterBtn();                  //获取参数
    void slot_setParameterBtn();                  //设置参数

public:

    int   GrabThreadProcess();                    //线程取流函数,用于图像显示的取流操作

   int   SaveImage(MV_SAVE_IAMGE_TYPE enSaveImageType);         //由于图片类型不同,但是他们大部分的实现代码相同,于是定义一个公共的函数,便于操作。
   int   GetTriggerMode();                        //获取触发模式
   int   SetTriggerMode();                        //设置触发模式
   int   GetExposureTime();                       //获取曝光时间
   int   SetExposureTime();                       //设置曝光时间
   int   GetGain();                               //获取增益
   int   SetGain();                               //设置增益
   int   GetFrameRate();                          //获取帧率
   int   SetFrameRate();                          //设置帧率
   int   GetTriggerSource();                      //获取触发源
   int   SetTriggerSource();                      //设置触发源

   //按钮使能,比如在没有打开设备之前,后续的比如采集图片之类按钮将变灰不能按下,只有满足一定条件才可以进行操作,每次在按下一个按钮的最后都将调用该函数做一个判断。
   void  EnableControls();

   void  InitUi();                //初始化界面

   // ch:去除自定义的像素格式
   bool  RemoveCustomPixelFormats(enum MvGvspPixelType enPixelFormat);

private:
        MV_CC_DEVICE_INFO_LIST  m_stDevList;                    //保存设备信息的链表
        CMvCamera*              m_pcMyCamera;                   //用于操作相机的指针


        bool                    m_isDeviceReady;                //是否查询到设备
        bool                    m_bOpenDevice;                  //是否打开设备
        bool                    m_bSoftWareTriggerCheck;        //软触发按钮是否被按下
        bool                    m_bStartGrabbing;               //是否开始抓图



        int                     m_nDeviceCombo;
        int                     m_nTriggerMode;                 //触发模式
        MV_FRAME_OUT_INFO_EX    m_stImageInfo;                  //保存一帧图像信息
        unsigned char*          m_pSaveImageBuf;                //保存一帧图像的指针
        unsigned int            m_nSaveImageBufSize;            //保存一帧图像的大小

        int                     m_nTriggerSource;               //触发源

        WorkThread*             m_WorkThread;                   //线程句柄,用于开启线程取流图像用

        QMutex                  m_mutex;

        double                  m_dExposureEdit;
        double                  m_dGainEdit;
        double                  m_dFrameRateEdit;
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include<QMessageBox>
#include<QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    ,m_bOpenDevice(false)
    ,m_nDeviceCombo(0)
    ,m_nTriggerMode(MV_TRIGGER_MODE_OFF)
    ,m_bStartGrabbing(false)
    ,m_pcMyCamera(NULL)
    ,m_pSaveImageBuf(NULL)
    ,m_nSaveImageBufSize(0)
    ,m_bSoftWareTriggerCheck(false)
    , m_nTriggerSource(MV_TRIGGER_SOURCE_SOFTWARE)
    ,m_dExposureEdit(0)
    ,m_dGainEdit(0)
    ,m_dFrameRateEdit(0)
    ,m_isDeviceReady(false)
{
    m_WorkThread=new WorkThread(*this);
    ui->setupUi(this);
    InitUi();
    connect(ui->searchDeviceBtn   ,SIGNAL(clicked()),this,SLOT(slot_searchDevice()));
    connect(ui->openDeviceBtn     ,SIGNAL(clicked()),this,SLOT(slot_openDevice()));
    connect(ui->closeDeviceBtn    ,SIGNAL(clicked()),this,SLOT(slot_closeDevice()));
    connect(ui->continusModeBtn   ,SIGNAL(clicked()),this,SLOT(slot_continusModeBtn()));
    connect(ui->triggerModeBtn    ,SIGNAL(clicked()),this,SLOT(slot_triggerModeBtn()));
    connect(ui->startGrabBtn      ,SIGNAL(clicked()),this,SLOT(slot_grabbingBtn()));

    connect(ui->softwareTriggerBtn,SIGNAL(stateChanged(int)),this,SLOT(slot_softwareTriggerBtn()));

    connect(ui->softwareOnceBtn   ,SIGNAL(clicked()),this,SLOT(slot_softwareoOnceBtn()));
    connect(ui->saveBmpBtn        ,SIGNAL(clicked()),this,SLOT(slot_saveBmpBtn()));
    connect(ui->saveJpgBtn        ,SIGNAL(clicked()),this,SLOT(slot_saveJpgBtn()));
    connect(ui->saveTiffBtn       ,SIGNAL(clicked()),this,SLOT(slot_saveTiffBtn()));
    connect(ui->savePngBtn        ,SIGNAL(clicked()),this,SLOT(slot_savePngBtn()));
    connect(ui->GetParamterBtn    ,SIGNAL(clicked()),this,SLOT(slot_getParameterBtn()));
    connect(ui->SetParameterBtn   ,SIGNAL(clicked()),this,SLOT(slot_setParameterBtn()));
    connect(ui->stopGrabBtn       ,SIGNAL(clicked()),this,SLOT(slot_stopGrbbingBtn()));
}

void Widget::slot_setParameterBtn()
{
  bool bIsSetSucceed = true;
  int nRet = SetExposureTime();
  if (nRet != MV_OK)
  {
      bIsSetSucceed = false;
      QMessageBox::critical(this,"Exposure","Set Exposure Time Fail");
  }
  nRet = SetGain();
  if (nRet != MV_OK)
  {
      bIsSetSucceed = false;
      QMessageBox::critical(this,"Gain","Set Gain Fail");
  }
  nRet = SetFrameRate();
  if (nRet != MV_OK)
  {
      bIsSetSucceed = false;
      QMessageBox::critical(this,"Frame","Set Frame Rate Fail");
  }

  if (true == bIsSetSucceed)
  {
      QMessageBox::critical(this,"Parameter","Set Parameter Succeed");
  }
}

void Widget::slot_stopGrbbingBtn()
{
    if (false == m_bOpenDevice || false == m_bStartGrabbing || NULL == m_pcMyCamera)
        {
            return;
        }
        m_WorkThread->SetRun(false);
        bool f=m_WorkThread->isRun();
        if(f)
        {
            m_WorkThread->quit();
            m_WorkThread->wait();
            m_WorkThread=NULL;
        }
        int nRet = m_pcMyCamera->StopGrabbing();
        if(MV_OK != nRet)
        {
            QMessageBox::critical(this,"grabbing","Stop grabbing fail");
            return;
        }
        m_bStartGrabbing = false;
}

int Widget::SetTriggerSource()
{
    int nRet = MV_OK;
    m_bSoftWareTriggerCheck=ui->softwareTriggerBtn->isChecked();
    if (m_bSoftWareTriggerCheck)
    {
        m_nTriggerSource = MV_TRIGGER_SOURCE_SOFTWARE;
        nRet = m_pcMyCamera->SetEnumValue("TriggerSource", m_nTriggerSource);
        if (MV_OK != nRet)
        {
            QMessageBox::critical(this,"Trigger","Set Software Trigger Fail");
            return nRet;
        }
       // GetDlgItem(IDC_SOFTWARE_ONCE_BUTTON )->EnableWindow(TRUE);
    }
    else
    {
        m_nTriggerSource = MV_TRIGGER_SOURCE_LINE0;
        nRet = m_pcMyCamera->SetEnumValue("TriggerSource", m_nTriggerSource);
        if (MV_OK != nRet)
        {
            QMessageBox::critical(this,"Hardware","Set Hardware Trigger Fail");
            return nRet;
        }
        //GetDlgItem(IDC_SOFTWARE_ONCE_BUTTON )->EnableWindow(FALSE);
    }
    return nRet;
}

int Widget::SetFrameRate()
{
    int nRet = m_pcMyCamera->SetBoolValue("AcquisitionFrameRateEnable", true);
    if (MV_OK != nRet)
    {
        return nRet;
    }

    return m_pcMyCamera->SetFloatValue("AcquisitionFrameRate", (float)m_dFrameRateEdit);
}

int Widget::SetGain()
{
    // ch:设置增益前先把自动增益关闭,失败无需返回
       //en:Set Gain after Auto Gain is turned off, this failure does not need to return
       m_pcMyCamera->SetEnumValue("GainAuto", 0);

       return m_pcMyCamera->SetFloatValue("Gain", (float)m_dGainEdit);
}

int Widget::SetExposureTime()
{
    // ch:调节这两个曝光模式,才能让曝光时间生效
       // en:Adjust these two exposure mode to allow exposure time effective
       int nRet = m_pcMyCamera->SetEnumValue("ExposureMode", MV_EXPOSURE_MODE_TIMED);
       if (MV_OK != nRet)
       {
           return nRet;
       }

       m_pcMyCamera->SetEnumValue("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF);

       return m_pcMyCamera->SetFloatValue("ExposureTime", (float)m_dExposureEdit);
}

int Widget::SetTriggerMode()
{
return m_pcMyCamera->SetEnumValue("TriggerMode", m_nTriggerMode);
}

void Widget::slot_getParameterBtn()
{
    int nRet = GetTriggerMode();
    if (nRet != MV_OK)
    {
        QMessageBox::critical(this,"Mode","Get Trigger Mode Fail");
    }
    nRet = GetExposureTime();
    if (nRet != MV_OK)
    {
         QMessageBox::critical(this,"Mode","Get Exposure Time Fail");
    }
   ui->ExposureLineEdit->setText(QString("%1").arg(m_dExposureEdit));
    nRet = GetGain();
    if (nRet != MV_OK)
    {
         QMessageBox::critical(this,"Gain","Get Gain Fail");
    }
    ui->GainLineEdit->setText(QString("%1").arg(m_dGainEdit));
    nRet = GetFrameRate();
    if (nRet != MV_OK)
    {
         QMessageBox::critical(this,"Frame","Get Frame Rate Fail");
    }
    ui->FrameRateLineEdit->setText(QString("%1").arg(m_dFrameRateEdit));
    nRet = GetTriggerSource();
    if (nRet != MV_OK)
    {
         QMessageBox::critical(this,"Trigger","Get Trigger Source Fail");
    }

}

int Widget::GetTriggerSource()
{
    MVCC_ENUMVALUE stEnumValue = {0};

   int nRet = m_pcMyCamera->GetEnumValue("TriggerSource", &stEnumValue);
   if (MV_OK != nRet)
   {
       return nRet;
   }

   if ((unsigned int)MV_TRIGGER_SOURCE_SOFTWARE == stEnumValue.nCurValue)
   {
       m_bSoftWareTriggerCheck = true;
   }
   else
   {
       m_bSoftWareTriggerCheck = false;
   }

   return MV_OK;
}

int Widget::GetGain()
{
    MVCC_FLOATVALUE stFloatValue = {0};

    int nRet = m_pcMyCamera->GetFloatValue("Gain", &stFloatValue);
    if (MV_OK != nRet)
    {
        return nRet;
    }
    m_dGainEdit = stFloatValue.fCurValue;

    return MV_OK;
}

int Widget::GetFrameRate()
{
    MVCC_FLOATVALUE stFloatValue = {0};

    int nRet = m_pcMyCamera->GetFloatValue("ResultingFrameRate", &stFloatValue);
    if (MV_OK != nRet)
    {
        return nRet;
    }
    m_dFrameRateEdit = stFloatValue.fCurValue;

    return MV_OK;
}

int Widget::GetTriggerMode()
{
    MVCC_ENUMVALUE stEnumValue = {0};
    int nRet = m_pcMyCamera->GetEnumValue("TriggerMode", &stEnumValue);
    if(MV_OK != nRet)
    {
        return nRet;
    }
    m_nTriggerMode = stEnumValue.nCurValue;
    EnableControls();
    return MV_OK;
}

int Widget::GetExposureTime()
{
    MVCC_FLOATVALUE stFloatValue = {0};

        int nRet = m_pcMyCamera->GetFloatValue("ExposureTime", &stFloatValue);
        if(MV_OK != nRet)
        {
            return nRet;
        }
        m_dExposureEdit = stFloatValue.fCurValue;
        return MV_OK;
}

void Widget::slot_savePngBtn()
{
    int nRet = SaveImage(MV_Image_Png);
       if (MV_OK != nRet)
       {
           QMessageBox::critical(this,"picture","Save Png fail");
           return;
       }
        QMessageBox::critical(this,"picture","Save Png succeed!");
}

 void Widget::slot_saveTiffBtn()
 {
     int nRet = SaveImage(MV_Image_Tif);
        if (MV_OK != nRet)
        {
            QMessageBox::critical(this,"picture","Save jpg fail");
            return;
        }
         QMessageBox::critical(this,"picture","Save jpg succeed!");
 }

void Widget::slot_saveJpgBtn()
{
    int nRet = SaveImage(MV_Image_Jpeg);
       if (MV_OK != nRet)
       {
           QMessageBox::critical(this,"picture","Save jpg fail");
           return;
       }
        QMessageBox::critical(this,"picture","Save jpg succeed!");
}

int Widget::SaveImage(MV_SAVE_IAMGE_TYPE enSaveImageType)
{
       MV_SAVE_IMG_TO_FILE_PARAM stSaveFileParam;
       memset(&stSaveFileParam, 0, sizeof(MV_SAVE_IMG_TO_FILE_PARAM));
       m_mutex.lock();
       if (m_pSaveImageBuf == NULL || m_stImageInfo.enPixelType == 0)
       {
          m_mutex.unlock();
           return MV_E_NODATA;
       }

       if(RemoveCustomPixelFormats(m_stImageInfo.enPixelType))
       {
             m_mutex.unlock();
           return MV_E_SUPPORT;
       }
       stSaveFileParam.enImageType = enSaveImageType;              // ch:需要保存的图像类型 | en:Image format to save
       stSaveFileParam.enPixelType = m_stImageInfo.enPixelType;    // ch:相机对应的像素格式 | en:Camera pixel type
       stSaveFileParam.nWidth      = m_stImageInfo.nWidth;         // ch:相机对应的宽 | en:Width
       stSaveFileParam.nHeight     = m_stImageInfo.nHeight;        // ch:相机对应的高 | en:Height
       stSaveFileParam.nDataLen    = m_stImageInfo.nFrameLen;
       stSaveFileParam.pData       = m_pSaveImageBuf;
       stSaveFileParam.iMethodValue = 0;

       // ch:jpg图像质量范围为(50-99], png图像质量范围为[0-9] | en:jpg image nQuality range is (50-99], png image nQuality range is [0-9]
          if (MV_Image_Bmp == stSaveFileParam.enImageType)
          {
              sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.bmp", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_stImageInfo.nFrameNum);
          }
          else if (MV_Image_Jpeg == stSaveFileParam.enImageType)
          {
              stSaveFileParam.nQuality = 80;
              sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.jpg", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_stImageInfo.nFrameNum);
          }
          else if (MV_Image_Tif == stSaveFileParam.enImageType)
          {
              sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.tif", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_stImageInfo.nFrameNum);
          }
          else if (MV_Image_Png == stSaveFileParam.enImageType)
          {
              stSaveFileParam.nQuality = 8;
              sprintf_s(stSaveFileParam.pImagePath, 256, "Image_w%d_h%d_fn%03d.png", stSaveFileParam.nWidth, stSaveFileParam.nHeight, m_stImageInfo.nFrameNum);
          }
          int nRet = m_pcMyCamera->SaveImageToFile(&stSaveFileParam);
          m_mutex.unlock();
          return nRet;
}

void Widget::slot_saveBmpBtn()
{
int nRet = SaveImage(MV_Image_Bmp);
   if (MV_OK != nRet)
   {
       QMessageBox::critical(this,"picture","Save bmp fail");
       return;
   }
    QMessageBox::critical(this,"picture","Save bmp succeed!");
}

void Widget::slot_closeDevice()
{
    bool f=m_WorkThread->isRun();
    if (f)
    {
        m_WorkThread->quit();
        m_WorkThread->wait();
        m_WorkThread->SetRun(false);
    }

    if(m_pcMyCamera)
    {
        m_pcMyCamera->Close();
        delete m_pcMyCamera;
        m_pcMyCamera = NULL;
    }
    m_bStartGrabbing=false;
    m_bOpenDevice = false;
    if(m_pSaveImageBuf)
    {
        free(m_pSaveImageBuf);
        m_pSaveImageBuf=NULL;
    }
    m_nSaveImageBufSize=0;
    EnableControls();
}

void Widget::slot_continusModeBtn()
{
    m_nTriggerMode = MV_TRIGGER_MODE_OFF;
    m_pcMyCamera->SetEnumValue("TriggerMode", m_nTriggerMode);
    EnableControls();
}

void Widget::slot_triggerModeBtn()
{
    m_nTriggerMode = MV_TRIGGER_MODE_ON;
    m_pcMyCamera->SetEnumValue("TriggerMode", m_nTriggerMode);
    EnableControls();
}

void Widget::slot_grabbingBtn()
{
    if (false == m_bOpenDevice || true == m_bStartGrabbing || NULL == m_pcMyCamera)
    {
        return;
    }
    memset(&m_stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));

    m_WorkThread->SetRun(true);
    m_WorkThread->start();

    int nRet = m_pcMyCamera->StartGrabbing();
    if (MV_OK != nRet)
    {
        m_WorkThread->SetRun(false);
        QMessageBox::information(this,"Device","Start grabbing fail");
        return;
    }
    m_bStartGrabbing = true;
    EnableControls();
}

void Widget::slot_softwareTriggerBtn()
{
    int nRet = MV_OK;
    m_bSoftWareTriggerCheck=ui->softwareTriggerBtn->isChecked();
    if (m_bSoftWareTriggerCheck)
    {
        m_nTriggerSource = MV_TRIGGER_SOURCE_SOFTWARE;
        nRet = m_pcMyCamera->SetEnumValue("TriggerSource", m_nTriggerSource);
        if (MV_OK != nRet)
        {
            QMessageBox::information(this,"Device","Set Software Trigger Fail");
            return;
        }
    }
    else
    {
        m_nTriggerSource = MV_TRIGGER_SOURCE_LINE0;
        nRet = m_pcMyCamera->SetEnumValue("TriggerSource", m_nTriggerSource);
        if (MV_OK != nRet)
        {
            QMessageBox::information(this,"Device","Set Hardware Trigger Fail");
            return;
        }
    }
    EnableControls();
    return;
}

void Widget::slot_softwareoOnceBtn()
{
    if(true != m_bStartGrabbing)
    {
        return;
    }
    m_pcMyCamera->CommandExecute("TriggerSoftware");
}

void Widget::InitUi()
{
    memset(&m_stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
    m_WorkThread->SetRun(false);
    EnableControls();
}

void  Widget::EnableControls()
{
    //如果查询到设备,开启打开设备按钮
    ui->openDeviceBtn->setEnabled(m_isDeviceReady?true:false);
    ui->closeDeviceBtn->setEnabled(m_bOpenDevice?true:false);
    ui->continusModeBtn->setEnabled(m_bOpenDevice?true:false);
    ui->triggerModeBtn->setEnabled(m_bOpenDevice?true:false);
    ui->startGrabBtn->setEnabled(m_bOpenDevice?true:false);
    ui->stopGrabBtn->setEnabled(m_bOpenDevice?true:false);

    //首先是否是在采集图像中,在采集图像的过程中,判断是否为触发模式,只有在触发模式下,才能开启软触发按钮。
    ui->softwareTriggerBtn->setEnabled(m_bStartGrabbing?((MV_TRIGGER_MODE_ON ==  m_nTriggerMode)?true:false):false);
    ui->softwareOnceBtn->setEnabled(
      (MV_TRIGGER_MODE_OFF ==  m_nTriggerMode) ? false:
                (m_bSoftWareTriggerCheck?true:false) );
    ui->saveBmpBtn->setEnabled(m_bStartGrabbing?true:false);
    ui->saveJpgBtn->setEnabled(m_bStartGrabbing?true:false);
    ui->saveTiffBtn->setEnabled(m_bStartGrabbing?true:false);
    ui->savePngBtn->setEnabled(m_bStartGrabbing?true:false);
    ui->GetParamterBtn->setEnabled(m_bOpenDevice?true:false);
    ui->SetParameterBtn  ->setEnabled(m_bOpenDevice?true:false);
    ui->ExposureLineEdit->setEnabled(m_bOpenDevice?true:false);
    ui->GainLineEdit->setEnabled(m_bOpenDevice?true:false);
    ui->FrameRateLineEdit->setEnabled(m_bOpenDevice?true:false);
}

bool Widget::RemoveCustomPixelFormats(MvGvspPixelType enPixelFormat)
{
    int nResult = enPixelFormat & MV_GVSP_PIX_CUSTOM;
    if(MV_GVSP_PIX_CUSTOM == nResult)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void Widget::slot_openDevice()
{
    if (true == m_bOpenDevice || NULL != m_pcMyCamera)
    {
        return;
    }
    int nIndex = m_nDeviceCombo;
    if ((nIndex < 0) | (nIndex >= MV_MAX_DEVICE_NUM))
    {
        QMessageBox::critical(this,"Device","Device index is error!");
        return;
    }
    // ch:由设备信息创建设备实例 | en:Device instance created by device information
    if (NULL == m_stDevList.pDeviceInfo[nIndex])
    {
        QMessageBox::critical(this,"Device","Device does not exist!");
        return;
    }
    m_pcMyCamera = new CMvCamera;
    if (NULL == m_pcMyCamera)
    {
        return;
    }
    int nRet = m_pcMyCamera->Open(m_stDevList.pDeviceInfo[nIndex]);
    if (MV_OK != nRet)
    {
        delete m_pcMyCamera;
        m_pcMyCamera = NULL;
        QMessageBox::critical(this,"Device","Device Open error!");
        return;
    }
    // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
    if (m_stDevList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE)
    {
        unsigned int nPacketSize = 0;
        nRet = m_pcMyCamera->GetOptimalPacketSize(&nPacketSize);
        if (nRet == MV_OK)
        {
            nRet = m_pcMyCamera->SetIntValue("GevSCPSPacketSize",nPacketSize);
            if(nRet != MV_OK)
            {
                QMessageBox::critical(this,"Device","Warning: Set Packet Size fail!");
            }
        }
        else
        {
            QMessageBox::critical(this,"Device","Warning: Get Packet Size fail!");
        }
    }
    m_bOpenDevice = true;
    EnableControls();
    slot_getParameterBtn();
}

void Widget::slot_searchDevice()
{
    QString strMsg;
    ui->comboBox->clear();
    memset(&m_stDevList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));

    //ch:枚举子网内所有设备,将查询到的设备信息保存到m_stDevList列表中。
    int nRet   = CMvCamera::EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &m_stDevList);
    if (MV_OK != nRet)
    {
         QMessageBox::critical(this,"device","No device!");
        return;
    }

    // ch:将值加入到信息列表框中并显示出来 | en:Add value to the information list box and display
    for (unsigned int i = 0; i < m_stDevList.nDeviceNum; i++)
    {
        MV_CC_DEVICE_INFO* pDeviceInfo = m_stDevList.pDeviceInfo[i];
        if (NULL == pDeviceInfo)
        {
            continue;
        }

        if (pDeviceInfo->nTLayerType == MV_GIGE_DEVICE)
        {
            int nIp1 = ((m_stDevList.pDeviceInfo[i]->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
            int nIp2 = ((m_stDevList.pDeviceInfo[i]->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
            int nIp3 = ((m_stDevList.pDeviceInfo[i]->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
            int nIp4 = (m_stDevList.pDeviceInfo[i]->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);

            char strUserName[256] = {0};
            sprintf_s(strUserName, 256, "%s %s (%s)", pDeviceInfo->SpecialInfo.stGigEInfo.chManufacturerName,
                      pDeviceInfo->SpecialInfo.stGigEInfo.chModelName,
                      pDeviceInfo->SpecialInfo.stGigEInfo.chSerialNumber);

            QString  UserName(strUserName);

            strMsg= QString("[%1]GigE: %2  %3.%4.%5.%6")
                    .arg( QString::number(i)).
                    arg(strUserName).
                    arg(QString::number(nIp1)).
                    arg(QString::number(nIp2)).
                    arg(QString::number(nIp3)).
                    arg( QString::number(nIp4));
            ui->comboBox->addItem(strMsg);
        }
    }

    if (0 == m_stDevList.nDeviceNum)
    {

        QMessageBox::critical(this,"device","No device!");
        return;
    }
      ui->comboBox->setCurrentIndex(0);
      m_isDeviceReady=true;                                 //设备已经打开
      EnableControls();
}

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


workthread.h:

#ifndef WORKTHREAD_H
#define WORKTHREAD_H

#include <QThread>
class Widget;
class WorkThread : public QThread
{
public:
    explicit WorkThread(Widget&m_widget_,QObject *parent = nullptr);

    void run();
    bool isRun();
    void SetRun(bool f);

private:
    Widget& m_widget;
    bool runflag_;
};

#endif // WORKTHREAD_H

workthread.cpp:

#include "workthread.h"
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
WorkThread::WorkThread(Widget&m_widget_,QObject *parent)
    : QThread{parent}
    ,m_widget(m_widget_)
    ,runflag_(false)
{

}

bool WorkThread::isRun()
{
return runflag_;
}
void WorkThread::SetRun(bool f)
{
  runflag_=f;
}

void WorkThread::run()
{
    MV_FRAME_OUT stImageInfo = {0};
    int nRet = MV_OK;
    bool f=isRun();
    while(f)
    {
        nRet=m_widget.m_pcMyCamera->GetImageBuffer(&stImageInfo, 1000); 
        if (nRet == MV_OK)
        { 
            m_widget.m_mutex.lock();
            if(NULL ==  m_widget.m_pSaveImageBuf || stImageInfo.stFrameInfo.nFrameLen >  m_widget.m_nSaveImageBufSize)
            {
                if(m_widget.m_pSaveImageBuf)
                {
                    free(m_widget.m_pSaveImageBuf);
                    m_widget.m_pSaveImageBuf=NULL;
                }
                m_widget.m_pSaveImageBuf = (unsigned char *)malloc(sizeof(unsigned char) * stImageInfo.stFrameInfo.nFrameLen);
                  if (m_widget.m_pSaveImageBuf  == NULL)
                  {
                    m_widget.m_mutex.unlock();
                      return;
                  }
                 m_widget.m_nSaveImageBufSize = stImageInfo.stFrameInfo.nFrameLen;
            }
            memcpy(m_widget.m_pSaveImageBuf, stImageInfo.pBufAddr, stImageInfo.stFrameInfo.nFrameLen);
            memcpy(&m_widget.m_stImageInfo, &(stImageInfo.stFrameInfo), sizeof(MV_FRAME_OUT_INFO_EX));
            m_widget.m_mutex.unlock();

            auto pixmap = QPixmap::fromImage(QImage(stImageInfo.pBufAddr,stImageInfo.stFrameInfo.nWidth,stImageInfo.stFrameInfo.nHeight,QImage::Format_Indexed8));
            m_widget.ui->label->setPixmap(pixmap);
            m_widget.m_pcMyCamera->FreeImageBuffer(&stImageInfo);
        }
        else
        {
            if (MV_TRIGGER_MODE_ON == m_widget.m_nTriggerMode)
            {
                   qDebug()<<__FUNCTION__;
                // Sleep(5);

            }

        }
    }



}

ui_widget.h:

/********************************************************************************
** Form generated from reading UI file 'widget.ui'
**
** Created by: Qt User Interface Compiler version 5.15.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_WIDGET_H
#define UI_WIDGET_H

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QRadioButton>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_Widget
{
public:
    QPushButton *searchDeviceBtn;
    QPushButton *openDeviceBtn;
    QPushButton *closeDeviceBtn;
    QGroupBox *init_groupbox;
    QGroupBox *savepicture_groupbox;
    QPushButton *saveBmpBtn;
    QPushButton *saveJpgBtn;
    QPushButton *saveTiffBtn;
    QPushButton *savePngBtn;
    QPushButton *startGrabBtn;
    QRadioButton *continusModeBtn;
    QRadioButton *triggerModeBtn;
    QPushButton *stopGrabBtn;
    QCheckBox *softwareTriggerBtn;
    QPushButton *softwareOnceBtn;
    QGroupBox *grabpicture_groupbox;
    QLabel *Exposure;
    QLabel *Gain;
    QLabel *GrabRate;
    QLineEdit *ExposureLineEdit;
    QLineEdit *GainLineEdit;
    QLineEdit *FrameRateLineEdit;
    QPushButton *GetParamterBtn;
    QPushButton *SetParameterBtn;
    QGroupBox *parameter_groupbox;
    QWidget *layoutWidget;
    QVBoxLayout *verticalLayout;
    QComboBox *comboBox;
    QLabel *label;

    void setupUi(QWidget *Widget)
    {
        if (Widget->objectName().isEmpty())
            Widget->setObjectName(QString::fromUtf8("Widget"));
        Widget->resize(1028, 679);
        searchDeviceBtn = new QPushButton(Widget);
        searchDeviceBtn->setObjectName(QString::fromUtf8("searchDeviceBtn"));
        searchDeviceBtn->setGeometry(QRect(760, 30, 191, 29));
        openDeviceBtn = new QPushButton(Widget);
        openDeviceBtn->setObjectName(QString::fromUtf8("openDeviceBtn"));
        openDeviceBtn->setGeometry(QRect(760, 70, 93, 29));
        closeDeviceBtn = new QPushButton(Widget);
        closeDeviceBtn->setObjectName(QString::fromUtf8("closeDeviceBtn"));
        closeDeviceBtn->setGeometry(QRect(860, 70, 93, 29));
        init_groupbox = new QGroupBox(Widget);
        init_groupbox->setObjectName(QString::fromUtf8("init_groupbox"));
        init_groupbox->setGeometry(QRect(710, 10, 281, 101));
        savepicture_groupbox = new QGroupBox(Widget);
        savepicture_groupbox->setObjectName(QString::fromUtf8("savepicture_groupbox"));
        savepicture_groupbox->setGeometry(QRect(710, 300, 281, 141));
        saveBmpBtn = new QPushButton(savepicture_groupbox);
        saveBmpBtn->setObjectName(QString::fromUtf8("saveBmpBtn"));
        saveBmpBtn->setGeometry(QRect(10, 30, 93, 29));
        saveJpgBtn = new QPushButton(savepicture_groupbox);
        saveJpgBtn->setObjectName(QString::fromUtf8("saveJpgBtn"));
        saveJpgBtn->setGeometry(QRect(140, 30, 93, 29));
        saveTiffBtn = new QPushButton(savepicture_groupbox);
        saveTiffBtn->setObjectName(QString::fromUtf8("saveTiffBtn"));
        saveTiffBtn->setGeometry(QRect(10, 80, 93, 29));
        savePngBtn = new QPushButton(savepicture_groupbox);
        savePngBtn->setObjectName(QString::fromUtf8("savePngBtn"));
        savePngBtn->setGeometry(QRect(140, 80, 93, 29));
        startGrabBtn = new QPushButton(Widget);
        startGrabBtn->setObjectName(QString::fromUtf8("startGrabBtn"));
        startGrabBtn->setGeometry(QRect(730, 180, 111, 25));
        startGrabBtn->setCheckable(false);
        continusModeBtn = new QRadioButton(Widget);
        continusModeBtn->setObjectName(QString::fromUtf8("continusModeBtn"));
        continusModeBtn->setGeometry(QRect(740, 130, 118, 24));
        triggerModeBtn = new QRadioButton(Widget);
        triggerModeBtn->setObjectName(QString::fromUtf8("triggerModeBtn"));
        triggerModeBtn->setGeometry(QRect(850, 130, 118, 24));
        stopGrabBtn = new QPushButton(Widget);
        stopGrabBtn->setObjectName(QString::fromUtf8("stopGrabBtn"));
        stopGrabBtn->setGeometry(QRect(850, 180, 111, 25));
        softwareTriggerBtn = new QCheckBox(Widget);
        softwareTriggerBtn->setObjectName(QString::fromUtf8("softwareTriggerBtn"));
        softwareTriggerBtn->setGeometry(QRect(750, 230, 71, 24));
        softwareOnceBtn = new QPushButton(Widget);
        softwareOnceBtn->setObjectName(QString::fromUtf8("softwareOnceBtn"));
        softwareOnceBtn->setGeometry(QRect(860, 230, 93, 29));
        grabpicture_groupbox = new QGroupBox(Widget);
        grabpicture_groupbox->setObjectName(QString::fromUtf8("grabpicture_groupbox"));
        grabpicture_groupbox->setGeometry(QRect(710, 110, 281, 181));
        Exposure = new QLabel(Widget);
        Exposure->setObjectName(QString::fromUtf8("Exposure"));
        Exposure->setGeometry(QRect(750, 470, 69, 20));
        Gain = new QLabel(Widget);
        Gain->setObjectName(QString::fromUtf8("Gain"));
        Gain->setGeometry(QRect(750, 510, 69, 20));
        GrabRate = new QLabel(Widget);
        GrabRate->setObjectName(QString::fromUtf8("GrabRate"));
        GrabRate->setGeometry(QRect(750, 550, 69, 20));
        ExposureLineEdit = new QLineEdit(Widget);
        ExposureLineEdit->setObjectName(QString::fromUtf8("ExposureLineEdit"));
        ExposureLineEdit->setGeometry(QRect(830, 470, 113, 25));
        GainLineEdit = new QLineEdit(Widget);
        GainLineEdit->setObjectName(QString::fromUtf8("GainLineEdit"));
        GainLineEdit->setGeometry(QRect(830, 510, 113, 25));
        FrameRateLineEdit = new QLineEdit(Widget);
        FrameRateLineEdit->setObjectName(QString::fromUtf8("FrameRateLineEdit"));
        FrameRateLineEdit->setGeometry(QRect(830, 550, 113, 25));
        GetParamterBtn = new QPushButton(Widget);
        GetParamterBtn->setObjectName(QString::fromUtf8("GetParamterBtn"));
        GetParamterBtn->setGeometry(QRect(750, 600, 93, 29));
        SetParameterBtn = new QPushButton(Widget);
        SetParameterBtn->setObjectName(QString::fromUtf8("SetParameterBtn"));
        SetParameterBtn->setGeometry(QRect(850, 600, 93, 29));
        parameter_groupbox = new QGroupBox(Widget);
        parameter_groupbox->setObjectName(QString::fromUtf8("parameter_groupbox"));
        parameter_groupbox->setGeometry(QRect(710, 450, 281, 201));
        layoutWidget = new QWidget(Widget);
        layoutWidget->setObjectName(QString::fromUtf8("layoutWidget"));
        layoutWidget->setGeometry(QRect(10, 10, 661, 651));
        verticalLayout = new QVBoxLayout(layoutWidget);
        verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
        verticalLayout->setContentsMargins(0, 0, 0, 0);
        comboBox = new QComboBox(layoutWidget);
        comboBox->setObjectName(QString::fromUtf8("comboBox"));

        verticalLayout->addWidget(comboBox);

        label = new QLabel(layoutWidget);
        label->setObjectName(QString::fromUtf8("label"));

        verticalLayout->addWidget(label);

        parameter_groupbox->raise();
        init_groupbox->raise();
        grabpicture_groupbox->raise();
        searchDeviceBtn->raise();
        openDeviceBtn->raise();
        closeDeviceBtn->raise();
        savepicture_groupbox->raise();
        startGrabBtn->raise();
        continusModeBtn->raise();
        triggerModeBtn->raise();
        stopGrabBtn->raise();
        softwareTriggerBtn->raise();
        softwareOnceBtn->raise();
        Exposure->raise();
        Gain->raise();
        GrabRate->raise();
        ExposureLineEdit->raise();
        GainLineEdit->raise();
        FrameRateLineEdit->raise();
        GetParamterBtn->raise();
        SetParameterBtn->raise();
        layoutWidget->raise();

        retranslateUi(Widget);

        QMetaObject::connectSlotsByName(Widget);
    } // setupUi

    void retranslateUi(QWidget *Widget)
    {
        Widget->setWindowTitle(QCoreApplication::translate("Widget", "Widget", nullptr));
        searchDeviceBtn->setText(QCoreApplication::translate("Widget", "\346\237\245\350\257\242\350\256\276\345\244\207", nullptr));
        openDeviceBtn->setText(QCoreApplication::translate("Widget", "\346\211\223\345\274\200\350\256\276\345\244\207", nullptr));
        closeDeviceBtn->setText(QCoreApplication::translate("Widget", "\345\205\263\351\227\255\350\256\276\345\244\207", nullptr));
        init_groupbox->setTitle(QCoreApplication::translate("Widget", "\345\210\235\345\247\213\345\214\226", nullptr));
        savepicture_groupbox->setTitle(QCoreApplication::translate("Widget", "\345\233\276\347\211\207\344\277\235\345\255\230", nullptr));
        saveBmpBtn->setText(QCoreApplication::translate("Widget", "\344\277\235\345\255\230BMP", nullptr));
        saveJpgBtn->setText(QCoreApplication::translate("Widget", "\344\277\235\345\255\230JPG", nullptr));
        saveTiffBtn->setText(QCoreApplication::translate("Widget", "\344\277\235\345\255\230tiff", nullptr));
        savePngBtn->setText(QCoreApplication::translate("Widget", "\344\277\235\345\255\230PNG", nullptr));
        startGrabBtn->setText(QCoreApplication::translate("Widget", "\345\274\200\345\247\213\351\207\207\351\233\206", nullptr));
        continusModeBtn->setText(QCoreApplication::translate("Widget", "\350\277\236\347\273\255\346\250\241\345\274\217", nullptr));
        triggerModeBtn->setText(QCoreApplication::translate("Widget", "\350\247\246\345\217\221\346\250\241\345\274\217", nullptr));
        stopGrabBtn->setText(QCoreApplication::translate("Widget", "\345\201\234\346\255\242\351\207\207\351\233\206", nullptr));
        softwareTriggerBtn->setText(QCoreApplication::translate("Widget", "\350\275\257\350\247\246\345\217\221", nullptr));
        softwareOnceBtn->setText(QCoreApplication::translate("Widget", "\350\275\257\350\247\246\345\217\221\344\270\200\346\254\241", nullptr));
        grabpicture_groupbox->setTitle(QCoreApplication::translate("Widget", "\345\233\276\345\203\217\351\207\207\351\233\206", nullptr));
        Exposure->setText(QCoreApplication::translate("Widget", "\346\233\235\345\205\211", nullptr));
        Gain->setText(QCoreApplication::translate("Widget", "\345\242\236\347\233\212", nullptr));
        GrabRate->setText(QCoreApplication::translate("Widget", "\345\270\247\347\216\207", nullptr));
        ExposureLineEdit->setText(QCoreApplication::translate("Widget", "0", nullptr));
        GainLineEdit->setText(QCoreApplication::translate("Widget", "0", nullptr));
        FrameRateLineEdit->setText(QCoreApplication::translate("Widget", "0", nullptr));
        GetParamterBtn->setText(QCoreApplication::translate("Widget", "\345\217\202\346\225\260\350\216\267\345\217\226", nullptr));
        SetParameterBtn->setText(QCoreApplication::translate("Widget", "\345\217\202\346\225\260\350\256\276\347\275\256", nullptr));
        parameter_groupbox->setTitle(QCoreApplication::translate("Widget", "\345\217\202\346\225\260", nullptr));
        label->setText(QString());
    } // retranslateUi

};

namespace Ui {
    class Widget: public Ui_Widget {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_WIDGET_H

ui设计器中的效果如下:
在这里插入图片描述

  • 26
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 33
    评论
进行海康威视SDK二次开发的步骤如下: 1. 首先,您需要进入海康威视官网,下载最新版本的设备网络SDK。这个SDK包含了各种功能和接口,可以帮助您进行二次开发。您可以通过访问海康威视官网并在下载页面找到适合您系统的32位Windows版的SDK。 2. 下载完成后,您需要将SDK解压缩到您的开发环境中。确保您的开发环境已经配置好,并且可以访问到SDK所需的依赖库和文件。 3. 在进行SDK二次开发之前,您需要仔细阅读海康威视开发文档。这些文档会提供给您SDK的详细说明和使用方法。特别是关于SDK接口的初始化和资源释放的部分,您需要特别注意。 4. 在开始编写代码之前,您可以先确定您的项目需求和目标。根据您的需求,选择适合的SDK接口进行开发。您可以使用SDK提供的各种功能,如视频流获取、图像处理、设备控制等。 5. 在编写代码时,您需要按照SDK文档提供的接口说明来调用相应的函数。在调用SDK接口之前,您需要进行初始化操作,以确保SDK能够正常工作。在完成操作之后,记得释放SDK所占用的资源,以免造成内存泄漏或其他问题。 6. 在开发过程中,您可能会遇到一些问题或困惑。您可以参考海康威视官方网站提供的开发者社区或技术支持渠道,寻求帮助和解答。 总结起来,进行海康威视SDK二次开发的关键步骤包括下载最新版本的设备网络SDK,阅读开发文档并了解SDK的初始化和资源释放操作,根据项目需求选择适合的功能和接口进行开发,编写代码并调用SDK接口,最后注意释放SDK所占用的资源。希望这些步骤能够帮助您进行海康威视SDK二次开发。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [基于海康威视SDK实现二次开发](https://blog.csdn.net/weixin_44738214/article/details/126168881)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [海康威视sdk二次开发包.zip](https://download.csdn.net/download/weixin_43297335/12288413)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coder_gaozhiwei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值