Browse Source

20220617,初版v1.0.0

master
tianzhendong 2 years ago
commit
93f783e53b
  1. 84
      .gitignore
  2. 53
      CMakeLists.txt
  3. 8
      README.md
  4. BIN
      favicon.ico
  5. BIN
      resources/close.png
  6. BIN
      resources/fullscreen3.png
  7. BIN
      resources/fullscreen4.png
  8. BIN
      resources/min.png
  9. 75
      src/DuplicateFiles.cpp
  10. 40
      src/DuplicateFiles.h
  11. 10
      src/main.cpp
  12. 178
      src/widget.cpp
  13. 68
      src/widget.h
  14. 415
      src/widget.ui

84
.gitignore

@ -0,0 +1,84 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe
build*/
build-*
build-*/
build*
!.vscode
debug/
debug
release/
release
cmake-build*/
.idea/

53
CMakeLists.txt

@ -0,0 +1,53 @@
cmake_minimum_required(VERSION 3.22)
project(DuplicateFilesCheck)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_PREFIX_PATH "C:/Qt/Qt5.14.2/5.14.2/mingw73_64")
find_package(Qt5 COMPONENTS
Core
Gui
Widgets
REQUIRED)
include_directories(resources)
add_executable(DuplicateFilesCheck src/main.cpp app_win32.rc src/widget.cpp src/widget.h src/widget.ui src/DuplicateFiles.cpp src/DuplicateFiles.h)
target_link_libraries(DuplicateFilesCheck
Qt5::Core
Qt5::Gui
Qt5::Widgets
)
if (WIN32)
set(DEBUG_SUFFIX)
if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug")
set(DEBUG_SUFFIX "d")
endif ()
set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}")
if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
if (NOT EXISTS "${QT_INSTALL_PATH}/bin")
set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..")
endif ()
endif ()
if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
"$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll"
"$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/")
endif ()
foreach (QT_LIB Core Gui Widgets)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${QT_INSTALL_PATH}/bin/Qt5${QT_LIB}${DEBUG_SUFFIX}.dll"
"$<TARGET_FILE_DIR:${PROJECT_NAME}>")
endforeach (QT_LIB)
endif ()

8
README.md

@ -0,0 +1,8 @@
# 重复文件检测工具
## 环境
- Clion 2022.1.2
- qt 5.14.2
## 功能
- 单个文件计算md5
- 文件夹内重复文件检测

BIN
favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
resources/close.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

BIN
resources/fullscreen3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

BIN
resources/fullscreen4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

BIN
resources/min.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

75
src/DuplicateFiles.cpp

@ -0,0 +1,75 @@
//
// Created by 12038 on 2022/6/16.
//
#include "DuplicateFiles.h"
DuplicateFiles::DuplicateFiles(QObject *) {
}
DuplicateFiles::~DuplicateFiles() {
}
void DuplicateFiles::calMd5Slot(const QString &filePath) {
QByteArray md5 = calMd5(filePath);
emit md5Signal(md5);
}
QByteArray DuplicateFiles::calMd5(const QString &filePath) {
QFile file(filePath);
QByteArray md5 = "";
if(file.open(QIODevice::ReadOnly)){
QCryptographicHash hash(QCryptographicHash::Md5);
while(!file.atEnd()){
QByteArray buff = file.read(100 * 1024 * 1024);
hash.addData(buff);
}
md5 = hash.result().toHex();
file.close();
}
return md5;
}
void DuplicateFiles::getFilesSlot(const QString &filesDirPath) {
//获取文件夹下所有的文件
QStringList files= this->getFiles(filesDirPath);
//计算md5,并存到map中
QHash<QByteArray, QStringList> md5Results;
for (int i = 0; i < files.count(); i++){
QString fileName = files.at(i);
QByteArray fileMd5 = calMd5(fileName);
md5Results[fileMd5].append(fileName);
qDebug() <<"file:" << fileName << "md5:"<< fileMd5;
emit process(i + 1, files.count());
}
//找出map中value大于1的
for (QHash<QByteArray, QStringList>::iterator itr = md5Results.begin(); itr != md5Results.end(); itr++){
if(itr.value().count() > 1){
qDebug()<< "md5:"<<itr.key()<<"file:"<<itr.value();
duplicateFiles[itr.key()] = itr.value();
}
}
// emit filesSignal(files);
emit duplicateFilesSignal(duplicateFiles);
}
QStringList DuplicateFiles::getFiles(const QString &filesDirPath) {
QStringList files;
QDir filesDir(filesDirPath);
QList<QFileInfo> fileInfoList = filesDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
for(int i = 0; i < fileInfoList.count(); i++){
QFileInfo fileInfo = fileInfoList.at(i);
if(fileInfo.isDir()){
QStringList subFiles= getFiles(fileInfo.absoluteFilePath());
files.append(subFiles);
}
else{
QString fileName = fileInfo.absoluteFilePath();
files.append(fileName);
}
}
return files;
}

40
src/DuplicateFiles.h

@ -0,0 +1,40 @@
//
// Created by 12038 on 2022/6/16.
//
#ifndef DUPLICATEFILESCHECK_DUPLICATEFILES_H
#define DUPLICATEFILESCHECK_DUPLICATEFILES_H
#include <QObject>
#include <QHash>
#include <QDebug>
#include <QFile>
#include <QCryptographicHash>
#include <QDir>
class DuplicateFiles : public QObject{
Q_OBJECT
public:
DuplicateFiles(QObject *parent = nullptr);
~DuplicateFiles();
signals:
void md5Signal(const QByteArray &);
void filesSignal(const QStringList &);
void process(const int &, const int &);
void duplicateFilesSignal(const QHash<QByteArray, QStringList> &);
public slots:
void getFilesSlot(const QString & filesDirPath);
void calMd5Slot(const QString &filePath);
private:
QByteArray calMd5(const QString &filePath);
QStringList getFiles(const QString &);
QHash<QByteArray, QStringList> duplicateFiles;
};
#endif //DUPLICATEFILESCHECK_DUPLICATEFILES_H

10
src/main.cpp

@ -0,0 +1,10 @@
#include <QApplication>
#include "widget.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
qRegisterMetaType<QHash<QByteArray,QStringList>>("QHash<QByteArray,QStringList>");
Widget w;
w.show();
return QApplication::exec();
}

178
src/widget.cpp

@ -0,0 +1,178 @@
//
// Created by 12038 on 2022/6/15.
//
// You may need to build the project (run Qt uic code generator) to get "ui_Widget.h" resolved
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//取消菜单栏
this->setWindowFlags(Qt::FramelessWindowHint);
//阴影边框效果
QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
shadow->setBlurRadius(10);
shadow->setColor(Qt::black);
shadow->setOffset(0);
ui->shadowWidget->setGraphicsEffect(shadow);
//父窗口透明
this->setAttribute(Qt::WA_TranslucentBackground);
//最大化最小化关闭功能实现
connect(ui->btnMax, SIGNAL(clicked()), this, SLOT(btnMaxClickedSlot()));
connect(ui->btnMin, SIGNAL(clicked()), this, SLOT(btnMinClickedSlot()));
connect(ui->btnClose, SIGNAL(clicked()), this, SLOT(btnCloseClickedSlot()));
ui->btnMin->setStyleSheet("border-image: url(../resources/min.png)");
ui->btnMax->setStyleSheet("border-image: url(../resources/fullscreen3.png)");
ui->btnClose->setStyleSheet("border-image: url(../resources/close.png)");
duplicateFiles = new DuplicateFiles();
myThread = new QThread();
duplicateFiles->moveToThread(myThread);
myThread->start();
connect(duplicateFiles, SIGNAL(destroyed(QObject *)),
myThread, SLOT(deleteLater()));
ui->labelTitle->setText("文件一致性检测工具");
connect(ui->btnSelectFile, SIGNAL(clicked(bool)), this, SLOT(calMd5ofFileSlot()));
connect(ui->btnSelectDir, SIGNAL(clicked(bool)), this, SLOT(selectDirSlot()));
connect(this, SIGNAL(calFileMd5Signal(const QString &)),
duplicateFiles, SLOT(calMd5Slot(const QString &)));
connect(duplicateFiles, SIGNAL(md5Signal(const QByteArray &)),
this, SLOT(showFileMd5Slot(const QByteArray &)));
connect(this, SIGNAL(getFilesSignal(const QString &)),
duplicateFiles, SLOT(getFilesSlot(const QString &)));
connect(duplicateFiles, SIGNAL(filesSignal(const QStringList &)),
this, SLOT(filesSlot(const QStringList &)));
connect(duplicateFiles, SIGNAL(process(const int &, const int &)),
this, SLOT(processSlot(const int &, const int &)));
connect(duplicateFiles, SIGNAL(duplicateFilesSignal(const QHash<QByteArray, QStringList> &)),
this, SLOT(duplicateFilesSlot(const QHash<QByteArray, QStringList> &)));
connect(ui->listWidget, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(currentTextChangedSlot(const QString &)));
}
Widget::~Widget()
{
duplicateFiles->deleteLater();
myThread->exit();
myThread->wait(10 * 1000);
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *event)
{
// QWidget::mousePressEvent(event);
QPoint mouseStartPoint = event->globalPos();
QPoint windowLeftTopPoint = this->geometry().topLeft();
this->mousePosInWindow = mouseStartPoint - windowLeftTopPoint;
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
// QWidget::mouseMoveEvent(event);
if(this->mousePosInWindow == QPoint()) return;
QPoint mousePoint = event->globalPos();
QPoint windowLeftTopPoint = mousePoint - this->mousePosInWindow;
this->move(windowLeftTopPoint);
}
void Widget::mouseReleaseEvent(QMouseEvent *)
{
this->mousePosInWindow = QPoint();
}
void Widget::closeEvent(QCloseEvent *event)
{
QMessageBox::StandardButton button;
button=QMessageBox::question(this,tr("退出程序"),QString(tr("确认退出程序?")),QMessageBox::Yes|QMessageBox::No);
if(button==QMessageBox::No)
{
event->ignore(); // 忽略退出信号,程序继续进行
}
else if(button==QMessageBox::Yes)
{
event->accept(); // 接受退出信号,程序退出
}
}
void Widget::btnMaxClickedSlot()
{
ui->btnMax->setStyleSheet("border-image: url(../resources/fullscreen4.png)");
if(this->isMaximized()){
ui->layoutMain->setMargin(9);
ui->btnMax->setStyleSheet("border-image: url(../resources/fullscreen3.png)");
this->showNormal();
}
else{
ui->layoutMain->setMargin(0);
ui->btnMax->setStyleSheet("border-image: url(../resources/fullscreen4.png)");
this->showMaximized();
}
}
void Widget::btnMinClickedSlot()
{
this->showMinimized();
}
void Widget::btnCloseClickedSlot()
{
this->close();
}
void Widget::calMd5ofFileSlot() {
QString path = QFileDialog::getOpenFileName(
this, "选择文件",
"./",
"");
emit calFileMd5Signal(path);
}
void Widget::showFileMd5Slot(const QByteArray & md5) {
ui->leMd5Show->setText("");
ui->leMd5Show->setText(md5);
}
void Widget::selectDirSlot() {
ui->progressBar->setValue(0);
QString dirPathUrl = QFileDialog::getExistingDirectory(this, "选择文件夹", "./");
ui->lineDIrShow->setText(dirPathUrl);
emit getFilesSignal(dirPathUrl);
}
void Widget::filesSlot(const QStringList &files) {
ui->listWidget_2->clear();
ui->listWidget_2->addItems(files);
}
void Widget::processSlot(const int &now, const int &total) {
ui->progressBar->setMaximum(total);
ui->progressBar->setValue(now);
}
void Widget::duplicateFilesSlot(const QHash<QByteArray, QStringList> &duplicateFiles) {
ui->listWidget->clear();
this->duplicateResults = duplicateFiles;
for(QHash<QByteArray, QStringList>::const_iterator itr = duplicateFiles.begin(); itr != duplicateFiles.end(); itr++){
ui->listWidget->addItem(itr.key());
}
}
void Widget::currentTextChangedSlot(const QString &currentText) {
ui->listWidget_2->clear();
ui->listWidget_2->addItems(this->duplicateResults[currentText.toLocal8Bit()]);
}

68
src/widget.h

@ -0,0 +1,68 @@
//
// Created by 12038 on 2022/6/15.
//
#ifndef DUPLICATEFILESCHECK_WIDGET_H
#define DUPLICATEFILESCHECK_WIDGET_H
#include <QMouseEvent>
#include <QWidget>
#include <QPoint>
#include <QGraphicsDropShadowEffect>
#include <QMessageBox>
#include "DuplicateFiles.h"
#include <QDir>
#include <QDebug>
#include <QThread>
#include <QFileDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void closeEvent(QCloseEvent *event);
signals:
void calFileMd5Signal(const QString &path);
void getFilesSignal(const QString &dirPath);
public slots:
void showFileMd5Slot(const QByteArray &);
void filesSlot(const QStringList &);
void processSlot(const int &, const int &);
void duplicateFilesSlot(const QHash<QByteArray, QStringList> &);
void currentTextChangedSlot(const QString &);
private slots:
void btnMaxClickedSlot();
void btnMinClickedSlot();
void btnCloseClickedSlot();
void calMd5ofFileSlot();
void selectDirSlot();
private:
Ui::Widget *ui;
DuplicateFiles * duplicateFiles;
QThread *myThread;
QPoint mousePosInWindow = QPoint();
QHash<QByteArray, QStringList> duplicateResults;
};
#endif //DUPLICATEFILESCHECK_WIDGET_H

415
src/widget.ui

@ -0,0 +1,415 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Widget</string>
</property>
<layout class="QGridLayout" name="layoutMain">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item row="0" column="0">
<widget class="QWidget" name="shadowWidget" native="true">
<property name="styleSheet">
<string notr="true">#shadowWidget{
background-color: rgb(255, 255, 255);
border-radius: 5px;
}
</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelTitle">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>微软雅黑</family>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>windowTitle</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnMin">
<property name="minimumSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton
{
border:none;
}
QPushButton:hover
{
background-color: rgb(232, 232, 232);
}
QPushButton:pressed
{
background-color: rgb(162, 162, 162);
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnMin_2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>36</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton
{
border:none;
}
QPushButton:hover
{
background-color: rgb(232, 232, 232);
}
QPushButton:pressed
{
background-color: rgb(162, 162, 162);
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnMax">
<property name="minimumSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton
{
border:none;
}
QPushButton:hover
{
background-color: rgb(232, 232, 232);
}
QPushButton:pressed
{
background-color: rgb(162, 162, 162);
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnMin_3">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>36</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton
{
border:none;
}
QPushButton:hover
{
background-color: rgb(232, 232, 232);
}
QPushButton:pressed
{
background-color: rgb(162, 162, 162);
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClose">
<property name="minimumSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>36</width>
<height>36</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton
{
border:none;
border-top-right-radius: 5px;
}
QPushButton:hover
{
background-color: rgb(253, 0, 0);
}
QPushButton:pressed
{
background-color: rgb(211, 0, 0);
}
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="title">
<string>文件Md5计算</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="btnSelectFile">
<property name="text">
<string>选择文件</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leMd5Show"/>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>目录重复文件检测</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btnSelectDir">
<property name="text">
<string>选择目录</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineDIrShow"/>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QListWidget" name="listWidget"/>
</item>
<item>
<widget class="QListWidget" name="listWidget_2"/>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Loading…
Cancel
Save