赞
踩
本博客将讲解如何用Qt+OpenCV开发一款图片查看器的Windows应用程序,其实不用OpenCV也能开发出这类软件,作者目的是为了学习Qt+OpenCV开发项目,所以会使用OpenCV,本人会将项目开发的源代码上传到CSDN资源供大家学习参考,下载链接在文末。该款软件要实现的主要功能如下:
开发环境:Windows7(64) OpenCV4.1.0(支持VC14) QtCreator4.3.0(编译器使用VSVC2015 64bit)
打开Qt Creator,文件->新建文件或项目->Application->Qt Widgets Application,然后根据向导一步步创建项目即可。本人项目名取为TDPictureViewer,为本项目选择的编译器是MSVC2015 64bit,类名取为TdMainWindow。
注意,请确保开发本项目以前MSVC2015 64bit编译器和能安装的OpenCV能正常使用。如果电脑没有装VS2015和Windows Kits的相应调试器,则Qt Creator中的MSVC2015 64bit编译器将无法正常使用。如果你安装的OpenCV不支持vc14,则无法正常使用,官网下载的exe文件较高版本中一般都支持vc14和vc5,如本人安装的OpenCV4.1.0。如若你想要选择MinGW编译器,则需要下载OpenCV源码,然后使用cmake编译该源码,方法见本人另一篇博客【Qt+OpenCV项目开发学习】一、环境配置。
要想在项目中使用OpenCV,需要进行简单配置。双击打开项目文件(即后缀名为pro的文件),本人打开的是TDPictureViewer.pro文件。在该文件最后面加几行代码,代码如下所示。注意本人OpenCV安装在C:/Qt/opencv410路径下,如果你的OpenCV安装在其他路径,请修改下面代码中的路径。请将OpenCV安装在全英文的路径下。
TDPictureViewer.pro:
-
-
- # OpenCV配置
- CONFIG(release, debug|release): LIBS += -LC:/Qt/opencv410/build/x64/vc14/lib/ -lopencv_world410
- else:CONFIG(debug, debug|release): LIBS += -LC:/Qt/opencv410/build/x64/vc14/lib/ -lopencv_world410d
-
- INCLUDEPATH += C:/Qt/opencv410/build/include
- DEPENDPATH += C:/Qt/opencv410/build/include
想要实现界面的多语言,即本项目界面的中英文切换,也需要简单配置。也是在项目文件中加几行代码,代码如下,其中文件名可以任意取,只要区分开来并你知道哪个文件对应的是哪种语言即可。注意如果你想要某些代码中的一些字符串也能多语言,请使用tr()函数。
TDPictureViewer.pro:
- # 多语言配置
- TRANSLATIONS += \
- multi-language_cn.ts \
- multi-language_en.ts
本次项目主界面较为简单,加一个QGraphicsView控件用于显示图片,为菜单栏和工具栏的加点内容,然后修改控件名字等属性值,设置快捷键等操作,最后简单布局一下即可。不做详细介绍,上本人设计的主界面图片供大家参考。
ui界面设计好后,就需要实现相应功能,即要完成上述action控件的槽函数。QGraphicsView存放的图片的是QPixmap类型,而OpenCV中存放图片的是Mat类型,用OpenCV的imread()函数读取图片文件后,不能直接在QGraphicsView显示,所以在开始之前,需要实现一个功能就是Mat类型转化为QPixmap。此外,本人也写了一个readImage()函数用于读取图片文件,分别需要在源文件和头文件中加代码,代码如下:
tdmainwindow.h
- //别忘了包含头文件
- #include <opencv2/opencv.hpp>
-
-
- public:
- QPixmap readImage(QString FileName);
- QPixmap mat2QPixmap(const cv::Mat &img);
tdmainwindow.cpp
- QPixmap TdMainWindow::mat2QPixmap(const cv::Mat &img)
- {
- QPixmap imgQ;
- if(img.empty())
- {
- return imgQ;
- }
- if(img.channels() == 1)//灰度图像
- {
- QImage Qi = QImage( img.data, img.cols, img.rows,
- img.cols * img.channels(),
- QImage::Format_Grayscale8 );
- imgQ = QPixmap::fromImage(Qi);
- }
- else if(img.channels() == 3)//彩色图像
- {
- cv::Mat RGBimg;
- cv::cvtColor(img, RGBimg, cv::COLOR_BGR2RGB);
- QImage Qi = QImage( RGBimg.data, RGBimg.cols, RGBimg.rows,
- RGBimg.cols * RGBimg.channels(),
- QImage::Format_RGB888 );
- imgQ = QPixmap::fromImage(Qi);
- }
- return imgQ;
- }
- QPixmap TdMainWindow::readImage(QString FileName)
- {
- QPixmap imgQ;
- cv::Mat imgMat = cv::imread(FileName.toStdString());
- //格式转化
- imgQ = mat2QPixmap(imgMat);
- return imgQ;
- }
在完成基本功能时,还有有一些其他辅助的代码添加,如要加一些变量存放数据,辅助的方法实现等,代码如下:
tdmainwindow.h
- //别忘了包含必要的头文件,这里加的头文件仅供参考
- #include <QGraphicsScene>
- #include "formabout.h"//这是自己设计的一个子窗体,后面会提到它
-
-
- public:
- void initUI(); //将ui的一些设置操作放在该方法中
- void initConnect(); //将所有的信号与槽的连接都放在该方法中
- void writeIniFile(QString Language); //把语言设置写入配置文件中,用于多语言功能实现
- QString readIniFile(); //读取配置文件中的语言设置,,用于多语言功能实现
-
- private:
- QGraphicsScene *scene; //用于存放要显示图片
- FormAbout *about; //关于子窗体,当点击关于/帮助时,会显示该子窗体
- QStringList fileNames; //用于存放图片文件名
- QString currentDir; //用于存放路径
- QString TitleName;//用于存放程序标题名
- int currentNum;//当前显示的图片序号
- int Maxnum;//图片数量
tdmainwindow.cpp
- //别忘了包含必要的头文件
- #include <QSettings>
-
- TdMainWindow::TdMainWindow( QWidget *parent ) :
- QMainWindow(parent),
- ui(new Ui::TdMainWindow),
- scene(new QGraphicsScene),
- about(new FormAbout)
- {
- ui->setupUi(this);
- initUI();
- initConnect();
- }
- TdMainWindow::~TdMainWindow()
- {
- delete ui;
- delete scene;
- delete about;
- }
- void TdMainWindow::initUI()
- {
- about->setWindowFlags(Qt::WindowCloseButtonHint);
- QString language = readIniFile();
- if(language == "chinese")
- {
- ui->actionChinese->setChecked(true);
- ui->actionEnglish->setChecked(false);
- }
- else
- {
- ui->actionChinese->setChecked(false);
- ui->actionEnglish->setChecked(true);
- }
- this->TitleName = this->windowTitle();
- }
- void TdMainWindow::initConnect()
- {
- //目前内容为空
- }
- QString TdMainWindow::readIniFile()
- {
- QSettings *settings = new QSettings("./SystemSettings.ini", QSettings::IniFormat);
- QString language = settings->value("language", "chinese").toString();
- delete settings;
- return language;
- }
- void TdMainWindow::writeIniFile(QString Language)
- {
- QSettings *settings = new QSettings("./SystemSettings.ini", QSettings::IniFormat);
- settings->setValue("language", Language);
- delete settings;
- }
现在要实现按某个按钮后能执行想要的操作。如读取图片、文件夹下图片、上一张、下一张、放大、缩小、适应窗体显示、中英文切换等,不做过多介绍,直接上代码。
tdmainwindow.h
- public slots:
- void actionOpenPictureTriggeredSlot();
- void actionOpenFolderTriggeredSlot();
- void actionViewPreviousTriggeredSlot();
- void actionViewNextTriggeredSlot();
- void actionZoomInTriggeredSlot();
- void actionZoomOutTriggeredSlot();
- void actionZoomToFitTriggeredSlot();
- void actionChineseTriggeredSlot();
- void actionEnglishTriggeredSlot();
- void actionAboutTriggeredSlot();
- void actionHelpTriggeredSlot();
tdmainwindow.cpp
- //注意包含必要的头文件,仅供参考
- #include <QMessageBox>
- #include <QStringList>
- #include <QGraphicsView>
-
- void TdMainWindow::initConnect()
- {
- connect( ui->actionOpen_Picture,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionOpenPictureTriggeredSlot()) );
- connect( ui->actionOpen_Folder,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionOpenFolderTriggeredSlot()) );
- connect( ui->actionView_Previous,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionViewPreviousTriggeredSlot()) );
- connect( ui->actionView_Next,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionViewNextTriggeredSlot()) );
- connect( ui->actionZoom_In,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionZoomInTriggeredSlot()) );
- connect( ui->actionZoom_Out,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionZoomOutTriggeredSlot()) );
- connect( ui->actionAdapt_Window,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionZoomToFitTriggeredSlot()) );
- connect( ui->actionChinese,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionChineseTriggeredSlot()) );
- connect( ui->actionEnglish,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionEnglishTriggeredSlot()) );
- connect( ui->actionAbout,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionAboutTriggeredSlot()) );
- connect( ui->actionHelp,
- SIGNAL(triggered(bool)),
- this,
- SLOT(actionHelpTriggeredSlot()) );
- connect( ui->actionExit,
- SIGNAL(triggered(bool)),
- this,
- SLOT(close()) );
- }
-
- void TdMainWindow::actionOpenPictureTriggeredSlot()
- {
- //选择图像文件
- QString fileName = QFileDialog::getOpenFileName(this,
- tr("Open Image"),
- "C:/",
- tr("Image Files (*.png *.jpg *.bmp)")
- );
- if(!fileName.isEmpty())
- {
- this->fileNames.clear();
- this->fileNames += fileName;
- this->currentNum = 0;
- this->Maxnum = 1;
- QPixmap imgQ = readImage(fileNames[currentNum]);
- if(!imgQ.isNull())
- {
- //将图片加到场景中
- this->scene->clear();
- this->scene->addPixmap(imgQ);
- ui->graphicsView->setScene(this->scene);
- //显示
- ui->graphicsView->show();
- this->setWindowTitle(this->TitleName + " - " + fileNames[currentNum]);
- }
- }
-
- }
- void TdMainWindow::actionOpenFolderTriggeredSlot()
- {
- //用户选择文件夹
- QString dirStr = QFileDialog::getExistingDirectory(this,
- tr("Open Directory"),
- "C:/",
- QFileDialog::ShowDirsOnly
- );
- if(!dirStr.isEmpty())
- {
- //筛选出文件夹下的图片文件
- QDir dir = QDir(dirStr);
- QStringList filters;
- filters << "*.png" << "*.jpg" << "*.bmp";
- dir.setNameFilters(filters);
- QStringList fms = dir.entryList();
- if(!fms.isEmpty())
- {
- this->fileNames.clear();
- this->fileNames = fms;
- this->currentDir = dirStr + "/";
- this->currentNum = 0;
- this->Maxnum = this->fileNames.length();
- //读取文件夹下的图片文件并显示
- QPixmap imgQ = readImage(this->currentDir + this->fileNames[this->currentNum]);
- if(!imgQ.isNull())
- {
- //将所有图片加到场景中
- this->scene->clear();
- this->scene->addPixmap(imgQ);
- ui->graphicsView->setScene(this->scene);
- //显示
- ui->graphicsView->show();
- this->setWindowTitle(this->TitleName + " - " + this->currentDir + this->fileNames[this->currentNum]);
- }
- }
- }
- }
- void TdMainWindow::actionViewPreviousTriggeredSlot()
- {
- if(ui->graphicsView->items().isEmpty())
- {
- return;
- }
- this->currentNum--;
- if(this->currentNum < 0)
- {
- this->currentNum = this->Maxnum - 1;
- }
- //读取文件夹下的图片文件并显示
- QPixmap imgQ = readImage(this->currentDir + this->fileNames[this->currentNum]);
- if(!imgQ.isNull())
- {
- //将所有图片加到场景中
- this->scene->clear();
- this->scene->addPixmap(imgQ);
- ui->graphicsView->setScene(this->scene);
- //显示
- ui->graphicsView->show();
- this->setWindowTitle(this->TitleName + " - " + this->currentDir + this->fileNames[this->currentNum]);
- }
- }
- void TdMainWindow::actionViewNextTriggeredSlot()
- {
- if(ui->graphicsView->items().isEmpty())
- {
- return;
- }
- this->currentNum++;
- if(this->currentNum >= this->Maxnum)
- {
- this->currentNum = 0;
- }
- //读取文件夹下的图片文件并显示
- QPixmap imgQ = readImage(this->currentDir + this->fileNames[this->currentNum]);
- if(!imgQ.isNull())
- {
- //将所有图片加到场景中
- this->scene->clear();
- this->scene->addPixmap(imgQ);
- ui->graphicsView->setScene(this->scene);
- //显示
- ui->graphicsView->show();
- this->setWindowTitle(this->TitleName + " - " + this->currentDir + this->fileNames[this->currentNum]);
- }
- }
- void TdMainWindow::actionZoomInTriggeredSlot()
- {
- if(ui->graphicsView->items().isEmpty())
- {
- return;
- }
- ui->graphicsView->scale(1.1, 1.1);
- }
- void TdMainWindow::actionZoomOutTriggeredSlot()
- {
- if(ui->graphicsView->items().isEmpty())
- {
- return;
- }
- ui->graphicsView->scale(0.9, 0.9);
- }
- void TdMainWindow::actionZoomToFitTriggeredSlot()
- {
- if(ui->graphicsView->items().isEmpty())
- {
- return;
- }
- ui->graphicsView->setTransformationAnchor(QGraphicsView::AnchorViewCenter);
- int vh = ui->graphicsView->height()-4;
- int sh = ui->graphicsView->scene()->height();
- qreal m22 = (qreal)vh / sh;
- QMatrix q;
- q.setMatrix(m22,
- ui->graphicsView->matrix().m12(),
- ui->graphicsView->matrix().m21(),
- m22,
- ui->graphicsView->matrix().dx(),
- ui->graphicsView->matrix().dy()
- );
- ui->graphicsView->setMatrix(q);
- }
- void TdMainWindow::actionChineseTriggeredSlot()
- {
- ui->actionChinese->setChecked(true);
- ui->actionEnglish->setChecked(false);
- writeIniFile("chinese");
- QMessageBox::information(this,
- tr("Warning"),
- tr("Restart the software for the settings to take effect"));
- }
- void TdMainWindow::actionEnglishTriggeredSlot()
- {
- ui->actionChinese->setChecked(false);
- ui->actionEnglish->setChecked(true);
- writeIniFile("English");
- QMessageBox::information(this,
- tr("Warning"),
- tr("Restart the software for the settings to take effect"));
- }
- void TdMainWindow::actionAboutTriggeredSlot()
- {
- about->show();
- }
- void TdMainWindow::actionHelpTriggeredSlot()
- {
- about->show();
- }
前面有用到一个FormAbout子窗体,在这简单讲解以下。在项目上右键->添加新文件->Qt->Qt设计师界面类->choose。然后根据向导一步步完成即可。本人让界面继承Widget,类名为FormAbout。创建好后,只需要简单设计以下ui文件即可,上我设计的界面供大家参考。
此外要想实现鼠标控制图片缩放、移动等功能,则需要继承QGraphicsView控件,写个自定义控件,重新实现其鼠标事件。方法是,在项目上右键->添加新文件->C++->C++ Class->choose。然后根据向导一步步完成即可。类名取为TDGraphicsView,继承QGraphicsView。创建好后,写好头文件和源文件,代码如下:
tdgraphicsview.h
- #ifndef TDGRAPHICSVIEW_H
- #define TDGRAPHICSVIEW_H
-
- #include <QObject>
- #include <QGraphicsView>
-
- class TDGraphicsView : public QGraphicsView
- {
- Q_OBJECT
-
- public:
- TDGraphicsView(QWidget *parent = 0);
- void mouseMoveEvent(QMouseEvent *event);
- void mousePressEvent(QMouseEvent *event);
- void mouseReleaseEvent(QMouseEvent *event);
- void mouseDoubleClickEvent(QMouseEvent *event);
- void wheelEvent(QWheelEvent *event);
- signals:
- void mousePress(QPoint point); //鼠标按下
- void mouseMove(QPoint point); //鼠标移动
- void mouseDoubleClick(QPoint point);//鼠标双击
- void mouseRelease(QPoint point); //鼠标释放
- void wheelScroll(bool direction); //滚轮滚动
- };
-
- #endif // TDGRAPHICSVIEW_H
tdgraphicsview.cpp
- #include "tdgraphicsview.h"
- #include<QMouseEvent>
- TDGraphicsView::TDGraphicsView(QWidget *parent):QGraphicsView(parent)
- {
-
- }
- void TDGraphicsView::mousePressEvent(QMouseEvent *event)
- {
- if (event->button()==Qt::LeftButton)
- {
- QPoint point=event->pos(); //QGraphicsView的坐标
- emit mousePress(point);
- }
- QGraphicsView::mousePressEvent(event);
- }
- void TDGraphicsView::mouseReleaseEvent(QMouseEvent *event)
- {
- if (event->button()==Qt::LeftButton)
- {
- QPoint point=event->pos(); //QGraphicsView的坐标
- emit mouseRelease(point);
- }
- QGraphicsView::mouseReleaseEvent(event);
- }
- void TDGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
- {
- if (event->button()==Qt::LeftButton)
- {
- QPoint point=event->pos(); //QGraphicsView的坐标
- emit mouseDoubleClick(point);
- }
- QGraphicsView::mouseDoubleClickEvent(event);
- }
- void TDGraphicsView::mouseMoveEvent(QMouseEvent *event)
- {
- if(event->buttons() & Qt::LeftButton)
- {
- QPoint point = event->pos(); //QGraphicsView的坐标
- emit mouseMove(point);
- }
- }
- void TDGraphicsView::wheelEvent(QWheelEvent *event)
- {
- if(event->delta() > 0)
- {
- emit wheelScroll(true);
- }
- else
- {
- emit wheelScroll(false);
- }
- }
要想实现鼠标移动图片、双击图片变成合适大小、滚轮缩放图片等功能,首先切换到主界面的ui设计中,将QGraphicsView控件提升为上面实现的TDGraphicsView自定义控件。方法鼠标放置在控件位置,右键选择提升为->TDGraphicsView,如若没有,请自行输入提升的类名称。完成以上操作后即可开始实现鼠标的单击、释放、移动、双击以及滚轮功能。代码如下:
tdmainwindow.h
- public slots:
- void mousePressSlot(QPoint point);
- void mouseReleaseSlot(QPoint point);
- void mouseMoveSlot(QPoint point);
- void wheelScrollSlot(bool direction);
- private:
- QPointF offset;
tdmainwindow.cpp
- void TdMainWindow::initConnect()
- {
- connect(ui->graphicsView,
- SIGNAL(mousePress(QPoint)),
- this,
- SLOT(mousePressSlot(QPoint)) );
- connect(ui->graphicsView,
- SIGNAL(mouseRelease(QPoint)),
- this,
- SLOT(mouseReleaseSlot(QPoint)) );
- connect(ui->graphicsView,
- SIGNAL(mouseMove(QPoint)),
- this,
- SLOT(mouseMoveSlot(QPoint)) );
- connect(ui->graphicsView,
- SIGNAL(wheelScroll(bool)),
- this,
- SLOT(wheelScrollSlot(bool)) );
- connect(ui->graphicsView,
- SIGNAL(mouseDoubleClick(QPoint)),
- this,
- SLOT(actionZoomToFitTriggeredSlot()) );
- }
-
- void TdMainWindow::mousePressSlot(QPoint point)
- {
- if(ui->graphicsView->items().isEmpty())
- {
- return;
- }
- QCursor cursor;
- cursor.setShape(Qt::ClosedHandCursor);
- QApplication::setOverrideCursor(cursor);
- QPointF pointScence = ui->graphicsView->mapToScene(point); //转换到Scene坐标
- offset = pointScence;
-
- }
- void TdMainWindow::mouseReleaseSlot(QPoint point)
- {
- QApplication::restoreOverrideCursor();
- }
-
- void TdMainWindow::mouseMoveSlot(QPoint point)
- {
- if(ui->graphicsView->items().isEmpty())
- {
- return;
- }
- QPointF tmp = ui->graphicsView->mapToScene(point) - offset; //转换到Scene坐标
- int x = ui->graphicsView->horizontalScrollBar()->sliderPosition() - tmp.toPoint().x();
- int y = ui->graphicsView->verticalScrollBar()->sliderPosition() - tmp.toPoint().y();
- ui->graphicsView->horizontalScrollBar()->setSliderPosition(x);
- ui->graphicsView->verticalScrollBar()->setSliderPosition(y);
- }
- void TdMainWindow::wheelScrollSlot(bool direction)
- {
- if(direction)
- {
- actionZoomInTriggeredSlot();
- }
- else
- {
- actionZoomOutTriggeredSlot();
- }
- }
首先需要编译一下,然后点击工具->外部->Qt预言家->更新翻译(lupdate),此时项目文件夹下会产生两个ts文件如下图所示。
然后请用MVSC 2015(64-bit)下的Linguist程序进行翻译工作,如果原程序是全用的英文,则只需要打开中文ts文件,,注意如果你是别的编译器,就要选择相应编译器下的Linguist程序进行翻译工作。以下截图供大家参考。
翻译好后保存ts文件,关闭Linguist程序,然后点击工具->外部->Qt预言家->发布翻译(lrelease),此时项目文件夹下会产生两个qm文件。
最后还需要在main函数中加代码,注意多语言的相关代码,一定要在窗体创建之前加,且本次多语言实现重启软件后界面才会更改,没有热切换功能,代码如下:
main.cpp
- QApplication a(argc, argv);
- //多语言,以下为加的代码
- QTranslator *translator = new QTranslator();
- QSettings *settings = new QSettings("./SystemSettings.ini", QSettings::IniFormat);
- QString language = settings->value("language", "chinese").toString();
- delete settings;
- if(language == "chinese")
- {
- translator->load("./multi-language_cn.qm");
- a.installTranslator(translator);
- }
- else
- {
- translator->load("./multi-language_en.qm");
- a.installTranslator(translator);
- }
- //以上为加的代码
- //创建主窗体并显示
- TdMainWindow w;
- w.show();
- return a.exec();
最后就是编译运行,测试应用程序相关功能,整个开发到此结束。贴上运行的结果图。
动图效果,鼠标移动图片,滚轮缩放图片、左键双击适应窗口显示。
中文界面
英文界面
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。