近期毕设需要使用图形化界面,想要尝试Qt。
安装配置
Windows7系统,VS2013, 官网上下载了Qt5.5.1版本,没敢用最新的。
具体为qt-opensource-windows-x86-msvc2013_64-5.5.1,VS插件下载了qt-vs-addin-1.2.5。
(官网归档 http://download.qt.io/archive/)
安装过程比较顺利,大小一共2G+。
打开VS,点击选项栏QT5下的Qt Options,添加Qt安装目录(比如E:\programs_store\Qt\5.5\msvc2013_64)为路径。
之后新建Qt项目,可以发现include都起了红线,右键项目属性,给配置属性>C/C++>常规>附加包含目录加上Include,以为为例,路径为E:\programs_store\Qt\5.5\msvc2013_64\include。
以HelloWorld作为测试。
此刻的我对Qt框架一无所知,先照葫芦画瓢,include一下QLabel,然后
QLabel label("Hello,world");
label.show();
编译,成功,运行,成功。哇,意外地顺利,以为会遇到花式报错来着。
那么下面开始了解Qt的框架,学习资料为https://www.devbean.net/2012/08/qt-study-road-2-catelog/
与OpenCV结合
在初步了解Qt之后,我将先前的项目文件(.h, .cpp)先加入了新建的Qt项目,接着需要配置OpenCV。此处遇到了困难,在教程中,VS+Qt+OpenCV的组合里,配置OpenCV的过程和普通VS项目相同,而当我加入事先保存好的项目属性表时,编译出现很多错误,都是ERROR LNK 2001,无法解析的外部符号。
查询LNK 2001错误的可能原因,这篇文章有整理一些 https://blog.csdn.net/djinglan/article/details/6682193
梳理一遍自己的环境,
VS2013, Qt5.5.1_msvc2013_64, OpenCV 2.4.13,并且在配置文件里使用的是x86,VS项目调试模式为Debug-x64
会不会是x64和x86的区别呢?
那么更新OpenCV相关的属性,全部更换成x64试一试。
首先电脑的环境变量-系统变量,PATH加入x64的bin路径;
接着修改项目属性表,老三样,1)VC++目录>包含目录,2)VC++目录>库目录,3)链接器>输入>附加依赖项
因为更新了系统变量,重启试试。
重启之后编译程序,顺利通过!
MainWindow布局
参考网上教程(大多直接在main里写)和官方BasicLayouts示例(是QDialogue类)进行布局测试,发现怎么也显示不出正常的效果,menuBar不显示,一片空白,或者label堆叠在一起,没有起到layout的作用……令人烦躁。
查询到:Note: Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.
这可能是关键。新建项目时自动生成的是继承于QMainWindow的,所以应该先新建一个widget再进行布局。
main函数是很简洁的show这个MainWindow类,具体的编辑就在类里了。
我的窗体总体为水平布局,最左边是一张图,中间是三个竖列的按钮,最右边是另一张图,所以是水平-竖直的嵌套。
以下是.h文件:
#ifndef SKELETONCORRECTION_H
#define SKELETONCORRECTION_H
//主界面
#include <QtWidgets/QMainWindow>
#include "ui_skeletoncorrection.h"
QT_BEGIN_NAMESPACE
class QAction;
class QLabel;
class QGroupBox;
class QMenu;
class QMenuBar;
class QPushButton;
QT_END_NAMESPACE
class SkeletonCorrection : public QMainWindow
{
Q_OBJECT
public:
SkeletonCorrection(QWidget *parent = 0);
~SkeletonCorrection();
void createMenu();
void createLabels();
void createButtons();
private:
QWidget* cenWidget;
QLabel *lLabel, *rLabel;
QPushButton *btn_Root;
QPushButton *btn_Correction;
QPushButton *btn_Save;
QVBoxLayout *verBtnLayout;
QMenuBar *menuBar;
QMenu *fileMenu;
};
#endif // SKELETONCORRECTION_H
以下是.cpp文件:
#include "skeletoncorrection.h"
#include <qaction.h>
#include <qmenubar.h>
#include <qmessagebox.h>
#include <qstatusbar.h>
#include <qtoolbar.h>
#include <qlabel.h>
#include <qpushbutton.h>
SkeletonCorrection::SkeletonCorrection(QWidget *parent)
: QMainWindow(parent)
{
cenWidget = new QWidget(this);//point to QMainWindow
setCentralWidget(cenWidget);
createMenu();//尚未实现
createLabels();
createButtons();
QHBoxLayout *mainLayout = new QHBoxLayout(cenWidget);//主要布局,指定parent为cenWidget
mainLayout->addWidget(lLabel);
verBtnLayout = new QVBoxLayout;//嵌套布局,竖直
verBtnLayout->addWidget(btn_Root);
verBtnLayout->addWidget(btn_Correction);
verBtnLayout->addWidget(btn_Save);
mainLayout->addLayout(verBtnLayout);
mainLayout->addWidget(rLabel);
setWindowTitle(tr("Skeleton Correction"));
}
SkeletonCorrection::~SkeletonCorrection()
{
}
void SkeletonCorrection::createMenu(){
}
void SkeletonCorrection::createLabels(){
lLabel = new QLabel(tr("Left"));
rLabel = new QLabel(tr("Right"));
}
void SkeletonCorrection::createButtons(){
btn_Root= new QPushButton(tr("Set Root Point"));
btn_Correction = new QPushButton(tr("Auto Correction"));
btn_Save = new QPushButton(tr("Save to Local Disk"));
}
运行结果:
符合期望。
因为这个widget的缘故耗费了一两天……前途堪忧。
菜单栏
加入菜单栏,一开始是新建了MenuBar对象,将这个对象加入到cenWidget,发现长度很短,不能撑起整条,设置parent为this也是一样。后来不新建对象,使用MainWindow类自带的MenuBar(),就OK了……
void SkeletonCorrection::createMenu(){
openAction = new QAction(tr("&Open..."), this);
openAction->setShortcuts(QKeySequence::Open);
connect(openAction, &QAction::triggered, this, &SkeletonCorrection::open);
//menuBar = new QMenuBar(cenWidget);
//fileMenu = menuBar->addMenu(tr("&File"));
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(openAction);
}
另外设置了一下margin,所以边缘多了些空白。
显示图片
利用QLabel来显示图片。
首先,设置菜单动作,打开本地图片,测试输出路径。
void SkeletonCorrection::createMenu(){
//set action
fileOpenAction = new QAction(tr("&Open..."), this);
fileOpenAction->setShortcuts(QKeySequence::Open);
connect(fileOpenAction, SIGNAL(triggered()), this, SLOT(fileOpenActionSlot()));
//set menu
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(fileOpenAction);
}
void SkeletonCorrection::createContextMenu(){
this->addAction(fileOpenAction);
this->setContextMenuPolicy(Qt::ActionsContextMenu);
}
void SkeletonCorrection::fileOpenActionSlot(){
selectFile();
}
void SkeletonCorrection::selectFile(){
QFileDialog *fileDialog = new QFileDialog(this);
fileDialog->setWindowTitle(tr("Open Image"));
fileDialog->setDirectory(".");
fileDialog->setNameFilter(tr("Images(*.png *.jpg *.jpeg *.bmp"));
fileDialog->setViewMode(QFileDialog::Detail);
QStringList fileName;
if (fileDialog->exec()){
fileName = fileDialog->selectedFiles();
hasSrcImg = true;//布尔变量
}
for (auto tmp : fileName){
qDebug() << tmp << endl;
srcImg = imread(tmp.toLatin1().data());
}
updateLeftLabel();//更新左边标签,显示原图像
}
可以在输出处看到图片路径被正确输出了。之后是显示图片。
在selectFile函数末尾调用updateLeftLavel函数更新lLabel, 类新建Mat类型成员变量srcImg来保存图片。显示时,先转换为QImage再进行处理。
void SkeletonCorrection::updateLeftLabel(){
cvtColor(srcImg, srcImg, CV_RGB2RGBA);
QImage qImg = QImage((const unsigned char*)(srcImg.data), srcImg.cols, srcImg.rows, QImage::Format_RGB32);
lLabel->setPixmap(QPixmap::fromImage(qImg));
lLabel->resize(lLabel->pixmap()->size());
}
可以在label中显示图片了。
PushButton点击事件
对于事件的理解,可以参见这篇文章。
点击按钮,触发一些动作,最简单的就是利用信号与信号槽。
//.h文件中
private slots:
ointSlot();//设置根关节点(颈部)按钮的槽函数
void autoCorrectionSlot();//自动纠正按钮的槽函数
//.cpp文件中
void SkeletonCorrection::createButtons(){
btn_Root= new QPushButton(tr("Set Root Point"));
btn_Correction = new QPushButton(tr("Auto Correction"));
btn_Save = new QPushButton(tr("Save to Local Disk"));
//创建button时,用connect函数将按钮与槽函数联系起来
connect(btn_Root, SIGNAL(clicked()), this, SLOT(setRootPointSlot()));
connect(btn_Correction, SIGNAL(clicked()), this, SLOT(autoCorrectionSlot()));
}
另一种情况是使用MouseEvent,点击鼠标时,想要有针对性地实现一些功能。那么就要在类成员函数里对对应的鼠标事件函数进行重载,比如我想要在鼠标release时实现一些功能。
//.h
protected:
void mouseReleaseEvent(QMouseEvent *e);//鼠标释放事件
//.cpp
void SkeletonCorrection::mouseReleaseEvent(QMouseEvent *e){
QPoint curPos = e->pos() - (cenWidget->pos() + lLabel->pos());
qDebug() << "mouse release position " << curPos.x() << " " << curPos.y() << endl;
}
我想要获取鼠标在一个label中的相对位置,根据这篇文章所说,结果正确。
我所需要的基本功能就是这样,之后就添加一些messageBox之类,微调。
以上。