opencv+ffmpeg+QOpenGLWidget开发的音视频播放器demo

news2024/12/23 23:18:27

前言

    本篇文档的demo包含了 1.使用OpenCV对图像进行处理,对图像进行置灰,旋转,抠图,高斯模糊,中值滤波,部分区域清除置黑,背景移除,边缘检测等操作;2.单纯使用opencv播放显示视频;3.使用opencv和openGL播放显示视频;4.在ffmpeg解码后,使用opencv显示视频,并支持对视频的旋转翻转、裁剪、添加文字、添加logo、亮度调节、置灰、录像截图,音频开关等功能。视频播放器同时支持本地文件与网络码流地址的播放。本篇博客的最后有提供工程代码的下载。

一、OpenCV简单介绍:

    OpenCV是一个基于Apache2.0许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。应用领域包含:人机互动、物体识别、图像分割、人脸识别、动作识别、运动跟踪、机器人、运动分析、机器视觉、结构分析、汽车安全驾驶等。

二、demo调用OpenCV:

    本篇demo使用的是OpenCV-4.5.1, 是直接下载windows版本,没有额外去编译OpenCV的库,安装exe后,提取头文件和相关的库文件,用于工程调用即可。OpenCV下载路径:https://sourceforge.net/projects/opencvlibrary/

    OpenCV常用函数介绍:
    1)Mat Image = imread(filename); //从指定文件加载图像并返回
    2)namedWindow(“IMG-WIN”, cv::WINDOW_NORMAL);//创建图像显示窗口
    3)destroyWindow(“IMG-WIN”);//销毁图像显示窗口
    4)imshow(“IMG-WIN”, Image);//将图片显示在窗口中
    5)imwrite将图像保存到指定文件
    6)其他

三、demo功能介绍:

3.1.图像操作

在这里插入图片描述
    图像操作包含了显示原图、置灰、旋转、抠图、高斯模糊、中值滤波,部分区域清除置黑,背景移除,边缘检测等操作。点击显示原图后,通过点击其他按钮,可以实现图像的不同效果,例如置灰:
在这里插入图片描述
    其他功能可下载demo进行操作,点击显示原图,可恢复到之前图片显示,点击保存图片按钮可将处理后的图片进行保存。

3.2.图像操作相关代码

//图像操作
/
void MainWindow::on_pushButton_showImage_clicked()
{
    // load image using opencv
    m_srcImage = imread("./Image/test1.jpg");

    std::string winName = "Display window";
    namedWindow(winName, WINDOW_AUTOSIZE);
    imshow(winName, m_srcImage);

    if(m_bShowImage == false)
    {
        HWND hwnd = static_cast<HWND>(cvGetWindowHandle(winName.c_str()));
        HWND parent = GetParent(hwnd);//得到nameWindow窗口的父句柄
        SetParent(hwnd, (HWND)ui->widget_image->winId());//设置ui控件的句柄是父句柄
        ShowWindow(parent, SW_HIDE);//隐藏掉nameWindow窗口

        resizeWindow(winName, cv::Size(ui->widget_image->width(), ui->widget_image->height()));
    }

    m_destImage = m_srcImage;
    m_bShowImage = true;
}

void MainWindow::on_pushButton_saveImage_clicked()
{
    if(m_bShowImage)
    {
        std::string saveFile = "./Image/test-new.jpg";
        if(imwrite(saveFile, m_destImage))
        {
            QMessageBox::information(this, "myOpencv", "Save Image Success.");
        }
        else
        {
            QMessageBox::critical(this, "myOpencv", "Save Image Failed.");
        }
    }
    else
        QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}

void MainWindow::on_pushButton_gray_clicked()
{
    if(m_bShowImage)
    {
        cvtColor(m_srcImage, m_destImage, COLOR_BGR2GRAY);

        std::string winName = "Display window";
        namedWindow(winName, WINDOW_AUTOSIZE);
        imshow(winName, m_destImage);
    }
    else
        QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}

void MainWindow::on_pushButton_rotate_clicked()
{
    if(m_bShowImage)
    {
        warpAffine(m_srcImage, m_destImage, getRotationMatrix2D({m_srcImage.cols/2.0f, m_srcImage.rows/2.0f},
                                                            45,0.5),m_srcImage.size(),cv::INTER_LINEAR, cv::BORDER_CONSTANT, {255,0,0});
        std::string winName = "Display window";
        namedWindow(winName, WINDOW_AUTOSIZE);
        imshow(winName, m_destImage);
    }
    else
        QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}

void MainWindow::on_pushButton_matting_clicked()
{
    if(m_bShowImage)
    {
        m_destImage = m_srcImage({20, 20, 60, 60}).clone();

        std::string winName = "Display window";
        namedWindow(winName, WINDOW_AUTOSIZE);
        imshow(winName, m_destImage);
    }
    else
        QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}

void MainWindow::on_pushButton_gauss_clicked()
{
    if(m_bShowImage)
    {
        GaussianBlur(m_srcImage, m_destImage, cv::Size(5,5), 0);

        std::string winName = "Display window";
        namedWindow(winName, WINDOW_AUTOSIZE);
        imshow(winName, m_destImage);
    }
    else
        QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}

void MainWindow::on_pushButton_median_clicked()
{
    if(m_bShowImage)
    {
        medianBlur(m_srcImage, m_destImage, 5);

        std::string winName = "Display window";
        namedWindow(winName, WINDOW_AUTOSIZE);
        imshow(winName, m_destImage);
    }
    else
        QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}

void MainWindow::on_pushButton_clear_clicked()
{
    if(m_bShowImage)
    {
        cv::Rect roi(40, 40, 80, 80);
        Mat roiMat = m_destImage(roi);
        roiMat.setTo(cv::Scalar(0,0,0));

        std::string winName = "Display window";
        namedWindow(winName, WINDOW_AUTOSIZE);
        imshow(winName, m_destImage);
    }
    else
        QMessageBox::critical(this, "myOpencv", "Please click on the Show Original Image button first.");
}

void MainWindow::on_pushButton_clearBackground_clicked()
{
    //原图
    Mat image = imread("./Image/test1.jpg");

    // 创建一个掩码(mask),用于指定哪些区域是前景,哪些区域是背景
    cv::Mat mask(image.size(), CV_8UC1, cv::Scalar(cv::GC_BGD));
    cv::Rect rectangle(50, 50, image.cols - 100, image.rows - 100);
    cv::grabCut(image, mask, rectangle, cv::Mat(), cv::Mat(), 5, cv::GC_INIT_WITH_RECT);

    // 将掩码中被标记为前景的像素设为白色,背景设为黑色
    cv::Mat foregroundMask = (mask == cv::GC_PR_FGD) | (mask == cv::GC_FGD);
    cv::Mat foregroundImage(image.size(), CV_8UC3, cv::Scalar(0, 0, 0));
    image.copyTo(foregroundImage, foregroundMask);

    // 显示结果图像
    cv::imshow("Foreground", foregroundImage);
    cv::waitKey(0);
}

void MainWindow::on_pushButton_edgeDetection_clicked()
{
    // 读取图片
    cv::Mat image = cv::imread("./Image/test1.jpg", cv::IMREAD_GRAYSCALE);

    // 边缘检测
    cv::Mat edges;
    cv::Canny(image, edges, 100, 200);

    // 显示结果
    cv::imshow("Edges", edges);
    cv::waitKey(0);
}

3.3.视频操作

在这里插入图片描述

    代码中通过宏定义可以旋转不同的视频处理方式(默认使用ffmpeg解码后,opencv播放显示视频),如下:

#define OPENCV_PLAY_VIDEO                   0   //单纯使用opencv播放显示视频
#define OPENCV_OPENGL_PLAY_VIDEO            0   //使用opencv和openGL播放显示视频
#define OPENCV_FFMPEG_PLAY_VIDEO            1   //使用ffmpeg解码后,opencv显示视频

    播放器支持对视频的旋转翻转、裁剪、添加文字、添加logo、亮度调节、置灰、录像截图,音频开关等功能,同时支持本地文件与网络码流地址的播放。

    简单演示如下:
在这里插入图片描述

    对原视频进行相关处理后,可通过点击录像截图按钮进行视频与图片的保存。通过开关音频按钮,支持对音频的播放处理。

    demo工程中有提供测试视频和测试logo,可以进行添加测试。
在这里插入图片描述

3.4.视频操作部分代码

void MainWindow::on_pushButton_Open_clicked()
{
    QString sFileName = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("Select Video File"));
    if(sFileName.isEmpty())
    {
        QMessageBox::critical(this, "myOpencv", QObject::tr("错误:文件不能为空."));
        return;
    }

    ui->lineEdit_url->setText(sFileName);
}

void MainWindow::on_pushButton_Play_clicked()
{
    QString sStreamAddr = ui->lineEdit_url->text();
    if(sStreamAddr.isEmpty())
    {
        QMessageBox::critical(this, "myOpencv", QObject::tr("错误:码流地址不能为空."));
        return;
    }

#if OPENCV_FFMPEG_PLAY_VIDEO
    if(nullptr == m_pMediaThread)
    {
        MY_DEBUG << "new MediaThread";
        m_pMediaThread = new MediaThread(ui->openGLWidget);
    }
    else
    {
        if(m_pMediaThread->isRunning())
        {
            QMessageBox::critical(this, "myOpencv", "错误:请先点击停止按钮关闭视频.");
            return;
        }
    }

    bool bMediaInit = m_pMediaThread->Init(sStreamAddr);
    if(bMediaInit)
    {
        m_pMediaThread->startThread();
    }

#elif OPENCV_PLAY_VIDEO
    if(m_pOpencvPlayThread1 == nullptr)
    {
        m_pOpencvPlayThread1 = new COpencvPlayThread1(ui->openGLWidget, sStreamAddr);
    }
    m_pOpencvPlayThread1->startThread();

#elif OPENCV_OPENGL_PLAY_VIDEO
    if(m_pOpencvPlayThread2 == nullptr)
    {
        m_pOpencvPlayThread2 = new COpencvPlayThread2(ui->openGLWidget, sStreamAddr);
    }
    m_pOpencvPlayThread2->startThread();

#endif

}

void MainWindow::on_pushButton_Stop_clicked()
{
#if OPENCV_PLAY_VIDEO
    if(m_pOpencvPlayThread1)
        m_pOpencvPlayThread1->stopThread();
#endif

#if OPENCV_OPENGL_PLAY_VIDEO
    if(m_pOpencvPlayThread2)
        m_pOpencvPlayThread2->stopThread();
#endif

#if OPENCV_FFMPEG_PLAY_VIDEO
    if(m_pMediaThread)
    {
        disconnect(m_pMediaThread, SIGNAL(sig_emitImage(const QImage&)),
                ui->openGLWidget, SLOT(slot_showImage(const QImage&)));

        m_pMediaThread->stopThread();
        m_pMediaThread->quit();
        m_pMediaThread->wait();
        m_pMediaThread->DeInit();
    }
#endif

}

void MainWindow::on_pushButton_startRecord_clicked()
{
    if(m_pMediaThread)
        m_pMediaThread->startRecord();
}

void MainWindow::on_pushButton_stopRecord_clicked()
{
    if(m_pMediaThread)
    {
        m_pMediaThread->stopRecord();
        QDesktopServices::openUrl(QUrl::fromLocalFile(RECORD_DEFAULT_PATH));
    }
}

void MainWindow::on_pushButton_snapshot_clicked()
{
    if(m_pMediaThread)
    {
        m_pMediaThread->Snapshot();
        QDesktopServices::openUrl(QUrl::fromLocalFile(SNAPSHOT_DEFAULT_PATH));
    }
}

void MainWindow::on_pushButton_video_rotate_clicked()
{
    if(m_pMediaThread)
    {
        int nRotate = ui->comboBox_rotate->currentIndex() - 1;
        if(nRotate < 0)
            m_pMediaThread->setRotate(false, nRotate);
        else
            m_pMediaThread->setRotate(true, nRotate);
    }
}

void MainWindow::on_pushButton_video_overturn_clicked()
{
    if(m_pMediaThread)
    {
        int nOverturn = ui->comboBox_overturn->currentIndex() - 1;
        if(nOverturn < 0)
            m_pMediaThread->setFlip(false, nOverturn);
        else
            m_pMediaThread->setFlip(true, nOverturn);
    }
}

bool bGrey = false;
void MainWindow::on_pushButton_video_grey_clicked()
{
    if(m_pMediaThread)
    {
        if(bGrey)
        {
            m_pMediaThread->setGray(false);
            bGrey = false;
        }
        else
        {
            m_pMediaThread->setGray(true);
            bGrey = true;
        }
    }
}

void MainWindow::on_pushButton_video_cropping_clicked()
{
    if(m_pMediaThread)
    {
        int x = ui->lineEdit_crop_X->text().toInt();
        int y = ui->lineEdit_crop_Y->text().toInt();
        int w = ui->lineEdit_crop_W->text().toInt();
        int h = ui->lineEdit_crop_H->text().toInt();

        m_pMediaThread->setCrop(true, x, y, w, h);
    }
}

void MainWindow::on_pushButton_video_addText_clicked()
{
    if(m_pMediaThread)
    {
        int x = ui->lineEdit_text_X->text().toInt();
        int y = ui->lineEdit_text_Y->text().toInt();
        int nColor = ui->comboBox_text_rgb->currentIndex();
        double size = ui->comboBox_text_fonsize->currentText().toDouble();
        QString sText = ui->lineEdit_videoText->text();

        MY_DEBUG << "size:" << size;
        m_pMediaThread->setAddText(true, sText, x, y, nColor, size);
    }
}

void MainWindow::on_pushButton_logoPath_clicked()
{
    QString sFileName = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("Select Logo File"));
    if(sFileName.isEmpty())
    {
        QMessageBox::critical(this, "myOpenCV", QObject::tr("错误:文件不能为空."));
        return;
    }

    ui->lineEdit_logo_path->setText(sFileName);
}

void MainWindow::on_pushButton_addLogo_clicked()
{
    if(m_pMediaThread)
    {
        int x = ui->lineEdit_logo_X->text().toInt();
        int y = ui->lineEdit_logo_Y->text().toInt();
        QString sAddLogoPath = ui->lineEdit_logo_path->text();

        m_pMediaThread->setAddLogo(true, x, y, sAddLogoPath);
    }
}

void MainWindow::on_pushButton_setBrightness_clicked()
{
    if(m_pMediaThread)
    {
        double brightness = ui->comboBox_brightnessVal->currentText().toDouble();
        m_pMediaThread->setImagePara(brightness);
    }
}

void MainWindow::on_pushButton_restore_clicked()
{
    if(m_pMediaThread)
    {
        int x = ui->lineEdit_crop_X->text().toInt();
        int y = ui->lineEdit_crop_Y->text().toInt();
        int w = ui->lineEdit_crop_W->text().toInt();
        int h = ui->lineEdit_crop_H->text().toInt();

        m_pMediaThread->setCrop(false, x, y, w, h);
    }
}

void MainWindow::on_pushButto_video_deleteText_clicked()
{
    if(m_pMediaThread)
    {
        int x = ui->lineEdit_text_X->text().toInt();
        int y = ui->lineEdit_text_Y->text().toInt();
        int nColor = ui->comboBox_text_rgb->currentIndex();
        double size = ui->comboBox_text_rgb->currentText().toDouble();
        QString sText = ui->lineEdit_videoText->text();

        m_pMediaThread->setAddText(false, sText, x, y, nColor, size);
    }
}

void MainWindow::on_pushButton_deleteLogo_clicked()
{
    if(m_pMediaThread)
    {
        int x = ui->lineEdit_logo_X->text().toInt();
        int y = ui->lineEdit_logo_Y->text().toInt();
        QString sAddLogoPath = ui->lineEdit_logo_path->text();

        m_pMediaThread->setAddLogo(false, x, y, sAddLogoPath);
    }
}

void MainWindow::on_pushButton_audioOpen_clicked()
{
    ctAudioPlayer::getInstance().isPlay(true);
}

void MainWindow::on_pushButton_audioClose_clicked()
{
    ctAudioPlayer::getInstance().isPlay(false);
}
void ctFFmpeg::dealWithOpenCV(AVFrame *pFrame/*, int time*/)
{
    if(!m_bQuit)
    {
        cv::Mat destCvMat(cv::Size(m_nVideoW, m_nVideoH), CV_8UC3, cv::Scalar(1, 2, 3));
        destCvMat.data = (uchar*)pFrame->data[0];

        //旋转
        if(m_bRotate)
        {
            MY_DEBUG << "Set Rotate m_nRotate:" << m_nRotate;
            rotate(destCvMat, destCvMat, m_nRotate);
        }

        //翻转
        if(m_bFlip)
        {
            MY_DEBUG << "Set Flip";
            flip(destCvMat, destCvMat, m_nFlip);
        }

        //裁剪
        if(m_bCrop)
        {
            destCvMat = destCvMat(Rect(m_nCropX, m_nCropY, m_nCropW, m_nCropH));
        }

        //灰度
        if(m_bGray)
        {
            MY_DEBUG << "Set Gray";
            cvtColor(destCvMat, destCvMat, COLOR_BGR2GRAY);
        }

        //添加文字
        if(m_bAddText)
        {
            MY_DEBUG << "m_nAddTextX:" << m_nAddTextX << ", m_nAddTextY:" << m_nAddTextY << ",m_nAddTextSize:" << m_nAddTextSize;
            cv::Point ptPos(m_nAddTextX, m_nAddTextY);
            int fontFace = cv::FONT_HERSHEY_COMPLEX;
            double fontScale = m_nAddTextSize;
            int thickness = 2;
            cv::Scalar color;

            switch(m_nAddTextColor)
            {
                case ADD_TEXT_COLOR_TYPE_BLUE:
                    color[0]=255; color[1]=0; color[2]=0;
                    cv::putText(destCvMat, m_sAddText.toStdString(), ptPos, fontFace, fontScale, color, thickness);
                break;
                case ADD_TEXT_COLOR_TYPE_GREEN:
                    color[0]=0; color[1]=255; color[2]=0;
                    cv::putText(destCvMat, m_sAddText.toStdString(), ptPos, fontFace, fontScale, color, thickness);
                break;
                case ADD_TEXT_COLOR_TYPE_RED:
                default:
                    color[0]=0; color[1]=0; color[2]=255;//红色
                    cv::putText(destCvMat, m_sAddText.toStdString(), ptPos, fontFace, fontScale, color, thickness);
                break;
            }
        }

        //添加logo
        if(m_bAddLogo)
        {
            cv::Mat logoImage = cv::imread(m_sAddLogoPath.toStdString());
            cv::Mat roi = destCvMat(cv::Rect(m_nAddLogoX, m_nAddLogoY, logoImage.cols, logoImage.rows));
            cv::addWeighted(roi, 0.5, logoImage, 0.5, 0, roi);
        }

        //亮度\暗度
        cv::addWeighted(destCvMat, m_nBrightness, cv::Scalar(0, 0, 0), 0, 0, destCvMat);

#if OPENCV_NAME_WINDOW_SHOW
        cv::imshow(m_sWinName.toStdString(), destCvMat);
#endif

        //效率太慢, 容易导致崩溃, 不适合转QImage再用openGL播放
        //...
        //emit sig_getImage(image);

        //截图
        if(m_bSnapshot)
        {
            cv::cvtColor(destCvMat, destCvMat, cv::COLOR_BGR2RGB);

            QImage image(destCvMat.data, destCvMat.cols, destCvMat.rows, static_cast<int>(destCvMat.step), QImage::Format_RGB888);

            QPixmap pixPicture = QPixmap::fromImage(image);

            QString sPath = "./snapshot/";
            QDate date = QDate::currentDate();
            QTime time = QTime::currentTime();
            m_sSnapPath = QString("%1%2-%3-%4-%5%6%7.jpg").arg(sPath).arg(date.year()). \
                    arg(date.month()).arg(date.day()).arg(time.hour()).arg(time.minute()). \
                    arg(time.second());
            MY_DEBUG << "Snapshot... m_sSnapPath:" << m_sSnapPath;
            pixPicture.save(m_sSnapPath, "jpg");
            m_bSnapshot = false;
        }
    }
}

四、工程源码下载:

demo播放器下载:https://download.csdn.net/download/linyibin_123/88217460

demo播放器工程源码下载:https://download.csdn.net/download/linyibin_123/88217472

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/879845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

crm客户管理系统的功能有哪些?

阅读本文&#xff0c;您可以了解&#xff1a;1、CRM客户管理系统的定义&#xff1b;2、CRM客户管理系统的功能。 CRM客户管理系统是一个工具或软件&#xff0c;能够帮助企业更好地与客户进行沟通、理解客户需求&#xff0c;以及有效地处理客户信息和互动。通俗地说&#xff0c…

选择正确的液压密封件的综合指南

在液压系统中&#xff0c;选择正确的密封件对于确保较佳性能和防止潜在泄漏至关重要。由于有许多选择&#xff0c;因此需要选择较合适的液压密封件。在本文中&#xff0c;我们将讨论选择液压密封件时应考虑的关键因素&#xff0c;以帮助您做出明智的决定。 1、了解您的系统要求…

一篇文章告诉你,为什么要使用Javascript流程图来可视化进程?(上)

DHTMLX Diagram库是有各种类型的图组成的&#xff0c;其中最广泛使用的是JavaScript流程图&#xff0c;它可以显示任何类型的的工作流、过程或系统&#xff0c;您可以下载DHTMLX Diagram的评估版并亲自试用。 DHTMLX Diagram正式版下载 Javascript流程图是用来干什么的&#…

项目经理如何做好多项目管理?

多项目管理一直是一项极具挑战性的工作&#xff0c;之所以难&#xff0c;不仅在于项目数量的增加&#xff0c;而且在于项目资源分配不均、项目进度比较难监控、沟通不良&#xff0c;协作不畅。 项目经理应该如何做好多项目管理呢&#xff1f; 1、明确的项目组织结构 一个清晰…

纯js点击按钮切换首页部分页面

像我这种大数据的&#xff0c;不会前端的&#xff0c;懒得学框架&#xff0c;现在有gpt了&#xff0c;前端对于我来说&#xff0c;用原生的更加友好&#xff0c;毕竟算法gpt都能优化。 首页我有个页面&#xff0c;然后我现在想点击gm替换上面的统计&#xff0c;点击用户替换回…

低代码平台轻松玩转业务规则

规则&#xff0c;是运行、运作规律所遵循的法则。规则无处不在&#xff0c;社会活动中的规则通常由传统、公共认识形成&#xff0c;表现为该干什么或是不该干什么。在计算机应用系统中&#xff0c;经常会面对这样的场景&#xff0c;处理执行一个动作时&#xff0c;需要另外的条…

【从零学习python 】29. 「函数参数详解」——了解Python函数参数的不同用法

文章目录 函数参数详解一、缺省参数二、不定长参数三、缺省参数在*args后面可变、不可变类型总结 进阶案例 函数参数详解 一、缺省参数 调用函数时&#xff0c;缺省参数的值如果没有传入&#xff0c;则取默认值。 下例会打印默认的age&#xff0c;如果age没有被传入&#xf…

UI设计师的主要职责说明(合集)

UI设计师的主要职责说明1 职责&#xff1a; 1、负责公司移动端、PC端产品相关的交互、UI等设计 2、负责公司宣传册、海报、运营物料、banner等设计 3、负责公司品牌相关的视觉设计 3、制定相关设计规范&#xff0c;提高产品的可用性、不断优化产品体验; 4、与PM、运营紧密…

Labview选项卡之实现被选择选项卡工作

文章目录 前言一、使用选项卡二、实现被选择选项卡工作1、需求2、分析3、实现①、前面板②、程序框图 三、效果展示四、源码自取 前言 有些时候&#xff0c;我们做界面&#xff0c;需要好多个界面切换。如果是同一个 VI 里界面切换&#xff0c;一般都是选项卡了。切换不同选项…

解锁编程的新契机:深入探讨Kotlin Symbol Processor (KSP)的编写

解锁编程的新契机&#xff1a;深入探讨Kotlin Symbol Processor (KSP)的编写 1. 引言 随着软件开发领域的不断发展&#xff0c;新的工具和技术不断涌现&#xff0c;以满足开发者在构建高效、可维护和创新性的代码方面的需求。Kotlin Symbol Processor&#xff08;KSP&#xf…

ssm+vue网上花店设计源码和论文

ssmvue网上花店设计源码和论文017 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xf…

无涯教程-Perl - setgrent函数

描述 此功能将枚举设置(或重置)到组条目集的开头。该函数应在第一次调用getgrent之前调用。 语法 以下是此函数的简单语法- setgrent返回值 此函数不返回任何值。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -wwhile( ($name,$passwd,$gid,$members)getgrent…

算法与数据结构-哈希算法

文章目录 什么是哈希算法哈希算法的应用安全加密唯一标识数据校验散列函数负载均衡数据分片分布式存储 什么是哈希算法 哈希算法的定义和原理非常简单&#xff0c;基本上一句话就可以概括了。将任意长度的二进制值串映射为固定长度的二进制值串&#xff0c;这个映射的规则就是…

Github上git lfs oid sha256文件无法下载的解决方案

问题&#xff1a;github上sha文件无法下载&文件超出限制 当我克隆Github上的一个库时&#xff0c;其中有一个包的内容格式如下&#xff1a; version https://git-lfs.github.com/spec/v1 oid sha256:一堆数字和字母 size 一堆数字 这堆东西类似百度网盘的下载链接和密码&a…

深入理解Python装饰器:解析高阶函数与代码美学

文章目录 &#x1f340;引言&#x1f340;什么是装饰器&#xff1f;&#x1f340;装饰器的基本用法&#x1f340;带参数的装饰器&#x1f340;类装饰器&#x1f340;总结 &#x1f340;引言 当谈到Python编程中的高级特性时&#xff0c;装饰器&#xff08;decorators&#xff0…

深入理解索引B+树的基本原理

目录 1. 引言 2. 为什么要使用索引&#xff1f; 3. 索引的概述 4. 索引的优点是什么&#xff1f; 4.1 降低数据库的IO成本&#xff0c;提高数据查找效率 4.2 保证数据库每一行数据的唯一性 4.3 加速表与表之间的连接 4.4 减少查询中分组与排序的执行时间 5. 索引的缺点…

abp(net core)+easyui+efcore实现仓储管理系统——组织管理升级之下

在“组织管理”列表页面中使用鼠标勾选想要进行修改的组织信息&#xff0c;然后点击“修改”按钮。如下图。 14.此时会弹出“修改组织信息”界面&#xff0c;但界面上没有显示我们勾选的那一条组织信息的任何数据。如下图。 15.由于我们的功能主要都在javascript中写的&#x…

Python语言入门简介

一、Python的历史 关键点: Python是一种用途广泛、解释性、面向对象的程序设计语言。 Python是新西兰的Guido van Rossum在1990 年创建的&#xff0c;它以英国流行喜剧“Monty Python的飞行马戏团”命名。van Rossum将Python开发作为一个嗜好&#xff0c;Python 因其简单、简洁…

聊聊在集群环境中本地缓存如何进行同步

前言 之前有发过一篇文章聊聊如何利用redis实现多级缓存同步。有个读者就给我留言说&#xff0c;因为他项目的redis版本不是6.0版本&#xff0c;因此他使用我文章介绍通过MQ来实现本地缓存同步&#xff0c;他的同步流程大概如下图 他原来的业务流程是每天凌晨开启定时器去爬取…

硬盘文件恢复怎么做?文件恢复,问题已解决!

“请问一下可以帮我恢复硬盘里的文件吗&#xff1f;我之前删除了一些重要的文件和数据&#xff0c;但是最近我突然想起来那些文件是很有用的&#xff0c;有没有什么好的方法可以帮我恢复硬盘中的文件呢&#xff1f;” 硬盘作为电脑的存储设备&#xff0c;电脑用户通常会将各种重…