小喵的博客。小喵的博客。

小喵的博客,小喵的博客

小喵的唠叨话:话说最近小喵也如起来勾画论文了,想了少全面或无头脑,不掌握该写些什么。恰好又受分配了好几标注数据的办事,于是乎想写点代码,休闲一下。结果吧尽管是这首博客。对了,小喵对GUI编程一窍不通,只知Windows有MFC,Mac上之未明白。。。恰好听说过QT,而且知道是界面库是跨越平台的,也就算选用了此家伙了。

小喵的唠叨话:话说最近小喵也使开写论文了,想了片到家或没头绪,不了解该写些什么。恰好又为分配了一些标号数据的办事,于是乎想写点代码,休闲一下。结果为就是及时篇博客。对了,小喵对GUI编程一窍不通,只了解Windows有MFC,Mac上之非明了。。。恰好听说过QT,而且知道此界面库是越平台的,也不怕选用了这个家伙了。

 

 

正文系原创,转载请注明出处~

正文系原创,转载请注明出处~

小喵的博客:http://www.miaoerduo.com

小喵的博客:http://www.miaoerduo.com

博客原文:http://www.miaoerduo.com/qt/一个略粗暴的人脸认证标注工具的实现.html

博客原文:http://www.miaoerduo.com/qt/一个简短粗暴的食指脸认证标注工具的落实.html

 

 

那现在初步和小喵一起瞎猫似的捯饬QT吧~

那么现在始于与小喵一起瞎猫似的捯饬QT吧~

先看无异眼睛效果图:

先行押同样目效果图:

图片 1

图片 2

凡是未是新一圈还老炫酷。功能及呢尚吓,至少简单的标工作且能就了。那么吃咱来同样步一步的就这家伙吧。

是未是新一扣还特别炫酷。功能及啊尚好,至少简单的标工作都能够不辱使命了。那么受咱们来平等步一步的完成这个家伙吧。

同、功能需求

此序要的效力是成就一个丁脸认证的标注工具。

具体来说,就是加很多对准颜的图形,要标注一下当下无异针对性是休是跟一个人数。同时,每部分的图纸的人头脸一布置是生活照,一摆设凡证件照,需要以标注出啦张是证件照,那张凡生活照。照片还是由此检测及对同步之,这个家伙就需要完成简单的显得、标注、保存记录之行事便得。

当考虑到有时候要标注的list可能大非常,可以在跳转的效应。标注结果尚且保存在内存,用户可天天变动,点击保存,则形容副硬盘。

同、功能要求

这程序要的效用是好一个总人口脸认证的标工具。

具体来说,就是加很多对面部的图样,要标一下立马无异于针对凡无是跟一个人口。同时,每有之图的人口脸一布置凡生活照,一摆设是证件照,需要而标注有啦张凡证件照,那张是生活照。照片都是由此检测和针对性同之,这个家伙就待完成简单的来得、标注、保存记录之办事就是得。

理所当然考虑到有时候需要标注的list可能好要命,可以加入跳转的职能。标注结果都保存在内存,用户可天天变动,点击保存,则刻画副硬盘。

老二、数据结构

那么是免是今即令得入手写代码了呢?当然不是!

小喵写这软件一共用了3天的时空,第一上完成了一个超简单demo程序,熟悉了瞬间QT的事件添加,路径选择与出示图片的几乎独力量。之后以细致入微的思想了一下各种数码的结构,才动手做了就等同本子工具。没有一个清楚的数目的概念,会造成众多的无用功。所以,大家以描绘序的时,要当备等多花费一点工夫来想想,毕竟写代码才是最最简便的作业未是也?

  1. 输入数据格式:因为小喵的办事条件下,大家还对linux有一些了解,所以可以自行生成好图片的路的list,这里统一要求,list必须是偶数尽(2n行),代表n对,相邻之图纸也平针对性。
  2. 标明数据存储:考虑到我们不仅要标注是无是部分,还得标注哪张是证件照,所以不妨直接当朗诵数据的上便分为两客,这样尽管因此鲜只std::vector<std::string>来囤积就执行了。
  3. 标过程的状态:我们要掌握标注过程遭到之那些信息为?主要应该出:总数据量,当前曾标明的对数。
  4. 标结果:每一样针对性都发平等组针对许地
    结果,考虑到出4蒙受状态:未标注,不确定,不配合,匹配这四种植,我们定义一个枚举的状态表enum
    AnnoState就好。之后用一个std::vector<enum
    AnnoState>来储存标注结果。

亚、数据结构

这就是说是匪是现就是得入手写代码了啊?当然不是!

小喵写这个软件一共用了3龙之日子,第一天完成了一个超简单demo程序,熟悉了瞬间QT的波添加,路径选择跟出示图片的几单效益。之后又仔细的构思了瞬间各种数据的结构,才动手做了当下同本工具。没有一个清的数量的定义,会促成广大之无用功。所以,大家以形容序的时光,要在准备等多花费一点时刻来想,毕竟写代码才是无比简便易行的工作未是也?

  1. 输入数据格式:因为小喵的工作环境下,大家都指向linux有局部询问,所以可以自行生成好图片的途径的list,这里统一要求,list必须是偶数执(2n行),代表n对,相邻之图片为同针对。
  2. 标数据存储:考虑到我们不光用标注是无是一对,还得标注哪张是证件照,所以不妨直接当朗诵数据的时即便分为两客,这样即便用有限独std::vector<std::string>来囤积就行了。
  3. 标过程的状态:我们用懂得标注过程遭到的那些信息也?主要应来:总数据量,当前已标注的对数。
  4. 标结果:每一样针对还有一样组对许地
    结果,考虑到有4中状态:未标注,不确定,不兼容,匹配这四种植,我们定义一个枚举的状态表enum
    AnnoState就吓。之后用一个std::vector<enum
    AnnoState>来囤积标注结果。

其三、界面制作

GUI程序的界面直是单深受丁头疼的题材,记得在本科学习Java的时刻,需要团结亲手写一个控件,使用new
JButton()类似之法创造按钮,然后上加到主界面上,位置什么的都得调用这个目标来安,十分底繁琐。那么QT能不能够简化这进程为?答案是早晚之。

创办项目->选择Application->Qt Widgets
Application。然后项目名改成Anno
Pro,其他一切默认设置,就创办好了一个门类了。这个初步的类别中来3单文件夹:头文件,源文件与界面文件,以及一个.pro结尾的门类安排文件。

图片 3

既然要编制界面,我们自然会惦记翻转界面文件了,双击MainWindow.ui(我这里全部都是默认的名)。出现的凡一个充斥各种控件的可视化界面编辑器。

图片 4

随我们之前的界面样式,拖动左边的控件,就好完成界面的编制了。小喵这里就所以到了几乎栽控件:

QPushButton:各种按钮

QLabel:所以显得文字与图像的区域还是即刻是控件

QFrame:一个器皿,小喵用它们只有是为组织及再也清晰

QSlider:滑动条,小喵用的是程度滑动条

QStatusBar:状态栏,
这当是自带的,如果删掉的话,在MainWindow控件点击右键就足以创造了

拖动完成后,双击空间,就可为空间设置文本,同时注意让每个控件起一个满意的名(起名字怪重大之!《代码大全》中甚至用平等章节,好几十页的字数介绍如何命名)。

关于其他的控件,大家可以自动钻研。反正小喵现在之道行应该才是筑基。

那么我们虽欣然的成功了界面的编辑了~点击左右产的运作图标(三角形的十分),就可以看到祥和之运转程序了!

老三、界面制作

GUI程序的界面直是只特别为丁头疼的问题,记得在本科学习Java的上,需要协调亲手写一个控件,使用new
JButton()类似之方式创造按钮,然后上加到主界面上,位置什么的都得调用这个目标来安装,十分底繁琐。那么QT能不能够简化这过程为?答案是早晚的。

缔造项目->选择Application->Qt Widgets
Application。然后项目名改成Anno
Pro,其他一切默认设置,就创造好了一个列了。这个开始的路里来3只公文夹:头文件,源文件及界面文件,以及一个.pro结尾的档次配置文件。

图片 5

既是要编制界面,我们自然会想翻转界面文件了,双击MainWindow.ui(我这里全部都是默认的名)。出现的是一个充斥各种控件的可视化界面编辑器。

图片 6

比如我们前的界面样式,拖动左边的控件,就可好界面的编排了。小喵这里就所以到了几乎栽控件:

QPushButton:各种按钮

QLabel:所以展示文字及图像的区域还是即时这控件

QFrame:一个器皿,小喵用其仅是为着组织及还鲜明

QSlider:滑动条,小喵用的凡水平滑动条

QStatusBar:状态栏,
这应当是自带的,如果删掉的话,在MainWindow控件点击右键就好创建了

拖动完成后,双击空间,就足以被空间设置文本,同时注意让每个控件起一个顺心的名(起名字挺关键的!《代码大全》中竟为此相同回,好几十页的字数介绍如何命名)。

关于其他的控件,大家好活动钻研。反正小喵现在底道行应该才是筑基。

那么我们就算开心的做到了界面的编撰了~点击左右下的运转图标(三角形的充分),就可以看到温馨的运作程序了!

季、数据定义和初始化

俺们原先一度分析了咱要的数目了,这部分开始应用代码的概念这些组织。

打开我们/*唯一的*/头文件mianwindow.h,添加需要的变量,小喵就直拿温馨之条文件复制下来了:

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 
 4 #include <QMainWindow>
 5 #include <vector>
 6 #include <string>
 7 namespace Ui {
 8 
 9 class MainWindow;
10 }
11 
12 class MainWindow : public QMainWindow
13 {
14     enum AnnoState {
15         UNKNOWN = 0,  // 未标注
16         YES = 1,      // 匹配
17         NO = 2,       // 不匹配
18         UNSURE = 3    // 不确定
19     };
20 
21 public:
22     explicit MainWindow(QWidget *parent = 0);
23     ~MainWindow();
24 
25 private:
26     Ui::MainWindow *ui; // 自带的,ui界面的接口
27     std::vector<std::string> image_list_1;  // 用来存放左边的图片的list
28     std::vector<std::string> image_list_2;  // 用来存放右边的图片的list
29     int current_idx;                        // 当前图片对的id
30     int total_pair_num;                     // 总共的图片对的数目
31     std::vector< AnnoState > annotation_list;  // 标注的结果
32 };
33 
34 #endif // MAINWINDOW_H

可见见,小喵添加了一个enum的种类,用来表示标注结果的类别。虽然独自生4独状态,我们甚至好直接约定几个int值来代表,但相信自己,为如此4单状态定义一个枚举类型是全然有必要的。

以后咱们有的成员变量都是private的。具体意思,注释中呢来写明。

生一样步就是是初始化了。初始化的历程当然得写在构造函数里,这里,小喵于初始化的时光逼用户挑选一个标明的list,如果不这么做,会生出广大之飞情况。请见谅小喵的怠惰。。。

 1 MainWindow::MainWindow(QWidget *parent) :
 2     QMainWindow(parent),
 3     ui(new Ui::MainWindow)
 4 {
 5     ui->setupUi(this);
 6 
 7     // 选择输入文件
 8     while (1) {
 9         QString file_name = QFileDialog::getOpenFileName(this, "choose a file to annotate", ".");
10         if (file_name.isEmpty()) {
11             int ok = QMessageBox::information(this, "choose a file to annotate", "Don't want to work now?", QMessageBox::Ok | QMessageBox::Cancel);
12             if (ok == QMessageBox::Ok) {
13                 exit(0);
14             }
15             continue;
16         }
17         std::ifstream is(file_name.toStdString());
18         std::string image_name;
19         bool is_odd = true;
20         while (is >> image_name) {
21             if (is_odd) {
22                 this->image_list_1.push_back(image_name);
23             } else {
24                 this->image_list_2.push_back(image_name);
25             }
26             is_odd = !is_odd;
27         }
28         is.close();
29 
30         if (image_list_1.size() != image_list_2.size()) {
31             QMessageBox::information(this, "choose a file to annotate", "this image list is not even", QMessageBox::Ok);
32             continue;
33         }
34         if (0 == image_list_1.size()) {
35             QMessageBox::information(this, "choose a file to annotate", "this image list is empty", QMessageBox::Ok);
36             continue;
37         }
38         break;
39     }
40 
41     assert(image_list_1.size() == image_list_2.size());
42     // 初始化其他参数
43     this->total_pair_num = image_list_1.size();
44     this->current_idx = 0;
45     std::vector<AnnoState> annotation_list(this->total_pair_num, AnnoState::UNKNOWN);
46     this->annotation_list.swap(annotation_list);
47 
48     display();
49 }

这里用了少数只QT的零件:

QFileDialog:这个组件是一个文件对话框,其中有个别个深有效的函数:getOpenFileName用于选择一个文件,并回到文件称;getSaveFileName用于选择一个文书来保存数据,并返一个文本称。这有限单函数的参数很多,小喵只所以到了前面的3独,用到的参数依次是:父组件,标题,初始目录。其他的参数的效应,喵粉可以错过公共网查一下。

QMessageBox::information,这个函数的机能是显示一个音讯窗口。四只参数分别代表:父组件,标题,内容,按钮样式。

深信大家知道一点点C++的文化的话,很易看明白就段代码。

此就是是应用了一个循环,让用户选择文件,如果选成功了,则读取数据到我们的list中,最终初始化了别样的参数,在调用display函数来展示。这个display函数是我们团结编辑的,后面会说及。另外,assert函数是预言,他管了断言的数据的合法性,如果未合法,程序会离。想使这函数,需要包含头文件assert.h。

季、数据定义跟初始化

咱俩原先已分析了咱要之多少了,这片开始以代码的定义这些组织。

打开我们/*唯一的*/头文件mianwindow.h,添加需要之变量,小喵就一直拿自己的峰文件复制下来了:

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 
 4 #include <QMainWindow>
 5 #include <vector>
 6 #include <string>
 7 namespace Ui {
 8 
 9 class MainWindow;
10 }
11 
12 class MainWindow : public QMainWindow
13 {
14     enum AnnoState {
15         UNKNOWN = 0,  // 未标注
16         YES = 1,      // 匹配
17         NO = 2,       // 不匹配
18         UNSURE = 3    // 不确定
19     };
20 
21 public:
22     explicit MainWindow(QWidget *parent = 0);
23     ~MainWindow();
24 
25 private:
26     Ui::MainWindow *ui; // 自带的,ui界面的接口
27     std::vector<std::string> image_list_1;  // 用来存放左边的图片的list
28     std::vector<std::string> image_list_2;  // 用来存放右边的图片的list
29     int current_idx;                        // 当前图片对的id
30     int total_pair_num;                     // 总共的图片对的数目
31     std::vector< AnnoState > annotation_list;  // 标注的结果
32 };
33 
34 #endif // MAINWINDOW_H

足见到,小喵添加了一个enum的项目,用来代表标注结果的品种。虽然只生4只状态,我们甚至足以一直约定几独int值来表示,但相信我,为如此4个状态定义一个枚举类型是了产生必不可少之。

此后我们具备的成员变量都是private的。具体意思,注释中吗起写明。

生一样步就是是初始化了。初始化的历程当然得写以构造函数里,这里,小喵在初始化的时候逼用户挑选一个标注的list,如果未这么做,会产生很多的不测情况。请见谅小喵的怠惰。。。

 1 MainWindow::MainWindow(QWidget *parent) :
 2     QMainWindow(parent),
 3     ui(new Ui::MainWindow)
 4 {
 5     ui->setupUi(this);
 6 
 7     // 选择输入文件
 8     while (1) {
 9         QString file_name = QFileDialog::getOpenFileName(this, "choose a file to annotate", ".");
10         if (file_name.isEmpty()) {
11             int ok = QMessageBox::information(this, "choose a file to annotate", "Don't want to work now?", QMessageBox::Ok | QMessageBox::Cancel);
12             if (ok == QMessageBox::Ok) {
13                 exit(0);
14             }
15             continue;
16         }
17         std::ifstream is(file_name.toStdString());
18         std::string image_name;
19         bool is_odd = true;
20         while (is >> image_name) {
21             if (is_odd) {
22                 this->image_list_1.push_back(image_name);
23             } else {
24                 this->image_list_2.push_back(image_name);
25             }
26             is_odd = !is_odd;
27         }
28         is.close();
29 
30         if (image_list_1.size() != image_list_2.size()) {
31             QMessageBox::information(this, "choose a file to annotate", "this image list is not even", QMessageBox::Ok);
32             continue;
33         }
34         if (0 == image_list_1.size()) {
35             QMessageBox::information(this, "choose a file to annotate", "this image list is empty", QMessageBox::Ok);
36             continue;
37         }
38         break;
39     }
40 
41     assert(image_list_1.size() == image_list_2.size());
42     // 初始化其他参数
43     this->total_pair_num = image_list_1.size();
44     this->current_idx = 0;
45     std::vector<AnnoState> annotation_list(this->total_pair_num, AnnoState::UNKNOWN);
46     this->annotation_list.swap(annotation_list);
47 
48     display();
49 }

此地用了个别独QT的零部件:

QFileDialog:这个组件是一个文书对话框,其中起一定量单深管用之函数:getOpenFileName用于选择一个文书,并回文件称;getSaveFileName用于选择一个文本来保存数据,并回到一个文件称。这点儿只函数的参数很多,小喵只所以到了前头的3独,用到之参数依次是:父组件,标题,初始目录。其他的参数的效用,喵粉可以错过公共网查一下。

QMessageBox::information,这个函数的功能是显得一个信窗口。四只参数分别表示:父组件,标题,内容,按钮样式。

信任大家知道一点点C++的学问的话,很易看明白就段代码。

这里就是是以了一个循环往复,让用户选择文件,如果选择成功了,则读取数据到我们的list中,最终初始化了别样的参数,在调用display函数来展示。这个display函数是我们团结编辑的,后面会说及。另外,assert函数是预言,他管了断言的数据的合法性,如果未合法,程序会离。想采取这个函数,需要包含头文件assert.h。

五,添加风波响应

小喵之前了解及,QT使用的是千篇一律种信号和槽的波机制,是均等栽死尖端的机制。那么闹没发生什么简单的法子,为咱的每个控件绑定好的底波为?

以界面编辑界面下,右击欲加上事件的半空中,然后择转到槽。这时候会产生成千上万选,这里一直选择clicked就好。然后你会意识我们的mainwindow类中,多了一个pivate
slot的函数(也即是槽函数)。

图片 7

俺们好给各国一个需添加事件的函数都因此这种方法来绑定事件,最终头文件被见面出现这么的声明(函数名称的条条框框是:on_控件名_信号类型):

1 private slots:
2     void on_pushButton_save_clicked();
3     void on_pushButton_ok_clicked();
4     void on_pushButton_no_clicked();
5     void on_pushButton_unsure_clicked();
6     void on_pushButton_next_clicked();
7     void on_pushButton_prev_clicked();
8     void on_pushButton_switch_clicked();
9     void on_horizontalSlider_progress_sliderReleased();

每当来自文件被,也会见生成空的函数定义。我们无非待团结做到函数定义就是生功告成!

脚给出的凡除了save的有所的函数的概念。

一言九鼎办事是,给每个事件编写修改数据的代码,而休失背任何界面相关的有的。各个控件可以经过this->ui来装和获得。使用Qt
Creator的时刻,要充分利用智能提醒。

 1 /**
 2  * @brief MainWindow::on_pushButton_ok_clicked
 3  * 标注为"匹配"
 4  */
 5 void MainWindow::on_pushButton_ok_clicked()
 6 {
 7     this->annotation_list[this->current_idx] = MainWindow::AnnoState::YES;
 8     ++ this->current_idx;
 9     display();
10 }
11 
12 /**
13  * @brief MainWindow::on_pushButton_no_clicked
14  * 标注为"不匹配"
15  */
16 void MainWindow::on_pushButton_no_clicked()
17 {
18     this->annotation_list[this->current_idx] = MainWindow::AnnoState::NO;
19     ++ this->current_idx;
20     display();
21 }
22 
23 /**
24  * @brief MainWindow::on_pushButton_unsure_clicked
25  * 标注为"不确定"
26  */
27 void MainWindow::on_pushButton_unsure_clicked()
28 {
29     this->annotation_list[this->current_idx] = MainWindow::AnnoState::UNSURE;
30     ++ this->current_idx;
31     display();
32 }
33 
34 /**
35  * @brief MainWindow::on_pushButton_next_clicked
36  * 移动到下一组
37  */
38 void MainWindow::on_pushButton_next_clicked()
39 {
40     ++ this->current_idx;
41     display();
42 }
43 
44 /**
45  * @brief MainWindow::on_pushButton_prev_clicked
46  * 移动到上一组
47  */
48 void MainWindow::on_pushButton_prev_clicked()
49 {
50     -- this->current_idx;
51     display();
52 }
53 
54 /**
55  * @brief MainWindow::on_pushButton_switch_clicked
56  * 交换两边的图片
57  */
58 void MainWindow::on_pushButton_switch_clicked()
59 {
60     std::string tmp = this->image_list_1[this->current_idx];
61     this->image_list_1[this->current_idx] = this->image_list_2[this->current_idx];
62     this->image_list_2[this->current_idx] = tmp;
63     display();
64 }
65 
66 /**
67  * @brief MainWindow::on_horizontalSlider_progress_sliderReleased
68  * 拖放进度条,控制进度
69  */
70 void MainWindow::on_horizontalSlider_progress_sliderReleased()
71 {
72     int pos = this->ui->horizontalSlider_progress->value();
73     this->current_idx = pos;
74     this->display();
75 }

从那之后,我们的大致的法力逻辑就是编写了了。

那怎么为界面上亮我们的网状态也?注意到了俺们地方的各国一个函数都调用了display这个函数了也?这个函数正式承担绘制界面的效能。

一对着重介绍三单函数:

 1 const std::string UNSURE_FILE = ":Fileunsure.png";
 2 const std::string YES_FILE = ":Fileyes.gif";
 3 const std::string NO_FILE = ":Fileno.gif";
 4 const std::string UNKNOWN_FILE = ":Fileunknown.png";
 5 
 6 /**
 7  * @brief set_image 将图像设置到label上,图像自动根据label的大小来缩放
 8  * @param label
 9  * @param image
10  */
11 void set_image(QLabel *label, const QPixmap &image) {
12     float ratio(0.);
13     ratio = 1. * label->width() / image.width();
14     ratio = fmin( 1. * label->height() / image.height(), ratio );
15     QPixmap m = image.scaled(static_cast<int>(image.width() * ratio), static_cast<int>(image.height() * ratio));
16     label->setPixmap(m);
17 }
18 
19 void set_image(QLabel *label, const std::string image_path) {
20     QPixmap image(image_path.c_str());
21     set_image(label, image);
22 }
23 
24 /**
25  * @brief MainWindow::display \n
26  * 根据系统中的所有的变量来设置当前界面中的各个部分的内容
27  */
28 void MainWindow::display() {
29 
30     if (this->current_idx >= this->total_pair_num) {
31         QMessageBox::information(this, "annotation over", "Congratulations! You've finished all the job! Please save your work :)", QMessageBox::Ok);
32         this->current_idx = this->total_pair_num - 1;
33     }
34     if (this->current_idx < 0) {
35         QMessageBox::information(this, "annotation warning", "You must start at 0 (not a negative position, I konw you wanna challenge this app) :)", QMessageBox::Ok);
36         this->current_idx = 0;
37     }
38 
39     // 进度条
40     this->ui->horizontalSlider_progress->setRange(0, this->total_pair_num - 1);
41     this->ui->horizontalSlider_progress->setValue(this->current_idx);
42 
43     // 状态栏
44     this->ui->statusBar->showMessage(QString((std::to_string(this->current_idx + 1) + " / " + std::to_string(this->total_pair_num)).c_str()));
45 
46     // 文件名
47     std::string image_name_1 = this->image_list_1[this->current_idx];
48     std::string image_base_name_1 = image_name_1.substr(image_name_1.find_last_of("/") + 1);
49     std::string image_name_2 = this->image_list_2[this->current_idx];
50     std::string image_base_name_2 = image_name_2.substr(image_name_2.find_last_of("/") + 1);
51     this->ui->label_image_name_1->setText(image_base_name_1.c_str());
52     this->ui->label_image_name_2->setText(image_base_name_2.c_str());
53 
54     // 显示图像
55     set_image(this->ui->label_image_view_1, image_name_1);
56     set_image(this->ui->label_image_view_2, image_name_2);
57 
58     // 显示标注结果
59     std::string show_image_name = UNKNOWN_FILE;
60     switch (this->annotation_list[this->current_idx]) {
61     case AnnoState::UNKNOWN:
62         show_image_name = UNKNOWN_FILE;
63         break;
64     case AnnoState::YES:
65         show_image_name = YES_FILE;
66         break;
67     case AnnoState::NO:
68         show_image_name = NO_FILE;
69         break;
70     case AnnoState::UNSURE:
71         show_image_name = UNSURE_FILE;
72         break;
73     }
74     set_image(this->ui->label_image_compare_status, show_image_name);
75 
76 }

最为开头我们定义了4个图片的门径。这得是绝对路径或者相对路径。我们这里的门路设置的可比奇怪,在脚我们见面说到。

set_image负责将加以的图片绘制到QLabel上,为了展示的难堪,图像会按照QLabel的尺码来动态的缩放。这样虽不见面出现有只图像太特别还是极小之情事了。

display则是负各个区域之绘图。

还不同一步是保留结果:

 1 /*
 2  * @brief MainWindow::on_pushButton_save_clicked \n
 3  * 保存结果文件
 4  */
 5 void MainWindow::on_pushButton_save_clicked()
 6 {
 7     QString file_name = QFileDialog::getSaveFileName(this, "choose a file to save", ".");
 8     if (file_name.isEmpty()) {
 9         QMessageBox::information(this, "choose a file to save", "please enter a legal file name", QMessageBox::Ok);
10         return;
11     }
12     std::ofstream os(file_name.toStdString());
13     for (int idx = 0; idx < static_cast<int>(this->annotation_list.size()); ++ idx) {
14         os << this->image_list_1[idx] << " " << this->image_list_2[idx] << " " << this->annotation_list[idx] << "\n";
15     }
16     os.close();
17     QMessageBox::information(this, "save", "save result success", QMessageBox::Ok);
18 }

五,添加风波响应

小喵之前了解及,QT使用的是平栽信号和槽的波机制,是一模一样种植很尖端的机制。那么有无出什么简单的法,为咱的每个控件绑定好的之波为?

于界面编辑界面下,右击内需丰富事件的上空,然后选择转到槽。这时候会生出众多抉择,这里一直选择clicked就足以。然后你会意识我们的mainwindow类中,多了一个pivate
slot的函数(也尽管是槽函数)。

图片 8

咱得以吃各一个欲加上事件的函数都为此这种艺术来绑定事件,最终头文件被见面并发这样的扬言(函数名称的条条框框是:on_控件名_信号类型):

1 private slots:
2     void on_pushButton_save_clicked();
3     void on_pushButton_ok_clicked();
4     void on_pushButton_no_clicked();
5     void on_pushButton_unsure_clicked();
6     void on_pushButton_next_clicked();
7     void on_pushButton_prev_clicked();
8     void on_pushButton_switch_clicked();
9     void on_horizontalSlider_progress_sliderReleased();

以自文件中,也会生成空的函数定义。我们唯有需要团结就函数定义就是杀功告成!

脚被有底是除save的持有的函数的定义。

重要工作是,给每个事件编写修改数据的代码,而未去当任何界面相关的片。各个控件可以通过this->ui来设置与得到。使用Qt
Creator的时刻,要充分利用智能提醒。

 1 /**
 2  * @brief MainWindow::on_pushButton_ok_clicked
 3  * 标注为"匹配"
 4  */
 5 void MainWindow::on_pushButton_ok_clicked()
 6 {
 7     this->annotation_list[this->current_idx] = MainWindow::AnnoState::YES;
 8     ++ this->current_idx;
 9     display();
10 }
11 
12 /**
13  * @brief MainWindow::on_pushButton_no_clicked
14  * 标注为"不匹配"
15  */
16 void MainWindow::on_pushButton_no_clicked()
17 {
18     this->annotation_list[this->current_idx] = MainWindow::AnnoState::NO;
19     ++ this->current_idx;
20     display();
21 }
22 
23 /**
24  * @brief MainWindow::on_pushButton_unsure_clicked
25  * 标注为"不确定"
26  */
27 void MainWindow::on_pushButton_unsure_clicked()
28 {
29     this->annotation_list[this->current_idx] = MainWindow::AnnoState::UNSURE;
30     ++ this->current_idx;
31     display();
32 }
33 
34 /**
35  * @brief MainWindow::on_pushButton_next_clicked
36  * 移动到下一组
37  */
38 void MainWindow::on_pushButton_next_clicked()
39 {
40     ++ this->current_idx;
41     display();
42 }
43 
44 /**
45  * @brief MainWindow::on_pushButton_prev_clicked
46  * 移动到上一组
47  */
48 void MainWindow::on_pushButton_prev_clicked()
49 {
50     -- this->current_idx;
51     display();
52 }
53 
54 /**
55  * @brief MainWindow::on_pushButton_switch_clicked
56  * 交换两边的图片
57  */
58 void MainWindow::on_pushButton_switch_clicked()
59 {
60     std::string tmp = this->image_list_1[this->current_idx];
61     this->image_list_1[this->current_idx] = this->image_list_2[this->current_idx];
62     this->image_list_2[this->current_idx] = tmp;
63     display();
64 }
65 
66 /**
67  * @brief MainWindow::on_horizontalSlider_progress_sliderReleased
68  * 拖放进度条,控制进度
69  */
70 void MainWindow::on_horizontalSlider_progress_sliderReleased()
71 {
72     int pos = this->ui->horizontalSlider_progress->value();
73     this->current_idx = pos;
74     this->display();
75 }

迄今为止,我们的盖的机能逻辑就是编写了了。

这就是说怎么让界面及展示我们的系统状态呢?注意到了咱地方的各一个函数都调用了display这个函数了也?这个函数正式承担绘制界面的功力。

片要介绍三独函数:

 1 const std::string UNSURE_FILE = ":File/images/unsure.png";
 2 const std::string YES_FILE = ":File/images/yes.gif";
 3 const std::string NO_FILE = ":File/images/no.gif";
 4 const std::string UNKNOWN_FILE = ":File/images/unknown.png";
 5 
 6 /**
 7  * @brief set_image 将图像设置到label上,图像自动根据label的大小来缩放
 8  * @param label
 9  * @param image
10  */
11 void set_image(QLabel *label, const QPixmap &image) {
12     float ratio(0.);
13     ratio = 1. * label->width() / image.width();
14     ratio = fmin( 1. * label->height() / image.height(), ratio );
15     QPixmap m = image.scaled(static_cast<int>(image.width() * ratio), static_cast<int>(image.height() * ratio));
16     label->setPixmap(m);
17 }
18 
19 void set_image(QLabel *label, const std::string image_path) {
20     QPixmap image(image_path.c_str());
21     set_image(label, image);
22 }
23 
24 /**
25  * @brief MainWindow::display \n
26  * 根据系统中的所有的变量来设置当前界面中的各个部分的内容
27  */
28 void MainWindow::display() {
29 
30     if (this->current_idx >= this->total_pair_num) {
31         QMessageBox::information(this, "annotation over", "Congratulations! You've finished all the job! Please save your work :)", QMessageBox::Ok);
32         this->current_idx = this->total_pair_num - 1;
33     }
34     if (this->current_idx < 0) {
35         QMessageBox::information(this, "annotation warning", "You must start at 0 (not a negative position, I konw you wanna challenge this app) :)", QMessageBox::Ok);
36         this->current_idx = 0;
37     }
38 
39     // 进度条
40     this->ui->horizontalSlider_progress->setRange(0, this->total_pair_num - 1);
41     this->ui->horizontalSlider_progress->setValue(this->current_idx);
42 
43     // 状态栏
44     this->ui->statusBar->showMessage(QString((std::to_string(this->current_idx + 1) + " / " + std::to_string(this->total_pair_num)).c_str()));
45 
46     // 文件名
47     std::string image_name_1 = this->image_list_1[this->current_idx];
48     std::string image_base_name_1 = image_name_1.substr(image_name_1.find_last_of("/") + 1);
49     std::string image_name_2 = this->image_list_2[this->current_idx];
50     std::string image_base_name_2 = image_name_2.substr(image_name_2.find_last_of("/") + 1);
51     this->ui->label_image_name_1->setText(image_base_name_1.c_str());
52     this->ui->label_image_name_2->setText(image_base_name_2.c_str());
53 
54     // 显示图像
55     set_image(this->ui->label_image_view_1, image_name_1);
56     set_image(this->ui->label_image_view_2, image_name_2);
57 
58     // 显示标注结果
59     std::string show_image_name = UNKNOWN_FILE;
60     switch (this->annotation_list[this->current_idx]) {
61     case AnnoState::UNKNOWN:
62         show_image_name = UNKNOWN_FILE;
63         break;
64     case AnnoState::YES:
65         show_image_name = YES_FILE;
66         break;
67     case AnnoState::NO:
68         show_image_name = NO_FILE;
69         break;
70     case AnnoState::UNSURE:
71         show_image_name = UNSURE_FILE;
72         break;
73     }
74     set_image(this->ui->label_image_compare_status, show_image_name);
75 
76 }

无限初步我们定义了4只图片的途径。这好是绝对路径或者相对路径。我们这里的门径设置的可比奇怪,在下面我们见面说话到。

set_image负责将加的图纸绘制到QLabel上,为了显得的尴尬,图像会按照QLabel的尺寸来动态的缩放。这样便无会见油然而生有个图像太死或太小的景了。

display则是肩负各个区域的绘图。

尚不一一步是保存结果:

 1 /*
 2  * @brief MainWindow::on_pushButton_save_clicked \n
 3  * 保存结果文件
 4  */
 5 void MainWindow::on_pushButton_save_clicked()
 6 {
 7     QString file_name = QFileDialog::getSaveFileName(this, "choose a file to save", ".");
 8     if (file_name.isEmpty()) {
 9         QMessageBox::information(this, "choose a file to save", "please enter a legal file name", QMessageBox::Ok);
10         return;
11     }
12     std::ofstream os(file_name.toStdString());
13     for (int idx = 0; idx < static_cast<int>(this->annotation_list.size()); ++ idx) {
14         os << this->image_list_1[idx] << " " << this->image_list_2[idx] << " " << this->annotation_list[idx] << "\n";
15     }
16     os.close();
17     QMessageBox::information(this, "save", "save result success", QMessageBox::Ok);
18 }

六、添加资源

是因为我们的程序是需要publish出去的,因此图片文件等资源,必须带有在先后中。那么Qt怎么长文件资源为?

每当品种视图下,右键项目->添加新文件->Qt->Qt Resource
File。就足以创建一个qrc文件了。

图片 9

自此给这个文件取名为image。

此后,建议以品种的根本目录里新建一个文件夹,用来存放资源。小喵的构造是此法的:

图片 10

小喵的路根本目录新建了一个文本夹images,并将图像资料放入了是文件夹。

从此回来QT,我们恰好砌好之image.qrc文件->Open in Editor。

先期补充加前缀,这里描绘上/File。之后点击新建的/File目录,再点击添加->添加文件,选择我们的材料文件。最终之效能图如下:

图片 11

此后,我们尽管可在先后中直接看这些资源了。这吗就是咱之前的那么四个意想不到之门道的是因为来了。

六、添加资源

由于我们的顺序是需要publish出去的,因此图片文件等资源,必须含有在先后中。那么Qt怎么长文件资源为?

于品种视图下,右键项目->添加新文件->Qt->Qt Resource
File。就好创造一个qrc文件了。

图片 12

自我这里被这个文件取名为image。

后来,建议在列之绝望目录内新建一个文书夹,用来存放在资源。小喵的组织是者样子的:

图片 13

小喵的品种根本目录新建了一个文件夹images,并拿图像资料放入了这个文件夹。

后返回QT,我们刚刚建好之image.qrc文件->Open in Editor。

先行上加前缀,这里描绘上/File。之后点击新建的/File目录,再点击添加->添加文书,选择我们的资料文件。最终的效力图如下:

图片 14

后来,我们就算足以于程序中一直访问这些资源了。这为就算是咱们事先的那四单意外的路线的由于来了。

七、发布

当前,相信各一个喵粉的次都能够在投机的微处理器上快的玩耍了。这么有意思的次序,怎么分享给其他人也?

与Windows上常用之VS类似,Qt Creator的左下角有只公布选项:

图片 15

摘Release,然后构建整个项目就是得了。之后找到我们的次第,双击就好运作。

此时若会欣然的将这个次关你的好伴侣,得到的影响自然是:这是甚!我自不起头!

干什么吧?

虽然Qt是一个跨平台的界面库,但如若对方的处理器及从来不设置Qt,那么就算无可知运行。不过并非失落,Qt中既于闹一个上佳的解决办法。

小喵的处理器是Mac的,所以找到的化解方案也是Mac的,Windows和Linux上吧时有发生接近之艺术,大家可以自动检索。

http://www.cnblogs.com/E7868A/archive/2012/12/02/2798225.html

参照上述博客,我们以macdeployqt这个家伙来处理一下release的次即使搞定。这时候你会意识原100k的先后成为了22M。但是一直发放别人的当儿,是可以一直运行的!

 

迄今,本次的博客结束了。

一体化的类以github上可下载:

https://github.com/miaoerduo/Anno_pro

 

如你看本文对而有赞助,那请小喵喝杯茶吧O(∩_∩)O

图片 16

转载请注明出处~

七、发布

当前,相信各一个喵粉的顺序都能够在大团结的电脑及快的玩耍了。这么有意思的主次,怎么分享给其他人也?

与Windows上常用之VS类似,Qt Creator的左下角有个公布选项:

图片 17

择Release,然后构建整个项目就算足以了。之后找到我们的次第,双击就得运作。

此时若会欢欣鼓舞的管这个次关你的好伴侣,得到的反响自然是:这是吗!我从不开!

为何吧?

虽然Qt是一个跨平台的界面库,但如若对方的微机及尚无装Qt,那么就算无能够运作。不过并非失落,Qt中曾经被起一个大好的解决办法。

小喵的微处理器是Mac的,所以找到的缓解方案吗是Mac的,Windows和Linux上啊起近似之法子,大家好活动检索。

http://www.cnblogs.com/E7868A/archive/2012/12/02/2798225.html

参考上述博客,我们应用macdeployqt这个家伙来拍卖一下release的次就算搞定。这时候若见面发觉原先100k的先后变成了22M。但是一直发给别人的早晚,是可直接运行的!

 

迄今,本次的博客结束了。

完整的类型在github上得下载:

https://github.com/miaoerduo/Anno_pro

 

万一您当本文对君来拉,那要小喵喝杯茶吧O(∩_∩)O

图片 18

转载请注明出处~