#QT-可视化应用

😒5ea1
What&Why is QT
Qt是一个跨平台的C++图形用户界面应用程序框架。它为应用程序开发者提供建立图形界面所需的所有功能。它是完全面向对象的,很容易扩展,并且允许真正的组件编程。
前段时间打suctf的时候看到一道qt逆向,虽然被恶心坏了,但是确实新颖。因为最近刚好要交一个拓展作业,就想着用qt搭一个可视化的应用😋
How to QT
因为QT是一种面向对象的框架,所以实际上和编写安卓应用极为相似。
下面的论述仅代表我自己在使用过程中的体会,仅供参考
CMakeList.txt
就像vs项目中的.sln文件一样,qt直接采用了txt的形式来规划整个项目。每个项目中都会有一个名为CMakeList.txt的文本,这个文本的作用就是告知qt这个项目的架构是什么样子的,有什么文件,以及有什么依赖等等等等
CMakeList.txt分析
cmake_minimum_required(VERSION 3.16)
project(Dijkstra1 VERSION 0.1 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools)
set(TS_FILES Dijkstra1_zh_CN.ts)
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
${TS_FILES}
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(Dijkstra1
MANUAL_FINALIZATION
${PROJECT_SOURCES}
mainIO.cpp
mainIO.h
maindijkstra.h
maindijkstra.cpp
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET Dijkstra1 APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
else()
if(ANDROID)
add_library(Dijkstra1 SHARED
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(Dijkstra1
${PROJECT_SOURCES}
)
endif()
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
endif()
target_link_libraries(Dijkstra1 PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.Dijkstra1)
endif()
set_target_properties(Dijkstra1 PROPERTIES
${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
include(GNUInstallDirs)
install(TARGETS Dijkstra1
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(Dijkstra1)
endif()
CMake 最低版本要求
cmake_minimum_required(VERSION 3.16)
这行代码指定了运行此 CMake 脚本所需的最低 CMake 版本为 3.16。如果使用的 CMake 版本低于 3.16,将会抛出错误。
项目信息设置
project(Dijkstra1 VERSION 0.1 LANGUAGES CXX)
project 命令用于定义项目的基本信息。
Dijkstra1 是项目的名称。
VERSION 0.1 设定项目的版本号为 0.1。
LANGUAGES CXX 表明项目使用的编程语言是 C++。
Qt 自动处理功能开启
set(CMAKE_AUTOUIC ON)
开启 Qt 的 UI 文件自动处理功能,CMake 会自动将 .ui 文件转换为对应的 C++ 代码。
set(CMAKE_AUTOMOC ON)
开启 Qt 的元对象编译器(MOC)自动处理功能,用于处理 Qt 的信号和槽机制。
set(CMAKE_AUTORCC ON)
开启 Qt 的资源编译器(RCC)自动处理功能,用于处理 Qt 的资源文件。
C++ 标准设置
set(CMAKE_CXX_STANDARD 17)
指定项目使用的 C++ 标准为 C++17。
set(CMAKE_CXX_STANDARD_REQUIRED ON)
表示必须使用指定的 C++ 标准,如果编译器不支持 C++17,CMake 会报错。
查找 Qt 库
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools)
尝试查找 Qt 库,优先查找 Qt6,如果找不到则查找 Qt5。同时要求找到 Widgets 和 LinguistTools 组件。REQUIRED 表示如果找不到这些组件,CMake 会报错。
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools)
根据实际找到的 Qt 版本(QT_VERSION_MAJOR)再次查找相应版本的 Qt 库,并要求包含 Widgets 和 LinguistTools 组件。
翻译文件设置
set(TS_FILES Dijkstra1_zh_CN.ts)
定义一个变量 TS_FILES,其值为 Dijkstra1_zh_CN.ts,这是一个 Qt 的翻译源文件(.ts 文件)。
项目源文件设置
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
${TS_FILES}
)
定义一个变量 PROJECT_SOURCES,包含了项目的源文件、头文件、UI 文件和翻译源文件。
根据 Qt 版本创建可执行文件或库
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(Dijkstra1
MANUAL_FINALIZATION
${PROJECT_SOURCES}
mainIO.cpp
mainIO.h
maindijkstra.h
maindijkstra.cpp
)
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
else()
if(ANDROID)
add_library(Dijkstra1 SHARED
${PROJECT_SOURCES}
)
else()
add_executable(Dijkstra1
${PROJECT_SOURCES}
)
endif()
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
endif()
使用 qt_add_executable 命令创建一个名为 Dijkstra1 的可执行文件。MANUAL_FINALIZATION 表示需要手动调用 qt_finalize_executable 来完成最终的配置。
qt_create_translation 命令用于根据 .ts 文件生成 .qm 翻译文件。
链接 Qt 库
target_link_libraries(Dijkstra1 PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
将 Dijkstra1 目标与相应版本的 Qt Widgets 库进行链接。PRIVATE 表示该链接仅对 Dijkstra1 目标有效。
设置 macOS/iOS 包标识符
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.Dijkstra1)
endif()
如果使用的 Qt 版本低于 6.1.0,设置 BUNDLE_ID_OPTION 变量为 MACOSX_BUNDLE_GUI_IDENTIFIER com.example.Dijkstra1,用于指定 macOS/iOS 应用程序的包标识符。
设置目标属性
set_target_properties(Dijkstra1 PROPERTIES
命令用于设置 Dijkstra1 目标的属性
${BUNDLE_ID_OPTION}
如果 Qt 版本低于 6.1.0,设置包标识符。
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
设置 macOS 应用程序包的版本号为项目版本号。
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
设置 macOS 应用程序包的短版本号为项目主版本号和次版本号。
MACOSX_BUNDLE TRUE
表示该目标是一个 macOS 应用程序包。
WIN32_EXECUTABLE TRUE
表示该目标是一个 Windows 可执行文件。
安装目标
include(GNUInstallDirs)
包含 GNUInstallDirs 模块,该模块定义了一些标准的安装目录变量。
install(TARGETS Dijkstra1
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
用于指定如何安装 Dijkstra1 目标
完成 Qt 6 可执行文件的最终配置
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(Dijkstra1)
endif()
调用 qt_finalize_executable 命令完成 Dijkstra1 可执行文件的最终配置。
##mainwindow.ui
这个文件指定了qt应用的布局,类似于安卓中layout中的布局文件,同样的,在qt中这个布局文件也可以用可视化的方式来进行编写,双击打开mainwindow.ui文件即可进入设计页面
可以看到左侧有一个组件盒,可以从中取不同的组件进行diy,我在此处因为要做一个Djikstra计算器,需要输入无向图,所以使用了下列模块:
一个Text Edit接收输入
一个Text Edit写成只读作为输出
一个Text Edit用作提示
三个Plain Text Edit来提示各个框的作用
一个Spin Box来充当起始点的输入,防止非预期输入
一个Push Button来开始计算,一块QGraphics View来画示意图
只要为每个由你设定的组件设置一个独一无二的名字即可
##main.cpp
主程序代码,基本不用修改
##mainwindow.h
主窗口头文件,需要导入要调用的库,以及加入一些变量的定义和函数的定义
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "mainIO.h"
#include "maindijkstra.h"
#include <QSpinBox>
#include <QGraphicsScene>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_calculate_clicked();
private:
void drawGraph(int max_node);
Ui::MainWindow *ui;
int Data[100][10] = {0};
int n;
QGraphicsScene *scene;//此处就是额外加入的全局变量以及一些函数
};
#endif
##More
到这里一个最简单的qt代码已经成型了,其余部分就是一些额外的调用以及额外的算法调用,这里展示我的迪杰斯特拉计算器的代码
mainio.cpp
//mainio.cpp
#include "mainIO.h"
#include <QDebug>
void InputData(const QString& input, int Data[100][10], int* n)
{
QStringList input_lines = input.split('\n'), line_split;
int row = 0;
for (const QString &line : input_lines)
{
int col = 0;
line_split = line.split(' ');
for (const QString &datas : line_split)
{
bool ok;
Data[row][col] = datas.toInt(&ok);
if (ok)
{
col++;
}
}
row++;
}
*n=row;
}
void OutputData(QString& output, int Data[100][10], int n)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<3;j++)
{
output.append(" ");
output.append(QString::number(Data[i][j]));
}
output.append("\n");
}
}
这段 C++ 代码主要实现了两个功能函数:InputData 和 OutputData。InputData 函数用于将输入的字符串数据解析为二维数组,OutputData 函数则用于将二维数组的数据转换为字符串输出。即主要处理读入的无向图数据
###maindijkstra.cpp
//maindijkstra.cpp
#include <QDebug>
#include "maindijkstra.h"
#include <vector>
void Dijkstra(QString& output, int Data[100][10], int n, int source)
{
int find[100][100], flag[100];
int Way[100];
int prev[100];
int max = -1;
for (int i = 0; i < 100; i++)
{
for (int j = 0; j < 100; j++)
{
find[i][j] = 99999999;
}
Way[i] = 99999999;
flag[i] = 0;
prev[i] = -1;
}
for (int i = 0; i < n; i++)
{
find[Data[i][0]][Data[i][1]] = Data[i][2];
find[Data[i][1]][Data[i][0]] = Data[i][2];
for (int j = 0; j < 2; j++)
{
if (Data[i][j] > max)
{
max = Data[i][j];
}
}
}
Way[source] = 0;
for (int i = 0; i <= max; i++)
{
int Node = -1;
for (int j = 0; j <= max; j++)
{
if (flag[j] == 0 && (Node == -1 || Way[j] < Way[Node]))
{
Node = j;
}
}
if (Node == -1)
{
break;
}
for (int j = 0; j <= max; j++)
{
if (find[Node][j] != 99999999 && Way[Node] + find[Node][j] < Way[j])
{
Way[j] = Way[Node] + find[Node][j];
prev[j] = Node;
}
}
flag[Node] = 1;
}
for (int i = 0; i <= max; i++)
{
output.append(QString::number(source));
output.append(" to ");
output.append(QString::number(i));
output.append(" Length: ");
output.append(QString::number(Way[i]));
output.append(" Path: ");
std::vector<int> path;
for (int at = i; at != -1; at = prev[at])
{
path.push_back(at);
}
if (path.size() == 0)
{
output.append("No path\n");
}
else
{
std::reverse(path.begin(), path.end());
for (size_t j = 0; j < path.size(); j++)
{
output.append(QString::number(path[j]));
if (j != path.size() - 1)
{
output.append(" -> ");
}
}
output.append("\n");
}
}
}
int min(int x, int y)
{
if(x<y)
{
return x;
}
return y;
}
这段代码实现了 Dijkstra 算法,用于计算从给定源节点到图中所有其他节点的最短路径。代码接收一个二维数组 Data 来表示图的边信息,源节点 source,并将计算结果以字符串形式存储在 output 中。
打包
最后只要把代码打包成所要的构建,就能在外面运行了
如果要脱离软件,还要用qt自带的应用添加依赖文件