前言

  本项目的出现理由只是笔者的一个念头,于是利用专业Qt和Opencv相关的知识开发一个辅助工具,本文章仅用于Qt和Opencv结合的学习。

 

Demo演示效果

  
  
  
  

运行包下载地址(供测试学习)

  CSDN粉丝0积分下载地址:https://download.csdn.net/download/qq21497936/85372782
  QQ群下载地址:1047134658(点击“文件”搜索“findTheDifference”,群内与博文同步更新)

运行包+源码包下载地址(供测试学习)

  CSDN下载地址:https://download.csdn.net/download/qq21497936/85372767
  (注意:源码本博客后面都有,若是想一步到位,下载这个,源码编译版本为Qt5.9.x mingw32 + openCV3.4.10)

 

功能列表

  • 应用程序可将某Q游戏界面套入内部区域,游戏方便操作;
  • 抓图区域调整,可通过右上角区域,调整区域1和区域2的位置;
  • 位置微调功能,点击按钮可像对应方向微调一个像素;
  • 识别不同,调用opencv算法,识别不同处在游戏图上绘制显示区域;
  • 游戏界面区域操作焦点为游戏界面;
  • 可清空已经绘制的区域;
 

Qt技术点

 

OpenCV技术点

 

项目模块化部署

  项目的环境为Qt5.9.3 mingw32版本,使用QtCreator开发,配合mingw32版本的Opencv3.4.10,下图左侧为项目结构,右侧为实际文件夹部署结构。
  

 

Qt代码:DrawWdget

  该类的主要作用:

  • 覆盖在游戏窗口上
  • 全部透明窗口用以当作游戏界面上的画布
  • 对鼠标消息穿透(无法点击中)
  • 识别出后绘制标记处不同的区域

Ui界面

  为自动生成默认的,没有任何改动。
  
  一共绘制两类图形,一类是框出抓取图的界面,一类是识别出后的区域,定义两个缓存变量,用以绘制对应的区域矩形。

DrawWidegt.h

#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H #include <QWidget> namespace Ui {
class DrawWidget;
} class DrawWidget : public QWidget
{
Q_OBJECT public:
explicit DrawWidget(QWidget *parent = 0);
~DrawWidget(); public:
void setRect(int x, int y, int width, int height);
void setRect2(int x, int y, int width, int height);
void clearListRect();
void setListRect(QList<QRect> listRect); protected:
void initControl(); protected:
void paintEvent(QPaintEvent *event); protected:
void drawSelectRect(QPainter *painter);
void drawListRect(QPainter *painter); private:
Ui::DrawWidget *ui; private:
QColor _colorRect;
QRect _rect;
QRect _rect2; QList<QRect> _listRect; }; #endif // DRAWWIDGET_H

DrawWidget.cpp

#include "DrawWidget.h"
#include "ui_DrawWidget.h"
#include <QPainter>
#include <windows.h> #include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz") DrawWidget::DrawWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::DrawWidget),
_colorRect(Qt::red)
{
ui->setupUi(this); setWindowFlag(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_TransparentForMouseEvents); initControl();
} DrawWidget::~DrawWidget()
{
delete ui;
} void DrawWidget::setRect(int x, int y, int width, int height)
{
_rect.setRect(x, y, width, height);
} void DrawWidget::setRect2(int x, int y, int width, int height)
{
_rect2.setRect(x, y, width, height);
LOG << _rect << _rect2;
} void DrawWidget::clearListRect()
{
_listRect.clear();
update();
} void DrawWidget::setListRect(QList<QRect> listRect)
{
_listRect = listRect;
update();
} void DrawWidget::initControl()
{
// 置顶
::SetWindowPos(HWND(this->winId()), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
} void DrawWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this); drawSelectRect(&painter);
drawListRect(&painter);
} void DrawWidget::drawSelectRect(QPainter *painter)
{
painter->save(); painter->setPen(QPen(_colorRect, 4));
painter->drawRect(_rect);
painter->drawRect(_rect2); painter->restore();
} void DrawWidget::drawListRect(QPainter *painter)
{
painter->save(); painter->setPen(QPen(Qt::white, 2));
for(int index = 0; index < _listRect.size(); index++)
{
painter->drawRect(_rect.x() + _listRect.at(index).x(),
_rect.y() + _listRect.at(index).y(),
_listRect.at(index).width(),
_listRect.at(index).height()); }
painter->setPen(QPen(Qt::blue, 2));
for(int index = 0; index < _listRect.size(); index++)
{
painter->drawRect(_rect2.x() + _listRect.at(index).x(),
_rect2.y() + _listRect.at(index).y(),
_listRect.at(index).width(),
_listRect.at(index).height());
}
painter->restore();
}
 

Qt代码:FindDifferenceWidget

  该类的主要作用:

  • 作为主窗口提供一个套入找茬游戏界面的区域;
  • 提供可以动态选取抓取区域的控件;
  • 提供微调控件,微调应用窗口,并且让DrawWidget窗口与透明区域位置同步改变;
  • 提供识别触发按钮,将识别结果反馈到DrawWidget;
  • 清空按钮,将识别的结果进行清空,也就是删除识别结果的矩形;

Ui界面

  

FindDifferenceWidget.h

#ifndef FINDDIFFERENCEWIDGET_H
#define FINDDIFFERENCEWIDGET_H #include <QWidget>
#include <QPainter>
#include <QRect>
#include <QRegion>
#include <QList>
#include "FindDifferenceManager.h"
#include "DrawWidget.h"
#include <QElapsedTimer> namespace Ui {
class FindDifferenceWidget;
} class FindDifferenceWidget : public QWidget
{
Q_OBJECT public:
explicit FindDifferenceWidget(QWidget *parent = 0);
~FindDifferenceWidget(); protected:
void initControl();
void updateGameRect(); protected slots:
void slot_initControl(); protected:
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
void moveEvent(QMoveEvent *event);
void closeEvent(QCloseEvent *event); protected slots:
void slot_valueChanged(int value); protected slots:
void slot_findResult(bool result, QList<QRect> listRect = QList<QRect>()); private slots:
void on_pushButton_do_clicked();
void on_pushButton_up_clicked();
void on_pushButton_left_clicked();
void on_pushButton_right_clicked();
void on_pushButton_down_clicked();
void on_pushButton_clear_clicked(); private:
Ui::FindDifferenceWidget *ui; private:
FindDifferenceManager *_pFindDifferenceManager;
QRect _rectGame;
QRect _rectApplication;
int _captionHeigh;
int _margin; DrawWidget *_pDrawWidget;
}; #endif // FINDDIFFERENCEWIDGET_H

FindDifferenceWidget.cpp

#include "FindDifferenceWidget.h"
#include "ui_FindDifferenceWidget.h" #include <windows.h>
#include <QApplication>
#include <QDesktopWidget>
#include <QScreen>
#include <QTimer> #include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz") FindDifferenceWidget::FindDifferenceWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::FindDifferenceWidget),
_pFindDifferenceManager(0),
_pDrawWidget(0)
{
ui->setupUi(this); QString version = "v1.0.0";
setWindowTitle(QString("大家来找茬(仅供学习Qt+OpenCV实战项目) Demo %1(作者:长沙红胖子 QQ:21497936 WX:15173255813 blog:hpzwl.blog.csdn.net").arg(version)); resize(1230, 785); initControl(); QTimer::singleShot(0, this, SLOT(slot_initControl()));
} FindDifferenceWidget::~FindDifferenceWidget()
{
delete ui;
} void FindDifferenceWidget::initControl()
{
// 置顶
::SetWindowPos(HWND(this->winId()), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); // 识别类
_pFindDifferenceManager = new FindDifferenceManager();
connect(_pFindDifferenceManager, SIGNAL(signal_findResult(bool,QList<QRect>)),
this, SLOT(slot_findResult(bool,QList<QRect>))); // 初始化
_captionHeigh = 26;
_margin = 3;
updateGameRect(); _pDrawWidget = new DrawWidget();
_pDrawWidget->show(); connect(ui->spinBox_image1X, SIGNAL(valueChanged(int)),
this, SLOT(slot_valueChanged(int)));
connect(ui->spinBox_image1Y, SIGNAL(valueChanged(int)),
this, SLOT(slot_valueChanged(int)));
connect(ui->spinBox_image1Width, SIGNAL(valueChanged(int)),
this, SLOT(slot_valueChanged(int)));
connect(ui->spinBox_image1Height, SIGNAL(valueChanged(int)),
this, SLOT(slot_valueChanged(int))); connect(ui->spinBox_image2X, SIGNAL(valueChanged(int)),
this, SLOT(slot_valueChanged(int)));
connect(ui->spinBox_image2Y, SIGNAL(valueChanged(int)),
this, SLOT(slot_valueChanged(int)));
connect(ui->spinBox_image2Width, SIGNAL(valueChanged(int)),
this, SLOT(slot_valueChanged(int)));
connect(ui->spinBox_image2Height, SIGNAL(valueChanged(int)),
this, SLOT(slot_valueChanged(int))); _pDrawWidget->setRect(ui->spinBox_image1X->value(),
ui->spinBox_image1Y->value(),
ui->spinBox_image1Width->value(),
ui->spinBox_image1Height->value()); _pDrawWidget->setRect2(ui->spinBox_image2X->value(),
ui->spinBox_image2Y->value(),
ui->spinBox_image2Width->value(),
ui->spinBox_image2Height->value()); } void FindDifferenceWidget::updateGameRect()
{
_rectApplication = QRect(-_margin,
-_captionHeigh,
rect().width() + _margin*2,
rect().height() + _captionHeigh + _margin);
_rectGame = QRect(_margin, rect().height() - _margin - 780, 1032, 780);
} void FindDifferenceWidget::slot_initControl()
{
_pDrawWidget->setGeometry(ui->frame_mask->mapToGlobal(QPoint(0, 0)).x(),
ui->frame_mask->mapToGlobal(QPoint(0, 0)).y(),
ui->frame_mask->width(),
ui->frame_mask->height()); } void FindDifferenceWidget::paintEvent(QPaintEvent *event)
{
#if 1
QRegion r1(_rectApplication);
QRegion r2(_rectGame);
QRegion r3 = r1 - r2;
setMask(r3);
#endif
QWidget::paintEvent(event);
} void FindDifferenceWidget::resizeEvent(QResizeEvent *event)
{
// 初始化
updateGameRect(); if(_pDrawWidget)
{
_pDrawWidget->setGeometry(ui->frame_mask->mapToGlobal(QPoint(0, 0)).x(),
ui->frame_mask->mapToGlobal(QPoint(0, 0)).y(),
ui->frame_mask->width(),
ui->frame_mask->height());
}
} void FindDifferenceWidget::moveEvent(QMoveEvent *event)
{
if(_pDrawWidget)
{
_pDrawWidget->setGeometry(ui->frame_mask->mapToGlobal(QPoint(0, 0)).x(),
ui->frame_mask->mapToGlobal(QPoint(0, 0)).y(),
ui->frame_mask->width(),
ui->frame_mask->height());
}
LOG << geometry();
} void FindDifferenceWidget::closeEvent(QCloseEvent *event)
{
_pDrawWidget->hide();
_pDrawWidget->deleteLater();
} void FindDifferenceWidget::slot_valueChanged(int value)
{
_pDrawWidget->setRect(ui->spinBox_image1X->value(),
ui->spinBox_image1Y->value(),
ui->spinBox_image1Width->value(),
ui->spinBox_image1Height->value()); _pDrawWidget->setRect2(ui->spinBox_image2X->value(),
ui->spinBox_image2Y->value(),
ui->spinBox_image2Width->value(),
ui->spinBox_image2Height->value());
_pDrawWidget->update(); QSpinBox *pSpinBox = dynamic_cast<QSpinBox *>(sender());
if(pSpinBox == ui->spinBox_image1Width)
{
ui->spinBox_image2Width->setValue(value);
}else if(pSpinBox == ui->spinBox_image2Width)
{
ui->spinBox_image1Width->setValue(value);
}else if(pSpinBox == ui->spinBox_image1Height)
{
ui->spinBox_image2Height->setValue(value);
}else if(pSpinBox == ui->spinBox_image2Height)
{
ui->spinBox_image1Height->setValue(value);
}
} void FindDifferenceWidget::slot_findResult(bool result, QList<QRect> listRect)
{
if(result)
{
LOG << listRect;
_pDrawWidget->setListRect(listRect);
}
} void FindDifferenceWidget::on_pushButton_do_clicked()
{
_pDrawWidget->clearListRect(); QElapsedTimer elapsedTimer;
elapsedTimer.start();
while(elapsedTimer.elapsed() < 500)
{
qApp->processEvents();
} QScreen * pScreen = QApplication::primaryScreen();
QImage gameImage = pScreen->grabWindow(QApplication::desktop()->winId(),
ui->frame_mask->mapToGlobal(QPoint(0, 0)).x(),
ui->frame_mask->mapToGlobal(QPoint(0, 0)).y(),
ui->frame_mask->width(),
ui->frame_mask->height()).toImage();
QImage image1 = gameImage.copy(ui->spinBox_image1X->value(),
ui->spinBox_image1Y->value(),
ui->spinBox_image1Width->value(),
ui->spinBox_image1Height->value());
QImage image2 = gameImage.copy(ui->spinBox_image2X->value(),
ui->spinBox_image2Y->value(),
ui->spinBox_image2Width->value(),
ui->spinBox_image2Height->value());
_pFindDifferenceManager->slot_findDiffrence(image1, image2);
} void FindDifferenceWidget::on_pushButton_up_clicked()
{
setGeometry(geometry().x(), geometry().y() - 1, geometry().width(), geometry().height());
} void FindDifferenceWidget::on_pushButton_left_clicked()
{
setGeometry(geometry().x() - 1, geometry().y(), geometry().width(), geometry().height());
} void FindDifferenceWidget::on_pushButton_right_clicked()
{
setGeometry(geometry().x() + 1, geometry().y(), geometry().width(), geometry().height());
} void FindDifferenceWidget::on_pushButton_down_clicked()
{
setGeometry(geometry().x(), geometry().y() + 1, geometry().width(), geometry().height());
} void FindDifferenceWidget::on_pushButton_clear_clicked()
{
_pDrawWidget->setListRect(QList<QRect>());
}
 

OpenCV代码:FindDifferenceManager

  识别不同处代码类功能:

  • 引入OpenCV头文件和库文件
  • 提供接口输入2个同样大小的矩形、阈值和能识别的最小矩形。
  • 核心算法:先灰度图->阈值化->寻找边界->识别最小矩形
      (最开始算法进行了滤波,闭运算,因为实际有可能存在两幅图截屏就有色差导致灰度图色差小,实际有可能原图色差较小导致灰度色差较小,实际有可能不同的点较少导致滤波去掉了这些点,以上三种经过测试都是存在的)。

FindDifferenceManager.h

#ifndef FINDDIFFERENCEMANAGER_H
#define FINDDIFFERENCEMANAGER_H #include <QObject>
#include <QImage> // opencv
#include "opencv/highgui.h"
#include "opencv/cxcore.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/xphoto.hpp"
#include "opencv2/dnn/dnn.hpp"
// opencv_contrib
#include <opencv2/xphoto.hpp>
#include <opencv2/ximgproc.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/xfeatures2d/nonfree.hpp> class FindDifferenceManager : public QObject
{
Q_OBJECT
public:
explicit FindDifferenceManager(QObject *parent = 0); private:
bool getRunning() const; signals:
void signal_findResult(bool result, QList<QRect> listRect = QList<QRect>()); public slots:
void slot_start();
void slot_stop(); public slots:
void slot_findDiffrence(QImage image, QImage image2, int thresh = 20, QRect minRect = QRect(0, 0, 4, 4)); protected:
cv::Mat image2Mat(QImage image); private:
bool _running;
}; #endif // FINDDIFFERENCEMANAGER_H

FindDifferenceManager.cpp

#include "FindDifferenceManager.h"
#include <QTimer> #include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz") FindDifferenceManager::FindDifferenceManager(QObject *parent)
: QObject(parent),
_running(false)
{
qRegisterMetaType<QList<QRect>>("QList<QRect> ");
} bool FindDifferenceManager::getRunning() const
{
return _running;
} void FindDifferenceManager::slot_start()
{
if(_running)
{
LOG << "Failed to" << __FUNCTION__ << ", it's already running.";
return;
}
_running = true;
} void FindDifferenceManager::slot_stop()
{
if(!_running)
{
LOG << "Failed to" << __FUNCTION__ << ", it's not running.";
return;
}
_running = false;
} void FindDifferenceManager::slot_findDiffrence(QImage image, QImage image2, int thresh, QRect minRect)
{
QList<QRect> listRect; // 将QImage转换为cv::Mat
cv::Mat srcMat = image2Mat(image);
cv::Mat srcMat2 = image2Mat(image2); if ((srcMat.rows != srcMat2.rows) || (srcMat.cols != srcMat2.cols))
{
emit signal_findResult(false);
} cv::Mat srcMatGray;
cv::Mat srcMat2Gray; // 转灰度图
cv::cvtColor(srcMat, srcMatGray, cv::COLOR_BGR2GRAY);
cv::cvtColor(srcMat2, srcMat2Gray, cv::COLOR_BGR2GRAY); // cv::imshow("1", srcMatGray);
// cv::imshow("2", srcMat2Gray); cv::Mat diffMatGray;
// 图1减去图2:查看差异(灰度,可能差距不大,0-255, 0 1 2 3差距小看不出)
cv::subtract(srcMatGray, srcMat2Gray, diffMatGray, cv::Mat(), CV_16SC1); // 绝对值(有负数,变正数)
// cv::imshow("3", diffMatGray);
cv::Mat diffAbsMatGray = cv::abs(diffMatGray); // 改变位深(归一化试过,但是可能存在0和255,导致漏掉一些)
diffAbsMatGray.convertTo(diffAbsMatGray, CV_8UC1, 1, 0);
// cv::imshow("4", diffAbsMatGray); #if 0
// 整个像素降低5个点的误差(色差)
for(int row = 0; row < diffAbsMatGray.rows; row++)
{
for( int col = 0; col < diffAbsMatGray.cols; col++)
{
if(diffAbsMatGray.at<uchar>(row, col) < 3)
{
diffAbsMatGray.at<uchar>(row, col) = 0;
}else{
diffAbsMatGray.at<uchar>(row, col) = diffAbsMatGray.at<uchar>(row, col) - 5;
}
}
}
#endif cv::Mat threshMat;
//阈值处理
cv::threshold(diffAbsMatGray, threshMat, thresh, 255, cv::THRESH_BINARY);
// cv::imshow("5", threshMat); cv::Mat mdianMat;
#if 0
//中值滤波
cv::medianBlur(threshMat, mdianMat, 3);
cv::imshow("6", mdianMat);
#else
mdianMat = threshMat.clone();
#endif cv::Mat closeMat;
#if 0
// 闭运算: 用拟合小裂缝,消除小型黑洞,并且在平滑较大物体的边界的同时不明显改变其面积。
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));
cv::morphologyEx(mdianMat, closeMat, cv::MORPH_CLOSE, kernel, cv::Point(-1, -1), 2, cv::BORDER_REPLICATE);
#else
closeMat = mdianMat.clone();
#endif
// cv::imshow("7", closeMat); // 寻找边界
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(closeMat,
contours,
hierarchy,
CV_RETR_EXTERNAL,
CV_CHAIN_APPROX_SIMPLE,
cv::Point(0, 0));
std::vector<std::vector<cv::Point>> contoursPoly(contours.size()); for(int index = 0; index < contours.size(); index++)
{
cv::approxPolyDP(cv::Mat(contours[index]), contoursPoly[index], 5, true);
cv::Rect rect = cv::boundingRect(cv::Mat(contoursPoly[index])); #if 0
// 小于最小矩形则忽略
if(rect.width < minRect.width() || rect.height < minRect.height())
{
continue;
}
#endif
listRect.append(QRect(rect.x, rect.y, rect.width, rect.height));
// cv::rectangle(srcMat, rect, cv::Scalar(0, 255, 0), 2);
// cv::rectangle(srcMat2, rect, cv::Scalar(0, 255, 0), 2);
}
// cv::imshow("8", srcMat2); // cv::waitKey(0); emit signal_findResult(true, listRect);
} cv::Mat FindDifferenceManager::image2Mat(QImage image)
{
cv::Mat mat;
switch(image.format())
{
case QImage::Format_ARGB32:
case QImage::Format_RGB32:
case QImage::Format_ARGB32_Premultiplied:
mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
cv::cvtColor(mat, mat, CV_BGRA2BGR);
break;
case QImage::Format_RGB888:
mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());
cv::cvtColor(mat, mat, CV_BGR2RGB);
break;
case QImage::Format_Indexed8:
case QImage::Format_Grayscale8:
mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
break;
default:
qDebug() << __FILE__ << __LINE__ << "error to image2Mat !!! imge.format =" << image.format();
}
return mat;
}
 

项目实战:Qt+OpenCV大家来找茬(Qt抓图,穿透应用,识别左右图区别,框选区别,微调位置)的更多相关文章

  1. 项目实战:Qt+Ffmpeg+OpenCV相机程序(打开摄像头、支持多种摄像头、分辨率调整、翻转、旋转、亮度调整、拍照、录像、回放图片、回放录像)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  2. 【QT】找茬外挂制作

    找茬外挂制作 找茬游戏大家肯定都很熟悉吧,两张类似的图片,找里面的不同.在下眼神不大好,经常瞪图片半天也找不到区别.于是乎决定做个辅助工具来解放一下自己的双眼. 一.使用工具 Qt:主要是用来做界面的 ...

  3. 【大话QT之十六】使用ctkPluginFramework插件系统构建项目实战

    "使用ctkPluginFramework插件系统构建项目实战",这篇文章是写博客以来最纠结的一篇文章. 倒不是由于技术都多么困难,而是想去描写叙述一个项目架构採用ctkPlugi ...

  4. 项目实战:Qt手机模拟器拉伸旋转框架

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  5. Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码)

    Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码)    转 https://blog.csdn.net/lhl1124281072/article/details/800 ...

  6. QT5 QSS QML界面美化视频课程系列 QT原理 项目实战 C++1X STL

    QT5 QSS QML界面美化视频课程系列 QT原理 项目实战 C++1X STL 课程1   C语言程序设计高级实用速成课程 基础+进阶+自学 课程2   C语言程序设计Windows GDI图形绘 ...

  7. Win10下部署VS+Qt+OpenCV+darknet(YOLO)集成环境

    VS+Qt集成环境 下载VS与Qt并安装. VS:官网链接 Qt:下载链接 将Qt的bin目录加入PATH路径 bin目录举例:D:\development\Qt\5.12.0\msvc2017_64 ...

  8. 给缺少Python项目实战经验的人

    我们在学习过程中最容易犯的一个错误就是:看的多动手的少,特别是对于一些项目的开发学习就更少了! 没有一个完整的项目开发过程,是不会对整个开发流程以及理论知识有牢固的认知的,对于怎样将所学的理论知识应用 ...

  9. 【腾讯Bugly干货分享】React Native项目实战总结

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e16a7640ad7b4682c64a7 “8小时内拼工作,8小时外拼成长 ...

随机推荐

  1. 学习RabbitMQ(三)

    1 用户注册后(会立即提示注册成功),过一会发送短信和邮件通知 发布/订阅模型 以上模式一般是用户注册成功后,写入一条数据到mysql,在发送一条消息到MQ! 如果不用消息中间件(或者简单的做成异步发 ...

  2. 遇到的错误之"Exception in thread “main“ java.lang.NoClassDefFoundError: org/apache/velocity/context/Context at"

    问题 Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/velocity/context/ ...

  3. 使用Visual Studio查看C++类内存分布

    书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看编译器是怎么处理类成员内存分布的,特别是在继承.虚函数存在的情况下. 工欲善其事,必先利其器,我们先用好Visual Stu ...

  4. Azure DevOps 中 Dapr项目自动部署流程实践

    注:本文中主要讨论 .NET6.0项目在 k8s 中运行的 Dapr 的持续集成流程, 但实际上不是Dapr的项目部署到K8s也是相同流程,只是k8s的yaml配置文件有所不同 流程选择 基于 Dap ...

  5. 使用Bootstrap typeahead插件实现搜索框自动补全的配置参数。

    示例代码: <input type="text" id="addr"/> <input type="text" hidde ...

  6. 【Android开发】简单好用的阴影库 ShadowLayout

    先来看一张使用 ShadowLayout 库实现的各种阴影的效果图,如下图所示: 如上图所示,通过使用 ShadowLayout 可以控制阴影的颜色.范围.显示边界(上下左右四个边界).x 轴和 y ...

  7. try、catch、finally、return的执行顺序

    1. 不管有没有异常,finally里面的语句都会执行 2. 当try和catch中有返回语句时,finally里面的语句还是会执行 3. 如果finally里面没有return语句,try和catc ...

  8. python---选择排序的实现

    选择排序 思想 ​ 一趟遍历记录最小的数, 放到第一个位置 ​ 再一趟遍历记录剩余列表中最小的数, 继续放置 关键点: ​ 无序区: 第i趟, 无序区为 i~n-1 ​ 最小数的位置 import r ...

  9. ssm整合-ssmbuild

    目录 项目结构 导入相关的pom依赖 Maven资源过滤设置 建立基本结构和配置框架 Mybatis层编写 Spring层 Spring整合service层 SpringMVC层 Controller ...

  10. 讲解CPU之NUMA硬件体系以及机制(lscpu查看相关信息)

    先看看从系统层面反映出来的numa cpu信息.采样机器为实体机.80核.128内存. [root@ht2 src]# lscpu Architecture: x86_64 #x86架构下的64位 C ...