init
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.onnx filter=lfs diff=lfs merge=lfs -text
|
212
.gitignore
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# C++ objects and libs
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.a
|
||||
*.la
|
||||
*.lai
|
||||
*.so
|
||||
*.so.*
|
||||
*.dll
|
||||
*.dylib
|
||||
|
||||
# Qt-es
|
||||
object_script.*.Release
|
||||
object_script.*.Debug
|
||||
*_plugin_import.cpp
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
*.pro.user
|
||||
*.pro.user.*
|
||||
*.qbs.user
|
||||
*.qbs.user.*
|
||||
*.moc
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
qrc_*.cpp
|
||||
ui_*.h
|
||||
*.qmlc
|
||||
*.jsc
|
||||
Makefile*
|
||||
*build-*
|
||||
*.qm
|
||||
*.prl
|
||||
|
||||
# Qt unit tests
|
||||
target_wrapper.*
|
||||
|
||||
# QtCreator
|
||||
*.autosave
|
||||
|
||||
# QtCreator Qml
|
||||
*.qmlproject.user
|
||||
*.qmlproject.user.*
|
||||
|
||||
# QtCreator CMake
|
||||
CMakeLists.txt.user*
|
||||
|
||||
# QtCreator 4.8< compilation database
|
||||
compile_commands.json
|
||||
|
||||
# QtCreator local machine specific files for imported projects
|
||||
*creator.user*
|
||||
|
||||
*_qmlcache.qrc
|
||||
|
||||
# custom
|
||||
output*
|
||||
.vscode/settings.json
|
||||
package
|
||||
rc.py
|
||||
*.aux.xml
|
||||
|
||||
model/
|
12
ReadMe.md
Normal file
@ -0,0 +1,12 @@
|
||||
# 变化检测
|
||||
王铜
|
||||
CVEO团队
|
||||
|
||||
# 环境
|
||||
Python3 + PyQt + QGIS?
|
||||
|
||||
# 当前依赖
|
||||
pyqt qgis gdal numpy
|
||||
|
||||
# 打包方式
|
||||
comming soon
|
0
__init__.py
Normal file
341
actions/actions.py
Normal file
@ -0,0 +1,341 @@
|
||||
import logging
|
||||
import os
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from PyQt5.QtWidgets import QAction, QActionGroup, QLabel, QFileDialog
|
||||
from gui.about import AboutDialog
|
||||
from gui.project import Create
|
||||
from utils.project import Project
|
||||
|
||||
class ActionManager(QtCore.QObject):
|
||||
|
||||
def __init__(self,
|
||||
double_map,
|
||||
layer_tree,
|
||||
follow_box,
|
||||
result_box,
|
||||
message_box,
|
||||
parent=None):
|
||||
super().__init__(parent)
|
||||
self.w_parent = parent
|
||||
self.actions = {}
|
||||
self.action_groups = {}
|
||||
self.action_group_actions = {}
|
||||
|
||||
self.double_map = double_map
|
||||
self.layer_tree = layer_tree
|
||||
self.follow_box = follow_box
|
||||
self.result_box = result_box
|
||||
self.message_box = message_box
|
||||
|
||||
self.allways_enable = []
|
||||
self.init_enable = []
|
||||
self.dataload_enable = []
|
||||
|
||||
self.toolbar = None
|
||||
self.menubar = None
|
||||
self.status_bar = None
|
||||
|
||||
def set_menus(self, menubar):
|
||||
self.menubar = menubar
|
||||
self.file_menu = menubar.addMenu('&文件')
|
||||
# self.view_menu = menubar.addMenu('&视图')
|
||||
self.basic_menu = menubar.addMenu('&基本工具')
|
||||
self.preop_menu = menubar.addMenu('&预处理')
|
||||
self.change_detection_menu = menubar.addMenu('&变化检测')
|
||||
self.special_chagne_detec_menu = menubar.addMenu('&专题变化检测')
|
||||
self.postop_menu = menubar.addMenu('&后处理')
|
||||
self.eval_menu = menubar.addMenu('&评估')
|
||||
self.help_menu = menubar.addMenu('&帮助')
|
||||
|
||||
def set_toolbar(self, toolbar):
|
||||
self.toolbar = toolbar
|
||||
self.toolbar.setIconSize(QtCore.QSize(24, 24))
|
||||
|
||||
def set_status_bar(self, status_bar):
|
||||
self.status_bar = status_bar
|
||||
|
||||
def set_actions(self):
|
||||
'''
|
||||
File menu
|
||||
'''
|
||||
project_create = self.add_action(QAction('&工程创建', self.w_parent), 'File')
|
||||
project_open = self.add_action(QAction('&打开工程', self.w_parent), 'File')
|
||||
project_save = self.add_action(QAction('&保存工程', self.w_parent), 'File')
|
||||
data_load = self.add_action(QAction('&数据加载', self.w_parent), 'File')
|
||||
view_setting = self.add_action(QAction('&界面定制', self.w_parent), 'File')
|
||||
exit_app = self.add_action(QAction('&退出', self.w_parent), 'File')
|
||||
project_create.triggered.connect(self.project_create)
|
||||
project_open.triggered.connect(self.project_open)
|
||||
project_save.triggered.connect(self.project_save)
|
||||
data_load.triggered.connect(self.data_load)
|
||||
view_setting.triggered.connect(self.view_setting)
|
||||
exit_app.triggered.connect(self.w_parent.close)
|
||||
|
||||
self.allways_enable.append(project_create, project_open, exit_app, view_setting)
|
||||
self.init_enable.append(project_save, data_load)
|
||||
|
||||
self.file_menu.addAction(project_create)
|
||||
self.file_menu.addAction(project_open)
|
||||
self.file_menu.addAction(project_save)
|
||||
self.file_menu.addAction(data_load)
|
||||
self.file_menu.addAction(view_setting)
|
||||
self.file_menu.addAction(exit_app)
|
||||
|
||||
if self.toolbar is not None:
|
||||
self.toolbar.addAction(project_create)
|
||||
self.toolbar.addAction(project_open)
|
||||
self.toolbar.addAction(project_save)
|
||||
|
||||
'''
|
||||
Basic menu
|
||||
'''
|
||||
grid_line = self.add_action(QAction('&网格线', self.w_parent), 'Basic')
|
||||
grid_line.setCheckable(True)
|
||||
grid_line.setChecked(True)
|
||||
|
||||
zomm_in = self.add_action(QAction('&放大', self.w_parent), 'Basic')
|
||||
zomm_out = self.add_action(QAction('&缩小', self.w_parent), 'Basic')
|
||||
pan = self.add_action(QAction('&漫游', self.w_parent), 'Basic')
|
||||
locate = self.add_action(QAction('&定位', self.w_parent), 'Basic')
|
||||
|
||||
|
||||
|
||||
self.basic_menu.addAction(grid_line)
|
||||
self.basic_menu.addAction(zomm_in)
|
||||
self.basic_menu.addAction(zomm_out)
|
||||
self.basic_menu.addAction(pan)
|
||||
self.basic_menu.addAction(locate)
|
||||
|
||||
'''
|
||||
Preop menu
|
||||
'''
|
||||
morphology_filter = self.add_action(QAction('&形态学滤波', self.w_parent), 'filter')
|
||||
lee_filter = self.add_action(QAction('&Lee滤波', self.w_parent), 'filter')
|
||||
auto_filter = self.add_action(QAction('&自适应滤波-自主', self.w_parent), 'filter')
|
||||
auto_filter_no_params = self.add_action(QAction('自动滤波(无参自适应滤波)-自主', self.w_parent), 'filter')
|
||||
double_filter = self.add_action(QAction('&双边滤波', self.w_parent), 'filter')
|
||||
align_action = self.add_action(QAction('&配准', self.w_parent), 'align')
|
||||
filter_action_group = self.get_action_group('filter')
|
||||
|
||||
filter_menu = self.preop_menu.addMenu('&滤波')
|
||||
for action in filter_action_group.actions():
|
||||
filter_menu.addAction(action)
|
||||
|
||||
# self.preop_menu.addActionGroup(filter_action_group)
|
||||
self.preop_menu.addAction(align_action)
|
||||
|
||||
if self.toolbar is not None:
|
||||
self.toolbar.addAction(morphology_filter)
|
||||
self.toolbar.addAction(lee_filter)
|
||||
self.toolbar.addAction(auto_filter)
|
||||
self.toolbar.addAction(auto_filter_no_params)
|
||||
self.toolbar.addAction(double_filter)
|
||||
|
||||
'''
|
||||
Change detection menu
|
||||
'''
|
||||
diff_method = self.add_action(QAction('&差分法', self.w_parent), 'unsuper_change_detection')
|
||||
log_diff = self.add_action(QAction('&对数差分法', self.w_parent), 'unsuper_change_detection')
|
||||
lsts_ = self.add_action(QAction('&LSTS法', self.w_parent), 'unsuper_change_detection')
|
||||
lhba = self.add_action(QAction('&LHBA法', self.w_parent), 'unsuper_change_detection')
|
||||
aht = self.add_action(QAction('&AHT法', self.w_parent), 'unsuper_change_detection')
|
||||
kpvd = self.add_action(QAction('&KPVD法', self.w_parent), 'unsuper_change_detection')
|
||||
mohd = self.add_action(QAction('&MOHD法', self.w_parent), 'unsuper_change_detection')
|
||||
sh = self.add_action(QAction('&SH法', self.w_parent), 'unsuper_change_detection')
|
||||
cva = self.add_action(QAction('&CVA法', self.w_parent), 'unsuper_change_detection')
|
||||
mls = self.add_action(QAction('&MLS法', self.w_parent), 'unsuper_change_detection')
|
||||
pca_kmean = self.add_action(QAction('&PCA-KMean法', self.w_parent), 'unsuper_change_detection')
|
||||
semi_fcm = self.add_action(QAction('&Semi-FCM法', self.w_parent), 'unsuper_change_detection')
|
||||
mls_svm = self.add_action(QAction('&MLS-SVM法', self.w_parent), 'unsuper_change_detection')
|
||||
cva_fcm = self.add_action(QAction('&CVA-FCM法', self.w_parent), 'unsuper_change_detection')
|
||||
cva_emgmm = self.add_action(QAction('&CVA-EMGMM法', self.w_parent), 'unsuper_change_detection')
|
||||
gwdm = self.add_action(QAction('&GWDM法', self.w_parent), 'unsuper_change_detection')
|
||||
|
||||
mrf = self.add_action(QAction('&MRF法', self.w_parent), 'super_change_detection')
|
||||
mad = self.add_action(QAction('&MAD法', self.w_parent), 'super_change_detection')
|
||||
irmad = self.add_action(QAction('&IRMAD法', self.w_parent), 'super_change_detection')
|
||||
|
||||
dcva = self.add_action(QAction('&DCVA法', self.w_parent), 'ai_change_detection')
|
||||
dp_fcn = self.add_action(QAction('&DP-FCN法', self.w_parent), 'ai_change_detection')
|
||||
rcnn = self.add_action(QAction('&RCNN法', self.w_parent), 'ai_change_detection')
|
||||
|
||||
if self.toolbar is not None:
|
||||
self.toolbar.addAction(diff_method)
|
||||
self.toolbar.addAction(log_diff)
|
||||
self.toolbar.addAction(lsts_)
|
||||
self.toolbar.addAction(lhba)
|
||||
|
||||
|
||||
unsuper_change_detection = self.get_action_group('unsuper_change_detection')
|
||||
super_change_detection = self.get_action_group('super_change_detection')
|
||||
ai_change_detection = self.get_action_group('ai_change_detection')
|
||||
unsuper_menu = self.change_detection_menu.addMenu('&非监督')
|
||||
for action in unsuper_change_detection.actions():
|
||||
unsuper_menu.addAction(action)
|
||||
super_menu = self.change_detection_menu.addMenu('&监督')
|
||||
for action in super_change_detection.actions():
|
||||
super_menu.addAction(action)
|
||||
ai_menu = self.change_detection_menu.addMenu('&AI')
|
||||
for action in ai_change_detection.actions():
|
||||
ai_menu.addAction(action)
|
||||
|
||||
# self.change_detection_menu.addActionGroup(super_change_detection)
|
||||
# self.change_detection_menu.addActionGroup(ai_change_detection)
|
||||
|
||||
'''
|
||||
Special change detection menu
|
||||
'''
|
||||
|
||||
water_change = self.add_action(QAction('&水体变化', self.w_parent), 'special_change_detection')
|
||||
vegetation_change = self.add_action(QAction('&植被变化', self.w_parent), 'special_change_detection')
|
||||
build_change = self.add_action(QAction('&房屋变化', self.w_parent), 'special_change_detection')
|
||||
|
||||
self.special_chagne_detec_menu.addAction(water_change)
|
||||
self.special_chagne_detec_menu.addAction(vegetation_change)
|
||||
self.special_chagne_detec_menu.addAction(build_change)
|
||||
|
||||
'''
|
||||
Postop menu
|
||||
'''
|
||||
slide_window = self.add_action(QAction('&滑动窗口法', self.w_parent), 'noise_reduction')
|
||||
density = self.add_action(QAction('&密度法', self.w_parent), 'noise_reduction')
|
||||
|
||||
raster_export = self.add_action(QAction('&二值栅格数据导出', self.w_parent), 'export')
|
||||
txt_pos_export = self.add_action(QAction('&兼容ArcMap的坐标Txt文件', self.w_parent), 'export')
|
||||
render_export = self.add_action(QAction('&渲染图像导出', self.w_parent), 'export')
|
||||
|
||||
noise_reduction = self.get_action_group('noise_reduction')
|
||||
export = self.get_action_group('export')
|
||||
|
||||
noise_reduction_menu = self.postop_menu.addMenu('&噪声抑制')
|
||||
for action in noise_reduction.actions():
|
||||
noise_reduction_menu.addAction(action)
|
||||
export_menu = self.postop_menu.addMenu('&导出')
|
||||
for action in export.actions():
|
||||
export_menu.addAction(action)
|
||||
|
||||
# self.postop_menu.addActionGroup(noise_reduction)
|
||||
# self.postop_menu.addActionGroup(export)
|
||||
|
||||
'''
|
||||
Evaluation menu
|
||||
'''
|
||||
|
||||
evaluation = self.add_action(QAction('&评估', self.w_parent), 'evaluation')
|
||||
self.eval_menu.addAction(evaluation)
|
||||
|
||||
'''
|
||||
Help menu
|
||||
'''
|
||||
about = self.add_action(QAction('&关于', self.w_parent), 'about')
|
||||
about.triggered.connect(lambda : AboutDialog(self.w_parent).show())
|
||||
self.help_menu.addAction(about)
|
||||
|
||||
self.message_box.info('Menu init finished')
|
||||
self.message_box.info(self.actions.keys())
|
||||
for group in self.action_groups.keys():
|
||||
self.message_box.info('%s:' % (group))
|
||||
for action in self.action_groups[group].actions():
|
||||
action.setEnabled(False)
|
||||
self.message_box.info('\t%s' % (action.text()))
|
||||
|
||||
'''
|
||||
Enabled actions
|
||||
'''
|
||||
about.setEnabled(True)
|
||||
project_create.setEnabled(True)
|
||||
project_open.setEnabled(True)
|
||||
|
||||
Project().project_init.connect(self.project_init)
|
||||
|
||||
if self.status_bar is not None:
|
||||
corr_widget = QLabel(self.status_bar)
|
||||
# corr_widget.setLineWidth(200)
|
||||
corr_widget.setFixedWidth(200)
|
||||
self.status_bar.addWidget(corr_widget)
|
||||
scale_widget = QLabel(self.status_bar)
|
||||
scale_widget.setFixedWidth(200)
|
||||
self.status_bar.addWidget(scale_widget)
|
||||
self.double_map.corr_changed.connect(corr_widget.setText)
|
||||
self.double_map.scale_changed.connect(scale_widget.setText)
|
||||
|
||||
def project_create(self):
|
||||
project = Project()
|
||||
if project.is_init:
|
||||
project.save()
|
||||
|
||||
projec_create = Create(self.w_parent)
|
||||
if(projec_create.exec_()):
|
||||
project.setup(os.path.join(projec_create.file, projec_create.name + '.prj'))
|
||||
project.is_init = True
|
||||
project.cell_size = projec_create.cell_size
|
||||
|
||||
def project_init(self, state):
|
||||
self.message_box.info('Project init')
|
||||
for group in self.action_groups.keys():
|
||||
# self.message_box.info('%s:' % (group))
|
||||
for action in self.action_groups[group].actions():
|
||||
action.setEnabled(state)
|
||||
# self.message_box.info('\t%s' % (action.text()))
|
||||
|
||||
self.message_box.info('Project init finished')
|
||||
|
||||
def project_open(self):
|
||||
if Project().is_init:
|
||||
Project().save()
|
||||
|
||||
project_file = QFileDialog.getOpenFileName(self.w_parent, '打开工程', '.', '*.prj')
|
||||
if project_file[0] != '':
|
||||
Project().clear()
|
||||
Project().setup(project_file[0])
|
||||
|
||||
def project_save(self):
|
||||
if Project().is_init:
|
||||
Project().save()
|
||||
|
||||
def data_load(self):
|
||||
if Project().is_init:
|
||||
Project().save()
|
||||
|
||||
def view_setting(self):
|
||||
pass
|
||||
|
||||
|
||||
def add_action(self, action, group=None):
|
||||
if group is None:
|
||||
self.actions[action.text()] = action
|
||||
else:
|
||||
if group not in self.action_groups:
|
||||
self.action_groups[group] = QActionGroup(self.w_parent)
|
||||
self.action_groups[group].setExclusive(True)
|
||||
self.action_groups[group].addAction(action)
|
||||
self.action_group_actions[group] = action
|
||||
return action
|
||||
|
||||
def get_action(self, action_name, group_name=None):
|
||||
if action_name in self.actions:
|
||||
return self.actions[action_name]
|
||||
else:
|
||||
if group_name is None:
|
||||
return None
|
||||
else:
|
||||
if group_name not in self.action_group_actions:
|
||||
return None
|
||||
else:
|
||||
group = self.action_group_actions[group_name]
|
||||
for action in group.actions():
|
||||
if action.text() == action_name:
|
||||
return action
|
||||
return None
|
||||
|
||||
def get_action_group(self, group_name):
|
||||
return self.action_groups[group_name]
|
||||
|
||||
def get_action_group_action(self, group_name):
|
||||
return self.action_group_actions[group_name]
|
||||
|
||||
def get_action_group_actions(self, group_name):
|
||||
return self.action_group_actions[group_name].actions()
|
||||
|
||||
def get_actions(self):
|
||||
return self.actions
|
42
gui/about.py
Normal file
@ -0,0 +1,42 @@
|
||||
from PyQt5.QtWidgets import QDialog, QApplication, QLabel, QTextEdit, QVBoxLayout
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
|
||||
class AboutDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super(AboutDialog, self).__init__(parent)
|
||||
self.setWindowTitle("About")
|
||||
self.setFixedSize(800, 400)
|
||||
|
||||
self.label = QLabel("<h1>"+ QApplication.applicationName() + "</h1>")
|
||||
self.label.setAlignment(Qt.AlignCenter)
|
||||
self.label.setStyleSheet("font-size: 20px;")
|
||||
|
||||
self.label2 = QLabel("<h2>Version: " + QApplication.applicationVersion() + "</h2>")
|
||||
self.label2.setAlignment(Qt.AlignCenter)
|
||||
self.label2.setStyleSheet("font-size: 15px;")
|
||||
|
||||
self.label3 = QLabel("<h2>" + QApplication.organizationName() + "</h2>")
|
||||
self.label3.setAlignment(Qt.AlignCenter)
|
||||
self.label3.setStyleSheet("font-size: 15px;")
|
||||
|
||||
self.label4 = QLabel("<h3>Copyright (c) 2020</h3>")
|
||||
self.label4.setAlignment(Qt.AlignCenter)
|
||||
self.label4.setStyleSheet("font-size: 10px;")
|
||||
|
||||
self.text = QTextEdit()
|
||||
self.text.setReadOnly(True)
|
||||
self.text.setText('''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
''')
|
||||
|
||||
self.layout = QVBoxLayout()
|
||||
self.layout.addWidget(self.label)
|
||||
self.layout.addWidget(self.label2)
|
||||
self.layout.addWidget(self.label3)
|
||||
self.layout.addWidget(self.label4)
|
||||
self.layout.addWidget(self.text)
|
||||
self.setLayout(self.layout)
|
54
gui/layertree.py
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from PyQt5.QtCore import Qt,QModelIndex
|
||||
from PyQt5.QtGui import QStandardItemModel, QStandardItem
|
||||
from PyQt5.QtWidgets import (QTreeView, QTreeWidgetItem, QAbstractItemView, QHeaderView, QStyleFactory)
|
||||
|
||||
from utils.project import PairLayer
|
||||
|
||||
class LayerTree(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.tree_view = QTreeView(self)
|
||||
self.tree = QtWidgets.QTreeWidget(self)
|
||||
self.tree.setColumnCount(1)
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.customContextMenuRequested.connect(self.right_menu_show)
|
||||
self.root=QTreeWidgetItem(self.tree)
|
||||
self.tree.setHeaderHidden(True)
|
||||
# self.tree.setHeaderLabels(['图层'])
|
||||
self.root.setText(0,'Root')
|
||||
|
||||
child1=QTreeWidgetItem()
|
||||
child1.setText(0,'child1')
|
||||
child1.setCheckState(0,Qt.Checked)
|
||||
|
||||
self.root.addChild(child1)
|
||||
self.tree.expandAll()
|
||||
|
||||
self.tree.addTopLevelItem(self.root)
|
||||
|
||||
self.tree.clicked.connect(self.onClicked)
|
||||
|
||||
layout = QtWidgets.QGridLayout()
|
||||
layout.addWidget(self.tree)
|
||||
self.setLayout(layout)
|
||||
self.setLayoutDirection(Qt.LeftToRight)
|
||||
|
||||
def onClicked(self,index):
|
||||
print(index.row())
|
||||
|
||||
def add_layer(self, layer:PairLayer):
|
||||
pass
|
||||
|
||||
def right_menu_show(self, position):
|
||||
rightMenu = QtWidgets.QMenu(self)
|
||||
# QAction = QtWidgets.QAction(self.menuBar1)
|
||||
self.actionreboot = QtWidgets.QAction('zhangji')
|
||||
self.actionreboot.setObjectName("actionreboot")
|
||||
self.actionreboot.setText('aaa')
|
||||
rightMenu.addAction(self.actionreboot)
|
||||
|
||||
rightMenu.exec_(self.mapToGlobal(position))
|
||||
|
74
gui/license.py
Normal file
@ -0,0 +1,74 @@
|
||||
import shutil
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtGui import QIcon
|
||||
import os
|
||||
|
||||
from utils.license import LicenseHelper
|
||||
|
||||
class License(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent = None, flags = QtCore.Qt.WindowFlags() ) -> None:
|
||||
super().__init__(parent, flags)
|
||||
self.setWindowTitle("License")
|
||||
self.setWindowIcon(QIcon(os.path.join("gui", "icons", "license.png")))
|
||||
self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint)
|
||||
self.setFixedSize(600, 400)
|
||||
|
||||
self.text = QtWidgets.QLineEdit()
|
||||
self.text.setReadOnly(False)
|
||||
self.label = QtWidgets.QLabel()
|
||||
self.label.setText("License File Path: ")
|
||||
|
||||
self.setModal(True)
|
||||
|
||||
self.btn_open = QtWidgets.QPushButton("Open")
|
||||
self.btn_open.clicked.connect(self.open_file)
|
||||
|
||||
hlayout = QtWidgets.QHBoxLayout()
|
||||
hlayout.addWidget(self.label, 0, alignment=QtCore.Qt.AlignTop)
|
||||
hlayout.addWidget(self.text, 0, alignment=QtCore.Qt.AlignTop)
|
||||
hlayout.addWidget(self.btn_open, 0, alignment=QtCore.Qt.AlignTop)
|
||||
|
||||
self.btn_ok = QtWidgets.QPushButton("OK")
|
||||
self.btn_ok.clicked.connect(self.ok_clicked)
|
||||
|
||||
hlayout2 = QtWidgets.QHBoxLayout()
|
||||
hlayout2.addWidget(self.btn_ok, alignment = QtCore.Qt.AlignRight, stretch= 0)
|
||||
|
||||
vlayout = QtWidgets.QVBoxLayout()
|
||||
|
||||
infobox = QtWidgets.QTextEdit(self)
|
||||
infobox.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||
infobox.setPlainText("""
|
||||
This program is NOT free software: you can NOT redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Copy the MAC address to the clipboard and send it to the developer.
|
||||
MAC address: {}""".format(LicenseHelper().get_mac_address()))
|
||||
|
||||
vlayout.addLayout(hlayout)
|
||||
vlayout.addWidget(infobox)
|
||||
vlayout.addLayout(hlayout2)
|
||||
|
||||
self.setLayout(vlayout)
|
||||
|
||||
def open_file(self) -> None:
|
||||
file_path = QtWidgets.QFileDialog.getOpenFileName(self, "Open File", "", "License Files (*.*)")
|
||||
if file_path[0]:
|
||||
self.text.setText(file_path[0])
|
||||
# self.label.setText("License File Path: " + file_path[0])
|
||||
|
||||
def ok_clicked(self) -> None:
|
||||
if self.text.text() == "":
|
||||
QtWidgets.QMessageBox.warning(self, "Warning", "Please select a license file.")
|
||||
else:
|
||||
pth = self.text.text()
|
||||
if not os.path.exists(pth):
|
||||
QtWidgets.QMessageBox.warning(self, "Warning", "The selected file does not exist.")
|
||||
else:
|
||||
shutil.copy(pth, os.path.join("lic", "license.lic"))
|
||||
self.accept()
|
||||
self.close()
|
113
gui/mainwindow.py
Normal file
@ -0,0 +1,113 @@
|
||||
import pdb
|
||||
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow
|
||||
from PyQt5.QtCore import Qt, QSize
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5 import QtGui
|
||||
from PyQtAds import QtAds
|
||||
from actions.actions import ActionManager
|
||||
from gui.layertree import LayerTree
|
||||
from gui.mapcanvas import DoubleCanvas
|
||||
from gui.messagebox import MessageBox
|
||||
from gui.result import ResultTable
|
||||
from utils import Settings
|
||||
from utils.project import Project
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
|
||||
def __init__(self, parent=None, **kargs):
|
||||
super().__init__(parent)
|
||||
self.current_instance = kargs.get('current_instance', 0)
|
||||
if self.current_instance > 0:
|
||||
self.setWindowTitle(QApplication.applicationName() + ' ' + str(self.current_instance))
|
||||
else:
|
||||
self.setWindowTitle(QApplication.applicationName())
|
||||
self.setWindowIcon(QIcon(":/icons/logo.svg"))
|
||||
|
||||
self.setAcceptDrops(True)
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.set_toolbar()
|
||||
self.set_pannels()
|
||||
Project(self).connect(
|
||||
self.double_map,
|
||||
self.layer_tree,
|
||||
self.message_box,
|
||||
self.result_box)
|
||||
self.action_manager = ActionManager(
|
||||
self.double_map,
|
||||
self.layer_tree,
|
||||
self.follow_box,
|
||||
self.result_box,
|
||||
self.message_box, self)
|
||||
self.action_manager.set_menus(self.menuBar())
|
||||
self.action_manager.set_toolbar(self.toolbar)
|
||||
self.action_manager.set_status_bar(self.statusBar())
|
||||
|
||||
self.action_manager.set_actions()
|
||||
|
||||
self.resize(*Settings.General().size)
|
||||
|
||||
|
||||
def set_toolbar(self):
|
||||
self.toolbar = self.addToolBar('Toolbar')
|
||||
self.toolbar.setMovable(False)
|
||||
self.toolbar.setFloatable(False)
|
||||
self.toolbar.setIconSize(QSize(16, 16))
|
||||
self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
|
||||
self.toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
|
||||
self.toolbar.setLayoutDirection(Qt.LeftToRight)
|
||||
pass
|
||||
|
||||
|
||||
def set_pannels(self):
|
||||
|
||||
self.dock_manager = QtAds.CDockManager(self)
|
||||
self.dock_manager.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
|
||||
self.double_map = DoubleCanvas(self)
|
||||
central_dock_widget = QtAds.CDockWidget(self.tr("Canvas"))
|
||||
central_dock_widget.setWidget(self.double_map)
|
||||
central_dock_area = self.dock_manager.setCentralWidget(central_dock_widget)
|
||||
central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas)
|
||||
|
||||
self.double_map.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
|
||||
self.layer_tree = LayerTree(self)
|
||||
# self.layer_tree.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
|
||||
def set_docker_fixed(docker):
|
||||
docker.setFeature(QtAds.ads.CDockWidget.DockWidgetFeature.DockWidgetClosable , False)
|
||||
docker.setFeature(QtAds.ads.CDockWidget.DockWidgetFeature.DockWidgetMovable , False)
|
||||
docker.setFeature(QtAds.ads.CDockWidget.DockWidgetFeature.DockWidgetFloatable , False)
|
||||
|
||||
self.layer_tree_dock = QtAds.CDockWidget(self.tr("图层树"), self)
|
||||
|
||||
self.layer_tree_dock.setWidget(self.layer_tree)
|
||||
left_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, self.layer_tree_dock, central_dock_area)
|
||||
self.left_arre = left_area
|
||||
self.follow_dock = QtAds.CDockWidget(self.tr("流程"))
|
||||
self.follow_box = QWidget(self)
|
||||
self.follow_dock.setWidget(self.follow_box)
|
||||
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, self.follow_dock, left_area)
|
||||
|
||||
self.result_dock = QtAds.CDockWidget(self.tr("结果"))
|
||||
self.result_box = ResultTable(self)
|
||||
self.result_dock.setWidget(self.result_box)
|
||||
bottom_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, self.result_dock, central_dock_area)
|
||||
self.message_dock = QtAds.CDockWidget(self.tr("消息"))
|
||||
self.message_box = MessageBox(self, MessageBox.INFO)
|
||||
self.message_dock.setWidget(self.message_box)
|
||||
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, self.message_dock, bottom_area)
|
||||
# bottom_area.setCurrentDockWidget(self.result_dock)
|
||||
self.bottom_area = bottom_area
|
||||
|
||||
set_docker_fixed(self.layer_tree_dock)
|
||||
set_docker_fixed(self.follow_dock)
|
||||
set_docker_fixed(self.result_dock)
|
||||
set_docker_fixed(self.message_dock)
|
||||
|
||||
def closeEvent(self, event):
|
||||
pass
|
||||
|
||||
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
|
||||
Settings.General().size = (a0.size().width(), a0.size().height())
|
||||
return super().resizeEvent(a0)
|
491
gui/mapcanvas.py
Normal file
@ -0,0 +1,491 @@
|
||||
|
||||
# from alg.utils import random_color
|
||||
# from mul.mulgrubcut import GrabCut
|
||||
import multiprocessing
|
||||
# from alg.grubcut import grubcut
|
||||
# from gui.layerselect import LayerSelect
|
||||
# from gui.default import get_default_category_colors, get_default_category_keys
|
||||
# from os import truncate
|
||||
from pathlib import Path
|
||||
from PyQt5.QtCore import QSettings, QUrl, pyqtSignal, Qt, QVariant
|
||||
from PyQt5.QtWidgets import QMessageBox, QWidget, QHBoxLayout
|
||||
from PyQt5.QtGui import QColor, QDragEnterEvent, QDropEvent
|
||||
|
||||
from qgis.core import QgsPointXY, QgsRasterLayer, QgsVectorLayer, QgsFeature, QgsGeometry, QgsCategorizedSymbolRenderer, QgsRendererCategory, QgsFillSymbol, QgsPalLayerSettings, QgsRuleBasedLabeling, QgsTextFormat
|
||||
from qgis.gui import QgsMapCanvas
|
||||
from qgis.core import QgsVectorLayerExporter, QgsVectorFileWriter, QgsProject, QgsField, QgsRasterFileWriter, QgsRasterPipe
|
||||
import threading
|
||||
import tempfile
|
||||
import cv2
|
||||
import os
|
||||
|
||||
class DoubleCanvas(QWidget):
|
||||
corr_changed = pyqtSignal(str)
|
||||
scale_changed = pyqtSignal(str)
|
||||
|
||||
def __init__(self, parent = None) -> None:
|
||||
super().__init__(parent)
|
||||
self.setAcceptDrops(False)
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.mapcanva1 = CanvasWidget(self)
|
||||
self.mapcanva2 = CanvasWidget(self)
|
||||
self.mapcanva1.setCanvasColor(QColor(255, 255, 255))
|
||||
self.mapcanva2.setCanvasColor(QColor(255, 255, 255))
|
||||
|
||||
self.mapcanva1.update_coordinates_text.connect(self.corr_changed)
|
||||
self.mapcanva2.update_coordinates_text.connect(self.corr_changed)
|
||||
|
||||
self.mapcanva1.update_scale_text.connect(self.scale_changed)
|
||||
self.mapcanva2.update_scale_text.connect(self.scale_changed)
|
||||
|
||||
layout = QHBoxLayout(self)
|
||||
layout.addWidget(self.mapcanva1)
|
||||
layout.addWidget(self.mapcanva2)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
class CanvasWidget(QgsMapCanvas):
|
||||
update_coordinates_text = pyqtSignal(str)
|
||||
update_scale_text = pyqtSignal(str)
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.current_raster_layer = None
|
||||
self.current_vector_layer = None
|
||||
|
||||
self.setCanvasColor(Qt.white)
|
||||
self.enableAntiAliasing(True)
|
||||
self.setAcceptDrops(False)
|
||||
|
||||
# coordinates updated
|
||||
def coordinates2text(pt:QgsPointXY):
|
||||
return self.update_coordinates_text.emit("X: {:.5f}, Y: {:.5f}".format(pt.x(), pt.y()))
|
||||
self.xyCoordinates.connect(coordinates2text)
|
||||
self.scaleChanged.connect(lambda _ : self.update_scale_text.emit("1 : {:.3f}".format(self.scale())))
|
||||
|
||||
self.total_f = 0
|
||||
self.start_extract = False
|
||||
self.label_pal = None
|
||||
# self.result_layers = []
|
||||
|
||||
def dragEnterEvent(self, e:QDragEnterEvent) -> None:
|
||||
'''
|
||||
Can drag
|
||||
'''
|
||||
candidates = [".tif", ".tiff", ".jpg", ".jpeg", ".bmp", ".png"]
|
||||
|
||||
if e.mimeData().hasUrls():
|
||||
if Path(e.mimeData().urls()[0].toLocalFile()).suffix in candidates:
|
||||
e.accept()
|
||||
return
|
||||
|
||||
e.ignore()
|
||||
|
||||
def dropEvent(self, e:QDropEvent) -> None:
|
||||
'''
|
||||
Drop image to the canvas
|
||||
'''
|
||||
url_path = e.mimeData().urls()[0]
|
||||
image_path = QUrl(url_path).toLocalFile()
|
||||
self.load_image(image_path)
|
||||
|
||||
def load_image(self, path) -> None:
|
||||
if not Path(path).exists():
|
||||
return
|
||||
|
||||
raster_layer = QgsRasterLayer(path, Path(path).name)
|
||||
if not raster_layer.isValid():
|
||||
print("栅格图层加载失败!")
|
||||
raster_layer.file_path = path
|
||||
|
||||
# self.layers.insert(0, raster_layer)
|
||||
# self.layers.insert(0, vector_layer)
|
||||
# if self.current_raster_layer:
|
||||
# del self.current_raster_layer
|
||||
# if self.current_vector_layer:
|
||||
# del self.current_vector_layer
|
||||
|
||||
QgsProject.instance().addMapLayer(raster_layer)
|
||||
self.current_raster_layer = raster_layer
|
||||
# self.current_vector_layer = vector_layer
|
||||
self.setExtent(raster_layer.extent())
|
||||
# self.setLayers([vector_layer, raster_layer])
|
||||
self.zoomToFeatureExtent(raster_layer.extent())
|
||||
self.have_current_image.emit(True)
|
||||
|
||||
def load_result_from_txt(self, path) -> None:
|
||||
if not Path(path).exists():
|
||||
return
|
||||
# vector_layer = QgsVectorLayer("Polygon?field=category:string(20)&field=confidence:double", Path(path).name, "memory")
|
||||
vector_layer = QgsVectorLayer("Polygon?field=category:string(20)&field=confidence:double&field=renderkey:string(32)&field=isman:boolean&field=isauto:boolean&field=label:string(64)", Path(path).name + ' outline', "memory")
|
||||
|
||||
if not vector_layer.isValid():
|
||||
print("矢量图层加载失败!")
|
||||
vector_layer.setLabelsEnabled(True)
|
||||
lyr = QgsPalLayerSettings()
|
||||
lyr.enabled = True
|
||||
lyr.fieldName = 'label' # default in data sources
|
||||
# lyr.textFont = self._TestFont
|
||||
lyr.textNamedStyle = 'Medium'
|
||||
text_format = QgsTextFormat()
|
||||
text_format.color = QColor('#ffffff')
|
||||
text_format.background().color = QColor('#000000')
|
||||
text_format.buffer().setEnabled(True)
|
||||
text_format.buffer().setSize(1)
|
||||
text_format.buffer().setOpacity(0.5)
|
||||
lyr.setFormat(text_format)
|
||||
self.label_pal = lyr
|
||||
root = QgsRuleBasedLabeling.Rule(QgsPalLayerSettings())
|
||||
rule = QgsRuleBasedLabeling.Rule(lyr)
|
||||
rule.setDescription('label')
|
||||
root.appendChild(rule)
|
||||
#Apply label configuration
|
||||
rules = QgsRuleBasedLabeling(root)
|
||||
vector_layer.setLabeling(rules)
|
||||
vector_layer.triggerRepaint()
|
||||
# lyr.writeToLayer(vector_layer)
|
||||
vector_layer.setRenderer(self.__get_categorical_renderer("renderkey"))
|
||||
QgsProject.instance().addMapLayer(vector_layer)
|
||||
self.current_vector_layer = vector_layer
|
||||
# provider = self.current_vector_layer.dataProvider()
|
||||
# provider.truncate()
|
||||
self.current_vector_layer.startEditing()
|
||||
# objects = []
|
||||
features = []
|
||||
with open(path) as f:
|
||||
for line in f.readlines():
|
||||
item_data = line.split("\n")[0].split(" ")
|
||||
if len(item_data) == 1 + 4 * 2:
|
||||
cls_name = item_data[0]
|
||||
item_data[2] = -1.0 * float(item_data[2])
|
||||
item_data[4] = -1.0 * float(item_data[4])
|
||||
item_data[6] = -1.0 * float(item_data[6])
|
||||
item_data[8] = -1.0 * float(item_data[8])
|
||||
wkt = "POLYGON (({} {}, {} {}, {} {}, {} {}))".format(*item_data[1:])
|
||||
conf = 1.0
|
||||
else:
|
||||
cls_name = item_data[8]
|
||||
# print(cls_name)
|
||||
# print(cls_name[0])
|
||||
# print(cls_name[0].isalpha())
|
||||
if cls_name[0].isalpha():
|
||||
item_data[1] = -1.0 * float(item_data[1])
|
||||
item_data[3] = -1.0 * float(item_data[3])
|
||||
item_data[5] = -1.0 * float(item_data[5])
|
||||
item_data[7] = -1.0 * float(item_data[7])
|
||||
conf = 1.0
|
||||
wkt = "POLYGON (({} {}, {} {}, {} {}, {} {}))".format(*item_data[:8])
|
||||
else:
|
||||
cls_name = item_data[0]
|
||||
conf = float(item_data[1])
|
||||
item_data[3] = -1.0 * float(item_data[3])
|
||||
item_data[5] = -1.0 * float(item_data[5])
|
||||
item_data[7] = -1.0 * float(item_data[7])
|
||||
item_data[9] = -1.0 * float(item_data[9])
|
||||
wkt = "POLYGON (({} {}, {} {}, {} {}, {} {}))".format(*item_data[2:])
|
||||
|
||||
feat = QgsFeature(self.current_vector_layer.fields())
|
||||
feat.setGeometry(QgsGeometry.fromWkt(wkt))
|
||||
feat.setAttribute('category', cls_name)
|
||||
feat.setAttribute('confidence', conf)
|
||||
feat.setAttribute('renderkey', cls_name)
|
||||
feat.setAttribute('isman', False)
|
||||
feat.setAttribute('isauto', True)
|
||||
feat.setAttribute('label', f'{ cls_name},{conf:.3f}')
|
||||
features.append(feat)
|
||||
# objects.append({
|
||||
# "category": item_data[0],
|
||||
# "confidence": item_data[1],
|
||||
# "fid": feat.id()
|
||||
# })
|
||||
self.current_vector_layer.addFeatures(features)
|
||||
self.current_vector_layer.commitChanges()
|
||||
self.have_current_vector.emit(True)
|
||||
self.layer_update()
|
||||
|
||||
def clear_vector(self):
|
||||
if self.current_vector_layer is not None:
|
||||
provider = self.current_vector_layer.dataProvider()
|
||||
provider.truncate()
|
||||
self.layer_update()
|
||||
|
||||
def change_current_vector_layer(self, vector_layer):
|
||||
if self.current_vector_layer is not None:
|
||||
self.current_vector_layer.removeSelection()
|
||||
self.current_vector_layer = vector_layer
|
||||
|
||||
self.layer_update()
|
||||
|
||||
def layer_update(self):
|
||||
if self.current_vector_layer is None:
|
||||
self.object_updated.emit([])
|
||||
return
|
||||
self.current_vector_layer.updateExtents()
|
||||
self.refresh()
|
||||
objects = []
|
||||
for feature in self.current_vector_layer.getFeatures():
|
||||
objects.append({
|
||||
"category": feature['category'],
|
||||
"confidence": feature['confidence'],
|
||||
"renderkey": feature['renderkey'],
|
||||
'isman': feature['isman'],
|
||||
'isauto': feature['isauto'],
|
||||
"fid": feature.id()
|
||||
})
|
||||
self.object_updated.emit(objects)
|
||||
|
||||
def selectd_changed(self, items:list):
|
||||
if len(items) == 0:
|
||||
self.current_vector_layer.removeSelection()
|
||||
else:
|
||||
self.current_vector_layer.selectByIds(list(item['fid'] for item in items))
|
||||
|
||||
def item_change(self, items:list):
|
||||
self.current_vector_layer.startEditing()
|
||||
features = list(self.current_vector_layer.getFeatures())
|
||||
for f in features:
|
||||
has_f = False
|
||||
for item in items:
|
||||
if f.id() == item['fid']:
|
||||
# f = QgsFeature(f)
|
||||
has_f = True
|
||||
f.setAttribute('category', item['category'])
|
||||
f.setAttribute('confidence', item['confidence'])
|
||||
f.setAttribute('renderkey', item['renderkey'])
|
||||
f.setAttribute('isman', item['isman'])
|
||||
f.setAttribute('isauto', item['isauto'])
|
||||
self.current_vector_layer.updateFeature(f)
|
||||
break
|
||||
if has_f:
|
||||
continue
|
||||
|
||||
self.current_vector_layer.deleteFeature(f.id())
|
||||
|
||||
self.current_vector_layer.commitChanges()
|
||||
self.current_vector_layer.updateExtents()
|
||||
# print(self.current_vector_layer.fields())
|
||||
|
||||
self.refresh()
|
||||
|
||||
def zoom_to_full_extent(self) -> None:
|
||||
if self.current_raster_layer:
|
||||
self.zoomToFeatureExtent(self.current_raster_layer.extent())
|
||||
|
||||
def __get_categorical_renderer(self, fieldname:str) -> QgsCategorizedSymbolRenderer:
|
||||
settings = QSettings(self)
|
||||
|
||||
category_keys = settings.value("keys", get_default_category_keys())
|
||||
category_colors = settings.value("colors", get_default_category_colors())
|
||||
|
||||
settings.beginGroup("Category")
|
||||
if len(category_colors) < len(category_keys):
|
||||
for _ in range(len(category_keys) - len(category_colors)):
|
||||
category_colors.append(random_color())
|
||||
settings.setValue('colors', category_colors)
|
||||
settings.endGroup()
|
||||
categorized_renderer = QgsCategorizedSymbolRenderer()
|
||||
for key, color in zip(category_keys, category_colors):
|
||||
fill_color = QColor(color)
|
||||
fill_color.setAlphaF(0.3)
|
||||
categorized_renderer.addCategory(\
|
||||
QgsRendererCategory(
|
||||
key,
|
||||
QgsFillSymbol.createSimple(
|
||||
{"color":fill_color.name(QColor.HexArgb),"outline_color":color, "outline_width":"1"}), ''))
|
||||
categorized_renderer.setClassAttribute(fieldname)
|
||||
return categorized_renderer
|
||||
|
||||
def export_to_raster(self, path) -> None:
|
||||
if self.current_vector_layer is None:
|
||||
return
|
||||
|
||||
def load_extract_result(self, res):
|
||||
r = self.current_raster_layer
|
||||
vector_layer = QgsVectorLayer("Polygon?field=category:string(20)&field=confidence:double&field=renderkey:string(32)&field=isman:boolean&field=isauto:boolean", Path(r.file_path).name + ' outline', "memory")
|
||||
# vector_layer = QgsVectorLayer(tempfile)
|
||||
if not vector_layer.isValid():
|
||||
print("矢量图层加载失败!")
|
||||
|
||||
vector_layer.setRenderer(self.__get_categorical_renderer("renderkey"))
|
||||
lyr = QgsPalLayerSettings()
|
||||
lyr.enabled = True
|
||||
lyr.fieldName = 'label' # default in data sources
|
||||
# lyr.textFont = self._TestFont
|
||||
lyr.textNamedStyle = 'Medium'
|
||||
text_format = QgsTextFormat()
|
||||
text_format.color = QColor('#ffffff')
|
||||
text_format.background().color = QColor('#000000')
|
||||
text_format.buffer().setEnabled(True)
|
||||
text_format.buffer().setSize(1)
|
||||
text_format.buffer().setOpacity(0.5)
|
||||
lyr.setFormat(text_format)
|
||||
self.label_pal = lyr
|
||||
root = QgsRuleBasedLabeling.Rule(QgsPalLayerSettings())
|
||||
rule = QgsRuleBasedLabeling.Rule(lyr)
|
||||
rule.setDescription('label')
|
||||
root.appendChild(rule)
|
||||
#Apply label configuration
|
||||
rules = QgsRuleBasedLabeling(root)
|
||||
vector_layer.setLabeling(rules)
|
||||
vector_layer.triggerRepaint()
|
||||
vector_layer.startEditing()
|
||||
features = []
|
||||
for f in res:
|
||||
pts = f[0]
|
||||
prop = f[1]
|
||||
# pts = grubcut(img_path, pts, False, True, False )
|
||||
pts = list( f'{p[0]} {p[1]}' for p in pts )
|
||||
wkt = f'POLYGON (( {",".join(pts)} ))'
|
||||
# geometry = QgsGeometry.fromWkt(wkt)
|
||||
feat = QgsFeature(vector_layer.fields())
|
||||
feat.setGeometry(QgsGeometry.fromWkt(wkt))
|
||||
feat.setAttribute('category', prop['category'])
|
||||
feat.setAttribute('confidence', prop['confidence'])
|
||||
feat.setAttribute('renderkey', prop['category'])
|
||||
feat.setAttribute('isman', False)
|
||||
feat.setAttribute('isauto', True)
|
||||
features.append(feat)
|
||||
|
||||
vector_layer.addFeatures(features)
|
||||
vector_layer.commitChanges()
|
||||
QgsProject.instance().addMapLayer(vector_layer)
|
||||
self.process_end.emit()
|
||||
r.has_extract = True
|
||||
self.start_extract = False
|
||||
r.extract_layer = vector_layer
|
||||
self.layer_update()
|
||||
|
||||
def run_thread(self, conn, pp):
|
||||
all_ok = False
|
||||
# print(pp.is_alive)
|
||||
while pp.is_alive:
|
||||
r = conn.recv()
|
||||
# print(r)
|
||||
if all_ok:
|
||||
self.extract_end.emit(r)
|
||||
break
|
||||
if int(r) == self.total_f - 1:
|
||||
all_ok = True
|
||||
self.process_update.emit(r)
|
||||
# print(conn.recv())
|
||||
def grubcut(self, v, r):
|
||||
# for f in v.getFeatures():
|
||||
if self.start_extract:
|
||||
return
|
||||
self.current_raster_layer = r
|
||||
img_path = r.file_path
|
||||
if getattr(r, 'has_extract', False):
|
||||
vector_layer = r.extract_layer
|
||||
try:
|
||||
QgsProject.instance().removeMapLayer(vector_layer)
|
||||
except:
|
||||
pass
|
||||
|
||||
# self.current_vector_layer = vector_layer
|
||||
features = []
|
||||
points = []
|
||||
for f in v.getFeatures():
|
||||
pts = f.geometry().vertices()
|
||||
pts = list([ vr.x(), vr.y() ] for vr in pts)
|
||||
points.append(pts)
|
||||
features.append({
|
||||
'category': f['category'],
|
||||
'confidence': f['confidence']
|
||||
})
|
||||
self.total_f = len(points)
|
||||
self.start_extract = True
|
||||
self.process_start.emit([0, self.total_f])
|
||||
parent_conn, child_conn = multiprocessing.Pipe()
|
||||
t = GrabCut(child_conn, img_path, points, features)
|
||||
p = threading.Thread(target=self.run_thread, args=(parent_conn,t))
|
||||
t.start()
|
||||
p.start()
|
||||
|
||||
def export_to(self, path, filter_name) -> None:
|
||||
if filter_name == 'Shp (*.shp)':
|
||||
if self.current_vector_layer is None:
|
||||
return
|
||||
ls = LayerSelect(self)
|
||||
ls.show()
|
||||
ls.exec()
|
||||
if ls.result() == LayerSelect.OK:
|
||||
save_options = QgsVectorFileWriter.SaveVectorOptions()
|
||||
save_options.driverName = "ESRI Shapefile"
|
||||
save_options.fileEncoding = "UTF-8"
|
||||
transform_context = QgsProject.instance().transformContext()
|
||||
error = QgsVectorFileWriter.writeAsVectorFormatV2(ls.value,
|
||||
path,
|
||||
transform_context,
|
||||
save_options)
|
||||
if error[0] == QgsVectorFileWriter.NoError:
|
||||
print("又成功了!")
|
||||
else:
|
||||
print(error)
|
||||
|
||||
if filter_name == 'JPEG Images(*.jpg)':
|
||||
file_name = path + '.tif'
|
||||
extent = self.current_raster_layer.extent()
|
||||
width, height = self.current_raster_layer.width(), self.current_raster_layer.height()
|
||||
|
||||
pipe = QgsRasterPipe()
|
||||
provider = self.current_raster_layer.dataProvider()
|
||||
pipe.set(provider.clone())
|
||||
|
||||
file_writer = QgsRasterFileWriter(file_name)
|
||||
error = file_writer.writeRaster(pipe,
|
||||
width,
|
||||
height,
|
||||
extent,
|
||||
self.current_raster_layer.crs())
|
||||
|
||||
target_img = cv2.imread(file_name)
|
||||
jpg_file_name = path + '.jpg'
|
||||
cv2.imwrite(jpg_file_name, target_img)
|
||||
os.remove(file_name)
|
||||
if error == QgsRasterFileWriter.NoError:
|
||||
QMessageBox.about(self, 'Export Files', '导出JPEG图像成功!')
|
||||
else:
|
||||
QMessageBox.about(self, 'Export Files', '导出JPEG图像失败!')
|
||||
|
||||
if filter_name == 'TIFF Images(*.tif)':
|
||||
file_name = path + '.tif'
|
||||
extent = self.current_raster_layer.extent()
|
||||
width, height = self.current_raster_layer.width(), self.current_raster_layer.height()
|
||||
|
||||
pipe = QgsRasterPipe()
|
||||
provider = self.current_raster_layer.dataProvider()
|
||||
pipe.set(provider.clone())
|
||||
|
||||
file_writer = QgsRasterFileWriter(file_name)
|
||||
error = file_writer.writeRaster(pipe,
|
||||
width,
|
||||
height,
|
||||
extent,
|
||||
self.current_raster_layer.crs())
|
||||
if error == QgsRasterFileWriter.NoError:
|
||||
QMessageBox.about(self, 'Export Files', '导出TIFF图像成功!')
|
||||
else:
|
||||
QMessageBox.about(self, 'Export Files', '导出TIFF图像失败!')
|
||||
if filter_name == 'PNG Images(*.png)':
|
||||
file_name = path + '.tif'
|
||||
extent = self.current_raster_layer.extent()
|
||||
width, height = self.current_raster_layer.width(), self.current_raster_layer.height()
|
||||
|
||||
pipe = QgsRasterPipe()
|
||||
provider = self.current_raster_layer.dataProvider()
|
||||
pipe.set(provider.clone())
|
||||
|
||||
file_writer = QgsRasterFileWriter(file_name)
|
||||
error = file_writer.writeRaster(pipe,
|
||||
width,
|
||||
height,
|
||||
extent,
|
||||
self.current_raster_layer.crs())
|
||||
target_img = cv2.imread(file_name)
|
||||
jpg_file_name = path + '.png'
|
||||
cv2.imwrite(jpg_file_name, target_img)
|
||||
os.remove(file_name)
|
||||
if error == QgsRasterFileWriter.NoError:
|
||||
QMessageBox.about(self, 'Export Files', '导出PNG图像成功!')
|
||||
else:
|
||||
QMessageBox.about(self, 'Export Files', '导出PNG图像失败!')
|
70
gui/messagebox.py
Normal file
@ -0,0 +1,70 @@
|
||||
from PyQt5.QtWidgets import QTextEdit
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5.QtGui import QTextCursor
|
||||
from PyQt5.QtCore import Qt
|
||||
from datetime import datetime, time
|
||||
class MessageBox(QTextEdit):
|
||||
|
||||
INFO=0
|
||||
WARNING=1
|
||||
DEBUG=2
|
||||
ERROR=3
|
||||
|
||||
def __init__(self, parent=None, level=0):
|
||||
super().__init__(parent)
|
||||
self.setReadOnly(True)
|
||||
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
||||
self.setStyleSheet("QTextEdit { background-color: #f0f0f0; }")
|
||||
self.msg = ''
|
||||
self.level = level
|
||||
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.customContextMenuRequested.connect(self.right_menu_show)
|
||||
|
||||
def right_menu_show(self, position):
|
||||
rightMenu = QtWidgets.QMenu(self)
|
||||
# QAction = QtWidgets.QAction(self.menuBar1)
|
||||
action = QtWidgets.QAction('清空')
|
||||
action.triggered.connect(self.clear)
|
||||
rightMenu.addAction(action)
|
||||
|
||||
rightMenu.exec_(self.mapToGlobal(position))
|
||||
|
||||
def append(self, text):
|
||||
self.msg += '<br/>' + text
|
||||
self.setText(self.msg)
|
||||
cursor = self.textCursor()
|
||||
# QTextCursor
|
||||
cursor.movePosition(QTextCursor.End)
|
||||
self.setTextCursor(cursor)
|
||||
|
||||
def info(self, text):
|
||||
if self.level <= self.INFO:
|
||||
timestr = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.append('<span style="color:green">[INFO] %s</span> <br/>'%timestr + str(text))
|
||||
# self.append(text)
|
||||
# self.append(text)
|
||||
|
||||
def warning(self, text):
|
||||
if self.level <= self.WARNING:
|
||||
timestr = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.append('<span style="color:yellow">[WARNING] %s</span> <br/>'%timestr + str(text))
|
||||
|
||||
def debug(self, text):
|
||||
if self.level <= self.DEBUG:
|
||||
timestr = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.append('<span style="color:green">[DEBUG] %s</span> <br/>'%timestr + str(text))
|
||||
# self.append(text)
|
||||
# self.append(text)
|
||||
|
||||
def error(self, text):
|
||||
if self.level <= self.ERROR:
|
||||
timestr = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.append('<span style="color:red">[ERROR] %s</span> <br/>'%timestr + str(text))
|
||||
# self.append(text)
|
||||
# self.append(text)
|
||||
|
||||
def clear(self):
|
||||
self.msg = ''
|
||||
self.setText(self.msg)
|
||||
|
72
gui/project.py
Normal file
@ -0,0 +1,72 @@
|
||||
from PyQt5.QtWidgets import QDialog, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel, QMessageBox
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtCore import Qt
|
||||
from utils.setting import Settings
|
||||
class Create(QDialog):
|
||||
|
||||
def __init__(self, parent=None) -> None:
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle('Create Project')
|
||||
self.setWindowIcon(QIcon(":/icons/logo.svg"))
|
||||
|
||||
self.file = str(Settings.General().root)
|
||||
self.name = '未命名'
|
||||
self.max_memory = Settings.Project().max_memory
|
||||
self.cell_size = Settings.Project().cell_size
|
||||
|
||||
file_label = QLabel('Project Dir:')
|
||||
file_label.setFixedWidth(100)
|
||||
file_input = QLineEdit()
|
||||
file_input.setPlaceholderText('Project Dir')
|
||||
file_input.setToolTip('Project Dir')
|
||||
file_input.setReadOnly(True)
|
||||
file_input.setText(self.file)
|
||||
|
||||
name_label = QLabel('Project Name:')
|
||||
name_label.setFixedWidth(100)
|
||||
name_input = QLineEdit()
|
||||
name_input.setPlaceholderText('Project Name')
|
||||
name_input.setToolTip('Project Name')
|
||||
|
||||
name_input_layout = QHBoxLayout()
|
||||
name_input_layout.addWidget(name_label)
|
||||
name_input_layout.addWidget(name_input)
|
||||
|
||||
file_input_layout = QHBoxLayout()
|
||||
file_input_layout.addWidget(file_label)
|
||||
file_input_layout.addWidget(file_input)
|
||||
|
||||
cell_size_label = QLabel('Cell Size:')
|
||||
cell_size_label.setFixedWidth(100)
|
||||
cell_size_x_label = QLabel('X:')
|
||||
cell_size_y_label = QLabel('Y:')
|
||||
cell_size_x_input = QLineEdit()
|
||||
cell_size_y_input = QLineEdit()
|
||||
cell_size_x_input.setPlaceholderText('Cell Size X')
|
||||
cell_size_y_input.setPlaceholderText('Cell Size Y')
|
||||
cell_size_x_input.setToolTip('Cell Size X')
|
||||
cell_size_y_input.setToolTip('Cell Size Y')
|
||||
|
||||
cell_input_layout = QHBoxLayout()
|
||||
cell_input_layout.addWidget(cell_size_label)
|
||||
cell_input_layout.addWidget(cell_size_x_label)
|
||||
cell_input_layout.addWidget(cell_size_x_input)
|
||||
cell_input_layout.addWidget(cell_size_y_label)
|
||||
cell_input_layout.addWidget(cell_size_y_input)
|
||||
|
||||
ok_button = QPushButton('OK')
|
||||
cancel_button = QPushButton('Cancel')
|
||||
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.setDirection(QHBoxLayout.RightToLeft)
|
||||
button_layout.addWidget(ok_button, 0, Qt.AlignRight)
|
||||
button_layout.addWidget(cancel_button, 0, Qt.AlignRight)
|
||||
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.addLayout(file_input_layout)
|
||||
main_layout.addLayout(name_input_layout)
|
||||
main_layout.addLayout(cell_input_layout)
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
self.setLayout(main_layout)
|
||||
|
41
gui/result.py
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from PyQt5.QtCore import Qt,QModelIndex
|
||||
from PyQt5.QtGui import QStandardItemModel, QStandardItem
|
||||
from PyQt5.QtWidgets import (QTreeView, QTreeWidgetItem, QAbstractItemView, QHeaderView, QStyleFactory)
|
||||
|
||||
from utils.project import PairLayer
|
||||
|
||||
class ResultTable(QtWidgets.QWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(ResultTable, self).__init__(parent)
|
||||
self.tree_view = QTreeView(self)
|
||||
self.tree = QtWidgets.QTreeWidget(self)
|
||||
self.tree.setColumnCount(1)
|
||||
self.tree.setColumnWidth(0,150)
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.root=QTreeWidgetItem(self.tree)
|
||||
self.tree.setHeaderLabels(['图层'])
|
||||
self.root.setText(0,'Root')
|
||||
|
||||
child1=QTreeWidgetItem()
|
||||
child1.setText(0,'child1')
|
||||
child1.setCheckState(0,Qt.Checked)
|
||||
|
||||
self.root.addChild(child1)
|
||||
self.tree.expandAll()
|
||||
|
||||
self.tree.addTopLevelItem(self.root)
|
||||
|
||||
self.tree.clicked.connect(self.onClicked)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout(self)
|
||||
layout.addWidget(self.tree)
|
||||
self.setLayout(layout)
|
||||
|
||||
def onClicked(self,index):
|
||||
print(index.row())
|
||||
|
||||
def add_layer(self, layer:PairLayer):
|
||||
pass
|
0
gui/setting.py
Normal file
1
icons/assessment.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621607588767" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="39677" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M288 160v32H160v704h704V192h-128V160h-64v32H352V160zM224 256h64v32h64V256h320v32h64V256h64v64H224z m0 128h576v448H224z m424.992 72.992L480 625.984l-72.992-72.96-46.016 45.984 96 96 23.008 21.984 23.008-21.984 192-192z" p-id="39678" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 638 B |
1
icons/cancel.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632402985895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3426" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M509.92 475.968l74.032-74.032a16 16 0 0 1 22.624 0l11.312 11.312a16 16 0 0 1 0 22.64L543.84 509.92l74.032 74.032a16 16 0 0 1 0 22.624l-11.312 11.312a16 16 0 0 1-22.624 0L509.92 543.84l-74.032 74.032a16 16 0 0 1-22.64 0l-11.312-11.312a16 16 0 0 1 0-22.624l74.032-74.032-74.032-74.032a16 16 0 0 1 0-22.64l11.312-11.312a16 16 0 0 1 22.64 0l74.032 74.032z m0 319.856c157.904 0 285.92-128 285.92-285.92C795.84 352 667.808 224 509.92 224 352 224 224 352 224 509.92c0 157.904 128 285.92 285.92 285.92z m0 48C325.504 843.84 176 694.336 176 509.92 176 325.52 325.504 176 509.92 176c184.416 0 333.92 149.504 333.92 333.92 0 184.416-149.504 333.92-333.92 333.92z" p-id="3427" fill="#e6922d"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
1
icons/clear.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621607368815" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="39116" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M604.992 131.008c-25.728 0-52.48 8.736-72.992 26.976v1.024L531.008 160 156.992 531.008c-38.72 38.72-38.496 101.856-1.984 142.976l0.992 1.024h0.992l192 192c38.752 38.72 101.888 38.496 143.008 1.984v-0.992L864 496c40.512-40.512 41.76-105.28 3.008-144l-192-192a97.888 97.888 0 0 0-70.016-28.992z m-0.992 64.992c10.24 0 19.744 2.752 26.016 8.992l192 192c12.352 12.384 14.08 36.896-3.008 54.016l-161.024 160.992-244.992-244.992 162.016-160.992 0.992-1.024c8.128-6.72 18.24-8.992 28-8.992z m-236.992 216.992l244.992 244.992-163.008 163.008c-0.352 0.256-0.64 0.768-0.992 1.024-16.512 13.76-41.888 12.096-54.016 0L203.04 632c-0.512-0.64-0.512-1.376-1.024-2.016-13.12-16.48-12-41.984 0-53.984z" p-id="39117" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 1.1 KiB |
1
icons/edit.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621607314296" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="38929" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M448 128c-35.36 0-64 28.64-64 64v2.016L221.984 296A64.288 64.288 0 0 0 192 288c-35.36 0-64 28.64-64 64 0 23.616 12.864 43.872 32 55.008V776.96c-19.136 11.136-32 31.36-32 55.008 0 35.36 28.64 64 64 64 23.616 0 43.872-12.864 55.008-32H648.96c11.136 19.136 31.36 32 55.008 32 35.36 0 64-28.64 64-64 0-12.736-3.52-24.992-10.016-35.008L836 640a64.16 64.16 0 0 0 8-127.008l-60.992-182.976C793.12 318.624 800 304.352 800 288c0-35.36-28.64-64-64-64-16.384 0-30.624 6.88-42.016 16.992L511.04 179.008A64.384 64.384 0 0 0 448 128z m42.016 112l182.976 60c4.896 25.76 25.28 46.112 51.008 51.008l60.992 181.984A63.68 63.68 0 0 0 768 576c0 13.248 4.128 25.76 11.008 36L700 768a63.616 63.616 0 0 0-51.008 32H247.04A63.392 63.392 0 0 0 224 776.992V407.04c19.136-11.136 32-31.36 32-55.008v-2.016l162.016-101.984c8.96 4.736 19.104 8 29.984 8 16.128 0 30.72-6.112 42.016-16z" p-id="38930" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
icons/exit.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621608444143" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="63716" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M160 160v704h704V160z m64 64h576v576H224z m150.016 106.016l-45.024 44.992L465.984 512l-138.976 139.008 44.992 44.992 139.008-139.008 137.984 138.016 45.024-45.024L556 512l136-136-44.992-44.992-136 136z" p-id="63717" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 622 B |
1
icons/export.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621607403815" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="39303" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M192 128v768h640v-256l-64 64v128H256V192h512v128l64 64V128z m524.992 224L672 396.992 754.016 480h-309.024v64h308.992L672 627.008 716.992 672l138.016-136.992 21.984-23.008-21.984-23.008z" p-id="39304" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 606 B |
1
icons/font.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1631612098602" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="53542" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M480 192L256 832h64l67.008-192h249.984L704 832h64L544 192z m32 91.008L614.016 576h-204z" p-id="53543" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 508 B |
1
icons/full.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1604376212656" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17876" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M128 128v288h64V237.984L466.016 512 192 786.016V608H128v288h288v-64H237.984L512 557.984 786.016 832H608v64h288v-288h-64v178.016L557.984 512 832 237.984V416h64V128h-288v64h178.016L512 466.016 237.984 192H416V128z" p-id="17877" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 632 B |
1
icons/load.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621592174373" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18094" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M64 160v704h896V160z m64 64h768v444.992l-168.992-169.984-23.008-23.008-144.992 144.992-184-185.984-23.008-23.008-224 224z m640 64a63.968 63.968 0 1 0 0 128 63.968 63.968 0 1 0 0-128z m-416 215.008L646.016 800H128v-72.992z m352 64l192 192V800h-159.008l-132.992-134.016z" p-id="18095" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 689 B |
1
icons/logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621592112747" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17907" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M160 160v128h32v448H160v128h128v-32h448v32h128v-128h-32V288h32V160h-128v32H288V160z m128 96h448v32h32v448h-32v32H288v-32H256V288h32z m32 64v256h128v128h256v-256h-128v-128z m64 64h128v128h-128z m192 128h64v128h-128v-64h64z" p-id="17908" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 642 B |
1
icons/model.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621607539796" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="39490" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 94.016L148.992 225.952 128 234.016v489.024l16.992 8.992 352 192 15.008 8 15.008-8 352-192 16.992-8.992V233.984l-20.992-8z m0 67.968l268 97.024L512 380.992 244 259.008zM512 224c-35.36 0-64 14.4-64 32s28.64 32 64 32 64-14.4 64-32-28.64-32-64-32zM192 304.992l288 131.008v406.016l-288-157.024z m640 0v380l-288 157.024V435.968zM404.992 448c-11.84 0-20.992 11.36-20.992 28 0 21.376 11.36 43.52 28 52.992 4.736 2.368 10.24 2.016 15.008 2.016 7.104 0 11.232-2.24 16-7.008 2.368-4.736 4.992-11.52 4.992-20.992 0-21.376-14.4-43.52-31.008-52.992-4.736-2.4-7.232-2.016-12-2.016z m236 32a30.272 30.272 0 0 0-8 3.008c-19.52 8.352-36 36-36 60.992 0 8.384-0.64 16.384 5.024 22.016 5.6 5.6 11.616 8.96 20 8.96 5.6 0 11.36-0.224 16.96-2.976 19.52-11.136 33.024-36.736 33.024-59.008 0-19.52-8.256-32.992-24.992-32.992-1.376 0-3.648-0.384-6.016 0z m-316.992 40.992c-11.872 0-20.992 11.392-20.992 28 0 21.376 11.36 42.496 28 52 4.736 2.368 9.248 3.008 13.984 3.008 7.136 0 12.256-2.24 16.992-7.008 2.4-4.736 5.024-12.48 5.024-21.984 0-21.376-14.4-42.496-31.008-52-4.736-2.368-7.264-2.016-12-2.016z m428.992 37.024c-2.368 0.352-5.248 0.608-8 1.984-19.52 8.384-36 36.992-36 62.016 0 8.352 0.384 16.352 6.016 21.984 5.632 5.632 10.624 8 18.976 8 5.632 0 11.392 0.736 17.024-2.016 19.52-11.104 32.992-36.736 32.992-58.976 0-19.52-8.256-33.024-24.992-33.024-1.376 0-3.648-0.352-6.016 0z m-508 30.976c-11.84 0-20.992 11.392-20.992 28 0 21.376 11.36 43.52 28 53.024 4.736 2.368 10.24 1.984 15.008 1.984 7.104 0 11.232-2.24 16-7.008 2.368-4.736 4.992-11.52 4.992-20.992 0-21.376-14.4-43.52-31.008-52.992-4.736-2.368-7.232-2.016-12-2.016z" p-id="39491" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 2.0 KiB |
1
icons/ok.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632402912710" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2494" width="48" height="48" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M512 1024a512 512 0 1 1 512-512 512.576 512.576 0 0 1-512 512z m0-960a448 448 0 1 0 448 448A448.512 448.512 0 0 0 512 64z m-34.848 630.4a32 32 0 0 1-22.688 9.6H454.4a32 32 0 0 1-22.624-9.376l-166.4-166.4A32 32 0 0 1 310.624 483.2l143.424 143.36 259.2-264.864a32 32 0 1 1 45.76 44.8z" p-id="2495" fill="#529834"></path></svg>
|
After Width: | Height: | Size: 699 B |
1
icons/outline.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1632464494060" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8621" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M291.3888 489.216a132.48 132.48 0 1 1 0.064-264.96 132.48 132.48 0 0 1-0.064 264.96z m-132.48 335.488V607.936a176.064 176.064 0 0 1 132.48-59.84c46.976 0 89.664 18.368 121.28 48.32a454.144 454.144 0 0 1 452.48-282.88v511.168H158.9728zM82.4928 295.168a41.216 41.216 0 1 1-82.368 0C0.0608 252.16 10.3008 210.56 29.7568 173.12a41.152 41.152 0 1 1 73.088 38.08 181.632 181.632 0 0 0-20.416 83.968z m102.976-163.84A41.152 41.152 0 1 1 150.0768 56.832 259.072 259.072 0 0 1 278.9728 32.704a41.216 41.216 0 0 1-5.696 82.304 175.68 175.68 0 0 0-87.872 16.384z m216.32 17.088a41.28 41.28 0 0 1 42.368-70.656 289.28 289.28 0 0 1 89.216 85.312 41.28 41.28 0 0 1-67.84 46.72c-17.28-25.024-38.4-46.08-63.808-61.376z m172.16 123.52a41.152 41.152 0 1 1-49.856-65.728 475.392 475.392 0 0 1 100.096-58.56 41.152 41.152 0 1 1 32.576 75.776 392.96 392.96 0 0 0-82.816 48.512z m212.992-81.984a41.216 41.216 0 0 1-9.728-81.92c31.936-3.776 65.792-5.568 102.4-5.568h5.12a41.216 41.216 0 0 1-0.192 82.432h-4.928c-33.472 0-64.128 1.664-92.672 5.056z m154.816 17.664a41.216 41.216 0 1 1 82.368 0v106.048a41.216 41.216 0 1 1-82.368 0V207.616z m0 249.6a41.216 41.216 0 1 1 82.368 0v106.048a41.216 41.216 0 1 1-82.368 0V457.216z m0 256a41.216 41.216 0 1 1 82.368 0v106.048a41.216 41.216 0 1 1-82.368 0v-106.048z m4.352 196.352a41.216 41.216 0 0 1 0 82.432h-105.92a41.216 41.216 0 0 1 0-82.432h105.92z m-247.168 0a41.216 41.216 0 0 1 0 82.432H592.9568a41.216 41.216 0 0 1 0-82.432h105.92z m-247.168 0a41.216 41.216 0 0 1 0 82.432H345.7888a41.216 41.216 0 0 1 0-82.432h105.92z m-247.168 0a41.216 41.216 0 0 1 0 82.432H98.6208a41.216 41.216 0 0 1 0-82.432h105.92zM82.4288 825.6A41.216 41.216 0 1 1 0.0608 825.6v-106.048a41.216 41.216 0 1 1 82.368 0v106.048z m0-249.6A41.216 41.216 0 1 1 0.0608 576V470.016a41.216 41.216 0 1 1 82.368 0v106.048z m0-248.576a41.216 41.216 0 1 1-82.368 0v-23.872a41.216 41.216 0 1 1 82.368 0v23.872z" fill="#548235" p-id="8622" data-spm-anchor-id="a313x.7781069.0.i16" class="selected"></path></svg>
|
After Width: | Height: | Size: 2.3 KiB |
1
icons/paint.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1631610702873" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="53289" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M160 128v256h576V128z m64 64h448v128H224z m544 32v64h32v136l-296.992 88.992-23.008 7.008V608h-64v288h192v-288h-64v-40l296.992-88.992 23.008-7.008V224z m-288 448h64v160h-64z" p-id="53290" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 593 B |
1
icons/pan.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621606142680" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18655" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 64c-44.256 0-80.992 31.008-92 72C408.736 131.36 396.864 128 384 128c-52.64 0-96 43.36-96 96v344l-28-28a96.736 96.736 0 0 0-136 0 96.736 96.736 0 0 0 0 136l216.992 216.992C378.624 930.496 430.624 960 492 960H640c123.36 0 224-100.64 224-224V352c0-52.64-43.36-96-96-96a94.4 94.4 0 0 0-32 6.016V224c0-52.64-43.36-96-96-96-12.864 0-24.736 3.36-36 8C592.992 95.008 556.256 64 512 64z m0 64c18.112 0 32 13.888 32 32v320h64V224c0-18.112 13.888-32 32-32 18.112 0 32 13.888 32 32v256h64v-128c0-18.112 13.888-32 32-32 18.112 0 32 13.888 32 32v384c0 88.736-71.264 160-160 160h-148c-40.736 0-75.744-19.872-104.992-48.992l-218.016-216a32.448 32.448 0 0 1 0-46.016 32.448 32.448 0 0 1 46.016 0l81.984 83.008L352 723.008V224c0-18.112 13.888-32 32-32 18.112 0 32 13.888 32 32v256h64V160c0-18.112 13.888-32 32-32z" p-id="18656" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
67
icons/qt.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="462pt"
|
||||
height="339pt"
|
||||
viewBox="0 0 462 339"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="TheQtCompany_logo_2.svg">
|
||||
<metadata
|
||||
id="metadata20">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs18" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1536"
|
||||
inkscape:window-height="801"
|
||||
id="namedview16"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.1138643"
|
||||
inkscape:cx="270.58047"
|
||||
inkscape:cy="174.65092"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<path
|
||||
fill="#548235"
|
||||
d=" M 63.50 0.00 L 462.00 0.00 L 462.00 274.79 C 440.60 296.26 419.13 317.66 397.61 339.00 L 0.00 339.00 L 0.00 63.39 C 21.08 42.18 42.34 21.13 63.50 0.00 Z"
|
||||
id="path6" />
|
||||
<path
|
||||
d=" M 122.37 71.33 C 137.50 61.32 156.21 58.79 174.00 58.95 C 190.94 59.16 208.72 62.13 222.76 72.24 C 232.96 79.41 239.59 90.48 244.01 101.93 C 251.16 120.73 253.26 141.03 253.50 161.01 C 253.53 181.13 252.62 201.69 245.96 220.86 C 241.50 233.90 233.01 245.48 221.81 253.52 C 229.87 266.58 238.09 279.54 246.15 292.60 C 236.02 297.27 225.92 301.97 215.78 306.62 C 207.15 292.38 198.56 278.11 189.90 263.89 C 178.19 265.81 166.21 265.66 154.44 264.36 C 140.34 262.67 125.97 258.37 115.09 248.88 C 106.73 241.64 101.48 231.51 97.89 221.21 C 92.01 203.79 90.43 185.25 90.16 166.97 C 90.02 147.21 91.28 127.14 97.24 108.18 C 101.85 93.92 109.48 79.69 122.37 71.33 Z"
|
||||
id="path8"
|
||||
fill="#ffffff" />
|
||||
<path
|
||||
d=" M 294.13 70.69 C 304.73 70.68 315.33 70.68 325.93 70.69 C 325.96 84.71 325.92 98.72 325.95 112.74 C 339.50 112.76 353.05 112.74 366.60 112.75 C 366.37 121.85 366.12 130.95 365.86 140.05 C 352.32 140.08 338.79 140.04 325.25 140.07 C 325.28 163.05 325.18 186.03 325.30 209.01 C 325.56 215.30 325.42 221.94 328.19 227.75 C 330.21 232.23 335.65 233.38 340.08 233.53 C 348.43 233.50 356.77 233.01 365.12 232.86 C 365.63 241.22 366.12 249.59 366.60 257.95 C 349.99 260.74 332.56 264.08 316.06 258.86 C 309.11 256.80 302.63 252.19 299.81 245.32 C 294.76 233.63 294.35 220.62 294.13 208.07 C 294.11 185.40 294.13 162.74 294.12 140.07 C 286.73 140.05 279.34 140.08 271.95 140.05 C 271.93 130.96 271.93 121.86 271.95 112.76 C 279.34 112.73 286.72 112.77 294.11 112.74 C 294.14 98.72 294.10 84.71 294.13 70.69 Z"
|
||||
id="path10"
|
||||
fill="#ffffff" />
|
||||
<path
|
||||
fill="#548235"
|
||||
d=" M 160.51 87.70 C 170.80 86.36 181.60 86.72 191.34 90.61 C 199.23 93.73 205.93 99.84 209.47 107.58 C 214.90 119.31 216.98 132.26 218.03 145.05 C 219.17 162.07 219.01 179.25 216.66 196.17 C 215.01 206.24 212.66 216.85 205.84 224.79 C 198.92 232.76 188.25 236.18 178.01 236.98 C 167.21 237.77 155.82 236.98 146.07 231.87 C 140.38 228.84 135.55 224.09 132.73 218.27 C 129.31 211.30 127.43 203.69 126.11 196.07 C 122.13 171.91 121.17 146.91 126.61 122.89 C 128.85 113.83 132.11 104.53 138.73 97.70 C 144.49 91.85 152.51 88.83 160.51 87.70 Z"
|
||||
id="path12" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
1
icons/settings.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1631608141807" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="53038" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M480 160c-41.376 0-76.64 27.008-90.016 64H128v64h262.016c13.344 36.992 48.608 64 89.984 64s76.64-27.008 90.016-64H896V224H569.984C556.64 187.008 521.408 160 480 160z m0 64c18.016 0 32 14.016 32 32 0 18.016-14.016 32-32 32-18.016 0-32-14.016-32-32 0-18.016 14.016-32 32-32z m224 192c-41.376 0-76.64 27.008-90.016 64H128v64h486.016c13.344 36.992 48.608 64 89.984 64s76.64-27.008 90.016-64H896v-64h-102.016c-13.344-36.992-48.608-64-89.984-64z m0 64c18.016 0 32 14.016 32 32 0 18.016-14.016 32-32 32-18.016 0-32-14.016-32-32 0-18.016 14.016-32 32-32z m-352 192c-41.376 0-76.64 27.008-90.016 64H128v64h134.016c13.344 36.992 48.608 64 89.984 64s76.64-27.008 90.016-64H896v-64H441.984c-13.344-36.992-48.608-64-89.984-64z m0 64c18.016 0 32 14.016 32 32 0 18.016-14.016 32-32 32-18.016 0-32-14.016-32-32 0-18.016 14.016-32 32-32z" p-id="53039" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
icons/splash.png
Normal file
After Width: | Height: | Size: 93 KiB |
1
icons/start.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1604050740174" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1304" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M288 164.992v694.016l48.992-32L827.008 512 336.96 196.992z m64 117.024L708.992 512 352 742.016z" fill="#548235" p-id="1305"></path></svg>
|
After Width: | Height: | Size: 514 B |
1
icons/vector.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621607964079" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="63529" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M160 160v224h64v256H160v224h224v-64h256v64h224v-224h-64v-256h64V160h-224v64h-256V160H160z m64 64h96v96H224V224z m480 0h96v96h-96V224zM384 288h256v96h96v256h-96v96h-256v-96H288v-256h96V288zM224 704h96v96H224v-96z m480 0h96v96h-96v-96z" p-id="63530" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 654 B |
1
icons/zoomin.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621606055767" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18281" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M608 96C431.616 96 288 239.616 288 416c0 76.64 26.88 146.88 72 202.016L104.992 872.96 151.04 919.04l254.976-255.008A317.504 317.504 0 0 0 608 736c176.384 0 320-143.616 320-320S784.384 96 608 96z m0 64c141.76 0 256 114.24 256 256s-114.24 256-256 256-256-114.24-256-256 114.24-256 256-256z m-32 128v96h-96v64h96v96h64v-96h96v-64h-96V288z" p-id="18282" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 756 B |
1
icons/zoomout.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621606083458" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18468" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M608 96C431.616 96 288 239.616 288 416c0 76.64 26.88 146.88 72 202.016L104.992 872.96 151.04 919.04l254.976-255.008A317.504 317.504 0 0 0 608 736c176.384 0 320-143.616 320-320S784.384 96 608 96z m0 64c141.76 0 256 114.24 256 256s-114.24 256-256 256-256-114.24-256-256 114.24-256 256-256z m-128 224v64h256v-64z" p-id="18469" fill="#548235"></path></svg>
|
After Width: | Height: | Size: 730 B |
1
lic/license.lic
Normal file
@ -0,0 +1 @@
|
||||
pGZJMmJtule8fwDCz4mnyHoQa7N6pl5GRdLqfoXREBqG4Xb1jbvgf7RmC8f1+sNpiCFSIt7NgvU362tKhB5UBXn/vUAadG1lOGC70dUhprGzBoqJN7VkAHkNGg0XjoE8H0SCVynr8To7ciwcnmK6HJXre6i+mBdTjACmKseTMlWp480XOt7uHysltORbTA3J
|
1
license.lic
Normal file
@ -0,0 +1 @@
|
||||
pGZJMmJtule8fwDCz4mnyHoQa7N6pl5GRdLqfoXREBqG4Xb1jbvgf7RmC8f1+sNpiCFSIt7NgvU362tKhB5UBXn/vUAadG1lOGC70dUhprGzBoqJN7VkAHkNGg0XjoE8H0SCVynr8To7ciwcnmK6HJXre6i+mBdTjACmKseTMlWp480XOt7uHysltORbTA3J
|
9
main.py
Normal file
@ -0,0 +1,9 @@
|
||||
from mul.mulstart import MulStart
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.INFO, filename='log.txt', filemode='w', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
t = MulStart()
|
||||
t.run()
|
32
mul/mulexport.py
Normal file
@ -0,0 +1,32 @@
|
||||
import multiprocessing
|
||||
from alg.txt_export_to import export_to_shp
|
||||
import os
|
||||
import glob
|
||||
from qgis.core import *
|
||||
class ExportToSHP(multiprocessing.Process):
|
||||
|
||||
def __init__(self, conn, result_path, output_dir):
|
||||
super(ExportToSHP, self).__init__()
|
||||
self.conn = conn
|
||||
self.result_path = result_path
|
||||
self.result_list = self.get_result_list()
|
||||
self.output_dir = output_dir
|
||||
os.makedirs(self.output_dir, exist_ok=True)
|
||||
|
||||
def get_result_list(self):
|
||||
fl = list(glob.glob(os.path.join(self.result_path, '*.txt')))
|
||||
return fl
|
||||
|
||||
def run(self):
|
||||
# result = []
|
||||
self.conn.send(len(self.result_list))
|
||||
qgs = QgsApplication([], False)
|
||||
QgsApplication.initQgis()
|
||||
for i, p in enumerate(self.result_list):
|
||||
o = os.path.basename(p)
|
||||
o = os.path.splitext(o)[0]
|
||||
r = export_to_shp(p, os.path.join(self.output_dir, o + '.shp'))
|
||||
# result.append([r, self.feats[i]])
|
||||
self.conn.send([i, r, o])
|
||||
# self.conn.send(result)
|
||||
|
19
mul/mulgrubcut.py
Normal file
@ -0,0 +1,19 @@
|
||||
import multiprocessing
|
||||
from alg.grubcut import grubcut
|
||||
|
||||
class GrabCut(multiprocessing.Process):
|
||||
|
||||
def __init__(self, conn, img_path, pts = [], feats = []):
|
||||
super(GrabCut, self).__init__()
|
||||
self.conn = conn
|
||||
self.pts = pts
|
||||
self.feats = feats
|
||||
self.img_path = img_path
|
||||
def run(self):
|
||||
result = []
|
||||
for i, p in enumerate(self.pts):
|
||||
r = grubcut(self.img_path, p, False, True, False)
|
||||
result.append([r, self.feats[i]])
|
||||
self.conn.send(i)
|
||||
self.conn.send(result)
|
||||
|
63
mul/mulstart.py
Normal file
@ -0,0 +1,63 @@
|
||||
import sys
|
||||
import time
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QFont, QPixmap
|
||||
from PyQt5.QtWidgets import QSplashScreen, QProgressBar, QStyleFactory, QMessageBox
|
||||
from qgis.core import QgsApplication
|
||||
# from qgis.core import
|
||||
from gui.mainwindow import MainWindow
|
||||
import multiprocessing
|
||||
from gui import license
|
||||
from utils.setting import Settings
|
||||
|
||||
class MulStart:
|
||||
|
||||
def __init__(self, **kargs) -> None:
|
||||
super(MulStart, self).__init__()
|
||||
self.kargs = kargs
|
||||
def run(self):
|
||||
|
||||
QgsApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
||||
QgsApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
|
||||
QgsApplication.setOrganizationName("西安理工大学-ImgSciGroup")
|
||||
QgsApplication.setApplicationName("Easy Change Detection")
|
||||
QgsApplication.setApplicationVersion("v0.0.1")
|
||||
QgsApplication.setFont(QFont("Segoe UI", 10))
|
||||
QgsApplication.setStyle(QStyleFactory.create("Fusion"))
|
||||
|
||||
|
||||
# pyrcc5 res.qrc -o rc.py
|
||||
import rc
|
||||
|
||||
app = QgsApplication([], True)
|
||||
QgsApplication.initQgis()
|
||||
while not Settings.General().license:
|
||||
QMessageBox.warning(None, "Warning", "Please select a license file.")
|
||||
if(license.License().exec_() == license.License.Accepted):
|
||||
continue
|
||||
else:
|
||||
sys.exit(0)
|
||||
# Create and display the splash screen
|
||||
splash_pix = QPixmap(':/icons/splash.png')
|
||||
|
||||
splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint)
|
||||
progressBar = QProgressBar(splash)
|
||||
progressBar.setMaximum(10)
|
||||
progressBar.setTextVisible(False)
|
||||
progressBar.setGeometry(46, splash_pix.height() - 60, splash_pix.width()-92, 10)
|
||||
|
||||
splash.show()
|
||||
for i in range(1, 11):
|
||||
progressBar.setValue(i)
|
||||
t = time.time()
|
||||
while time.time() < t + 0.05:
|
||||
app.processEvents()
|
||||
|
||||
ex = MainWindow(**self.kargs)
|
||||
# ex.canvas.load_image(r'data\100001678.jpg')
|
||||
# ex.canvas.load_result_from_txt(r'data\100001678.txt')
|
||||
# ex.showMaximized()
|
||||
ex.show()
|
||||
splash.finish(ex)
|
||||
sys.exit(app.exec_())
|
||||
|
26
res.qrc
Normal file
@ -0,0 +1,26 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>icons/splash.png</file>
|
||||
<file>icons/logo.svg</file>
|
||||
<file>icons/load.svg</file>
|
||||
<file>icons/settings.svg</file>
|
||||
<file>icons/exit.svg</file>
|
||||
<file>icons/vector.svg</file>
|
||||
<file>icons/zoomin.svg</file>
|
||||
<file>icons/zoomout.svg</file>
|
||||
<file>icons/pan.svg</file>
|
||||
<file>icons/full.svg</file>
|
||||
<file>icons/start.svg</file>
|
||||
<file>icons/clear.svg</file>
|
||||
<file>icons/edit.svg</file>
|
||||
<file>icons/export.svg</file>
|
||||
<file>icons/model.svg</file>
|
||||
<file>icons/assessment.svg</file>
|
||||
<file>icons/paint.svg</file>
|
||||
<file>icons/font.svg</file>
|
||||
<file>icons/qt.svg</file>
|
||||
<file>icons/ok.svg</file>
|
||||
<file>icons/cancel.svg</file>
|
||||
<file>icons/outline.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
132
test/tree.py
Normal file
@ -0,0 +1,132 @@
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtWidgets import QDesktopWidget
|
||||
import sys
|
||||
from PyQt5.QtWidgets import *
|
||||
|
||||
# import tree # tree.py文件
|
||||
|
||||
|
||||
class myTreeWidget:
|
||||
def __init__(self, objTree):
|
||||
self.myTree = objTree
|
||||
# 设置列数
|
||||
self.myTree.setColumnCount(1)
|
||||
# 设置树形控件头部的标题
|
||||
self.myTree.setHeaderLabels(['机构列表'])
|
||||
|
||||
# 设置根节点
|
||||
self.root = QTreeWidgetItem(self.myTree)
|
||||
self.root.setText(0, '本单位')
|
||||
|
||||
# 设置树形控件的列的宽度
|
||||
self.myTree.setColumnWidth(0, 100)
|
||||
|
||||
# 设置子节点1
|
||||
child1 = QTreeWidgetItem(self.root)
|
||||
child1.setText(0, '市场部')
|
||||
self.root.addChild(child1)
|
||||
|
||||
# 设置子节点11
|
||||
child11 = QTreeWidgetItem(child1)
|
||||
child11.setText(0, '销售班')
|
||||
|
||||
# 设置子节点2
|
||||
child2 = QTreeWidgetItem(self.root)
|
||||
child2.setText(0, '财务部')
|
||||
|
||||
# 设置子节点21
|
||||
child21 = QTreeWidgetItem(child2)
|
||||
child21.setText(0, '财务一班')
|
||||
|
||||
# 加载根节点的所有属性与子控件
|
||||
self.myTree.addTopLevelItem(self.root)
|
||||
|
||||
# TODO 优化2 给节点添加响应事件
|
||||
self.myTree.clicked.connect(self.onClicked)
|
||||
|
||||
# 节点全部展开
|
||||
self.myTree.expandAll()
|
||||
|
||||
def onClicked(self):
|
||||
item = self.myTree.currentItem()
|
||||
print('Key=%s' % (item.text(0)))
|
||||
|
||||
|
||||
class MyPyQTMainForm(QMainWindow):
|
||||
"""
|
||||
主界面
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化
|
||||
"""
|
||||
super(MyPyQTMainForm, self).__init__()
|
||||
# self.setupUi(self)
|
||||
# 创建树控件对象
|
||||
layout = QVBoxLayout()
|
||||
|
||||
tree_widget = QTreeWidget()
|
||||
# layout.addWidget(tree_widget)
|
||||
# self.setLayout(layout)
|
||||
self.layout().addWidget(tree_widget)
|
||||
self.myTreeTest = myTreeWidget(tree_widget)
|
||||
# self.myTreeTest = myTreeWidget(self.treeWidget)
|
||||
|
||||
def center(self):
|
||||
"""
|
||||
定义一个函数使得窗口居中显示
|
||||
"""
|
||||
# 获取屏幕坐标系
|
||||
screen = QDesktopWidget().screenGeometry()
|
||||
# 获取窗口坐标系
|
||||
size = self.geometry()
|
||||
newLeft = (screen.width() - size.width()) / 2
|
||||
newTop = (screen.height() - size.height()) / 2
|
||||
self.move(int(newLeft), int(newTop))
|
||||
|
||||
def addNode(self):
|
||||
"""
|
||||
添加节点
|
||||
"""
|
||||
print('--- addTreeNode ---')
|
||||
item = self.myTreeTest.myTree.currentItem()
|
||||
node = QTreeWidgetItem(item)
|
||||
node.setText(0, '后勤部')
|
||||
|
||||
def deleteNode(self):
|
||||
"""
|
||||
删除节点
|
||||
"""
|
||||
print('--- delTreeNode ---')
|
||||
item = self.myTreeTest.myTree.currentItem()
|
||||
root = self.myTreeTest.myTree.invisibleRootItem()
|
||||
for item in self.myTreeTest.myTree.selectedItems():
|
||||
(item.parent() or root).removeChild(item)
|
||||
|
||||
def modifyNode(self):
|
||||
"""
|
||||
修改节点
|
||||
"""
|
||||
print('--- modifyTreeNode ---')
|
||||
item = self.myTreeTest.myTree.currentItem()
|
||||
item.setText(0, '办公室')
|
||||
|
||||
|
||||
"""
|
||||
主函数
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
myPyMainForm = MyPyQTMainForm()
|
||||
# 主窗口显示在屏幕中间
|
||||
myPyMainForm.center()
|
||||
|
||||
# 禁止最大化按钮
|
||||
myPyMainForm.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowCloseButtonHint)
|
||||
# 禁止拉伸窗口大小
|
||||
myPyMainForm.setFixedSize(myPyMainForm.width(), myPyMainForm.height())
|
||||
|
||||
# 显示主界面
|
||||
myPyMainForm.show()
|
||||
sys.exit(app.exec_())
|
2
utils/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .setting import Settings
|
||||
from .license import LicenseHelper
|
143
utils/license.py
Normal file
@ -0,0 +1,143 @@
|
||||
import base64
|
||||
from Crypto.Cipher import AES
|
||||
import uuid
|
||||
import hashlib
|
||||
import datetime
|
||||
|
||||
#
|
||||
|
||||
class AESHelper(object):
|
||||
def __init__(self, password, iv):
|
||||
self.password = bytes(password, encoding='utf-8')
|
||||
self.iv = bytes(iv, encoding='utf-8')
|
||||
|
||||
def pkcs7padding(self, text):
|
||||
"""
|
||||
明文使用PKCS7填充
|
||||
最终调用AES加密方法时,传入的是一个byte数组,要求是16的整数倍,因此需要对明文进行处理
|
||||
:param text: 待加密内容(明文)
|
||||
:return:
|
||||
"""
|
||||
bs = AES.block_size # 16
|
||||
length = len(text)
|
||||
bytes_length = len(bytes(text, encoding='utf-8'))
|
||||
# tips:utf-8编码时,英文占1个byte,而中文占3个byte
|
||||
padding_size = length if(bytes_length == length) else bytes_length
|
||||
padding = bs - padding_size % bs
|
||||
# tips:chr(padding)看与其它语言的约定,有的会使用'\0'
|
||||
padding_text = chr(padding) * padding
|
||||
return text + padding_text
|
||||
|
||||
def pkcs7unpadding(self, text):
|
||||
"""
|
||||
处理使用PKCS7填充过的数据
|
||||
:param text: 解密后的字符串
|
||||
:return:
|
||||
"""
|
||||
length = len(text)
|
||||
unpadding = ord(text[length-1])
|
||||
return text[0:length-unpadding]
|
||||
|
||||
def encrypt(self, content):
|
||||
"""
|
||||
AES加密
|
||||
模式cbc
|
||||
填充pkcs7
|
||||
:param key: 密钥
|
||||
:param content: 加密内容
|
||||
:return:
|
||||
"""
|
||||
cipher = AES.new(self.password, AES.MODE_CBC, self.iv)
|
||||
content_padding = self.pkcs7padding(content)
|
||||
encrypt_bytes = cipher.encrypt(bytes(content_padding, encoding='utf-8'))
|
||||
result = str(base64.b64encode(encrypt_bytes), encoding='utf-8')
|
||||
return result
|
||||
|
||||
def decrypt(self, content):
|
||||
"""
|
||||
AES解密
|
||||
模式cbc
|
||||
去填充pkcs7
|
||||
:param key:
|
||||
:param content:
|
||||
:return:
|
||||
"""
|
||||
cipher = AES.new(self.password, AES.MODE_CBC, self.iv)
|
||||
encrypt_bytes = base64.b64decode(content)
|
||||
decrypt_bytes = cipher.decrypt(encrypt_bytes)
|
||||
result = str(decrypt_bytes, encoding='utf-8')
|
||||
result = self.pkcs7unpadding(result)
|
||||
return result
|
||||
|
||||
def get_aes():
|
||||
# AES_SECRET和AES_IV分别为密钥和偏移量
|
||||
aes_helper = AESHelper(
|
||||
'ao234esorGFSFGubh#$^&@gihdfjl$@4',
|
||||
'dergbdzbfdsdrt$g')
|
||||
return aes_helper
|
||||
|
||||
class LicenseHelper(object):
|
||||
def generate_license(self, end_date, mac_addr):
|
||||
print("Received end_date: {}, mac_addr: {}".format(end_date, mac_addr))
|
||||
psw = self.hash_msg('smartant' + str(mac_addr))
|
||||
license_str = {}
|
||||
license_str['mac'] = mac_addr
|
||||
license_str['time_str'] = end_date
|
||||
license_str['psw'] = psw
|
||||
s = str(license_str)
|
||||
licence_result = get_aes().encrypt(s)
|
||||
return licence_result
|
||||
|
||||
def get_mac_address(self):
|
||||
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
|
||||
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
|
||||
|
||||
def hash_msg(self, msg):
|
||||
sha256 = hashlib.sha256()
|
||||
sha256.update(msg.encode('utf-8'))
|
||||
res = sha256.hexdigest()
|
||||
return res
|
||||
|
||||
def read_license(self, license_result):
|
||||
lic_msg = bytes(license_result, encoding="utf8")
|
||||
license_str = get_aes().decrypt(lic_msg)
|
||||
license_dic = eval(license_str)
|
||||
return license_dic
|
||||
|
||||
def check_license_date(self, lic_date):
|
||||
current_time = datetime.datetime.strftime(datetime.datetime.now() ,"%Y-%m-%d %H:%M:%S")
|
||||
current_time_array = datetime.datetime.strptime(current_time,"%Y-%m-%d %H:%M:%S")
|
||||
lic_date_array = datetime.datetime.strptime(lic_date, "%Y-%m-%d %H:%M:%S")
|
||||
remain_days = lic_date_array - current_time_array
|
||||
remain_days = remain_days.days
|
||||
print('lic data:{}'.format(lic_date))
|
||||
print('remain_days: {}'.format(remain_days))
|
||||
if remain_days < 0 or remain_days == 0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def check_license_psw(self, psw):
|
||||
mac_addr = self.get_mac_address()
|
||||
hashed_msg = self.hash_msg('smartant' + str(mac_addr))
|
||||
if psw == hashed_msg:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
lic = LicenseHelper().generate_license('2022-12-31 00:00:00', LicenseHelper().get_mac_address())
|
||||
|
||||
with open('license.lic', 'w') as f:
|
||||
f.write(lic[::-1])
|
||||
|
||||
with open('license.lic', 'r') as f:
|
||||
license_result = f.read()[::-1]
|
||||
|
||||
license_dic = LicenseHelper().read_license(license_result)
|
||||
|
||||
print(license_dic)
|
||||
|
||||
|
11
utils/menu.py
Normal file
@ -0,0 +1,11 @@
|
||||
from functools import wraps
|
||||
|
||||
def as_menu(D, name, icon=None, shortcut=None, tip=None, checkable=False, signal=None, callback=None, enabled=True):
|
||||
|
||||
def func(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
return f(*args, **kwargs)
|
||||
D.addMenu(name, icon, shortcut, tip, checkable, signal, callback, enabled, wrapper)
|
||||
return f
|
||||
return func
|
121
utils/project.py
Normal file
@ -0,0 +1,121 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from osgeo import gdal, gdal_array
|
||||
from utils.setting import Settings
|
||||
from qgis.core import QgsRasterLayer
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
|
||||
def singleton(cls):
|
||||
_instance = {}
|
||||
|
||||
def inner(*args, **kargs):
|
||||
if cls not in _instance:
|
||||
_instance[cls] = cls(*args, **kargs)
|
||||
return _instance[cls]
|
||||
return inner
|
||||
|
||||
@singleton
|
||||
class Project(QObject):
|
||||
|
||||
project_init = pyqtSignal(bool)
|
||||
|
||||
def __init__(self,
|
||||
parent=None):
|
||||
super().__init__(parent)
|
||||
self.is_init = False
|
||||
self.cell_size = Settings.Project().cell_size
|
||||
self.max_memory = Settings.Project().max_memory
|
||||
self.max_threads = Settings.Project().max_threads
|
||||
self.root = Settings.General().root
|
||||
|
||||
def connect(self, pair_canvas,
|
||||
layer_tree,
|
||||
message_box,
|
||||
result_table):
|
||||
self.pair_canvas = pair_canvas
|
||||
self.layer_tree = layer_tree
|
||||
self.message_box = message_box
|
||||
self.result_table = result_table
|
||||
|
||||
def setup(self, file=None):
|
||||
self.is_init = True
|
||||
|
||||
if file is None:
|
||||
self.file = Path(self.root)/'project'/'untitled.cdp'
|
||||
dir_name = os.path.dirname(self.file)
|
||||
if not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name, exist_ok=True)
|
||||
if not self.file.exists():
|
||||
f = self.file.open('w')
|
||||
f.close()
|
||||
else:
|
||||
self.load()
|
||||
# self.project_created.emit()
|
||||
self.project_init.emit(True)
|
||||
|
||||
def save(self):
|
||||
pass
|
||||
|
||||
def clear(self):
|
||||
'''
|
||||
clear all layers
|
||||
'''
|
||||
self.layer_tree.clear()
|
||||
self.pair_canvas.clear()
|
||||
self.message_box.clear()
|
||||
self.result_table.clear()
|
||||
|
||||
def load(self):
|
||||
pass
|
||||
|
||||
def add_layer(self, pth1, pth2):
|
||||
player = PairLayer(pth1, pth2)
|
||||
if player.check():
|
||||
self.layer_tree.add_layer(player)
|
||||
else:
|
||||
self.message_box.show_message(player.msg)
|
||||
|
||||
class VectorLayer:
|
||||
pass
|
||||
|
||||
class GridLayer:
|
||||
pass
|
||||
|
||||
class PairLayer:
|
||||
|
||||
def __init__(self, pth1, pth2) -> None:
|
||||
self.pth1 = pth1
|
||||
self.pth2 = pth2
|
||||
|
||||
self.l1_name = os.path.basename(pth1)
|
||||
self.l2_name = os.path.basename(pth2)
|
||||
|
||||
self.grid_layer = GridLayer()
|
||||
|
||||
self.msg = ''
|
||||
|
||||
def check(self):
|
||||
if not os.path.exists(self.pth1):
|
||||
self.msg = '图层1不存在'
|
||||
return False
|
||||
if not os.path.exists(self.pth2):
|
||||
self.msg = '图层2不存在'
|
||||
return False
|
||||
|
||||
ds1 = gdal.Open(self.pth1)
|
||||
ds2 = gdal.Open(self.pth2)
|
||||
if ds1 is None or ds2 is None:
|
||||
self.msg = '图层打开失败'
|
||||
return False
|
||||
|
||||
if ds1.RasterXSize != ds2.RasterXSize or ds1.RasterYSize != ds2.RasterYSize:
|
||||
self.msg = '图层尺寸不一致'
|
||||
return False
|
||||
|
||||
del ds1
|
||||
del ds2
|
||||
|
||||
self.l1 = QgsRasterLayer(self.pth1, self.l1_name)
|
||||
self.l2 = QgsRasterLayer(self.pth2, self.l2_name)
|
||||
|
||||
return True
|
109
utils/setting.py
Normal file
@ -0,0 +1,109 @@
|
||||
from datetime import datetime
|
||||
import os
|
||||
from typing import Tuple
|
||||
from PyQt5.QtCore import QSettings
|
||||
from utils.license import LicenseHelper
|
||||
|
||||
class Settings(QSettings):
|
||||
|
||||
def __init__(self, key):
|
||||
super().__init__()
|
||||
self.key = key
|
||||
def __enter__(self):
|
||||
self.beginGroup(self.key)
|
||||
return self
|
||||
|
||||
def __exit__(self, *args, **kargs):
|
||||
self.endGroup()
|
||||
|
||||
class Project:
|
||||
|
||||
PRE= 'project'
|
||||
|
||||
@property
|
||||
def cell_size(self) -> Tuple[int, int]:
|
||||
with Settings(self.PRE) as s:
|
||||
return s.value('cell_size', (100, 100))
|
||||
|
||||
@cell_size.setter
|
||||
def cell_size(self, value:Tuple[int, int]):
|
||||
with Settings(self.PRE) as s:
|
||||
s.setValue('cell_size', value)
|
||||
|
||||
@property
|
||||
def max_memory(self):
|
||||
with Settings(self.PRE) as s:
|
||||
return s.value('max_memory', 100)
|
||||
|
||||
@max_memory.setter
|
||||
def max_memory(self, value):
|
||||
with Settings(self.PRE) as s:
|
||||
s.setValue('max_memory', value)
|
||||
|
||||
@property
|
||||
def max_threads(self):
|
||||
with Settings(self.PRE) as s:
|
||||
return s.value('max_threads', 4)
|
||||
|
||||
@max_threads.setter
|
||||
def max_threads(self, value):
|
||||
with Settings(self.PRE) as s:
|
||||
s.setValue('max_threads', value)
|
||||
|
||||
class General:
|
||||
|
||||
PRE='general'
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
with Settings(Settings.General.PRE) as s:
|
||||
return s.value('size', (800, 600))
|
||||
|
||||
@size.setter
|
||||
def size(self, value):
|
||||
with Settings(Settings.General.PRE) as s:
|
||||
s.setValue('size', value)
|
||||
|
||||
@property
|
||||
def end_date(self):
|
||||
if not os.path.exists('lic/license.lic'):
|
||||
return datetime.now()
|
||||
|
||||
with open('lic/license.lic', 'r') as f:
|
||||
lic = f.read()[::-1]
|
||||
|
||||
lic_helper = LicenseHelper()
|
||||
try:
|
||||
lic_dic = lic_helper.read_license(lic)
|
||||
|
||||
if lic_helper.check_license_date(lic_dic['time_str']) and lic_helper.check_license_psw(lic_dic['psw']):
|
||||
return lic_dic['time_str']
|
||||
else:
|
||||
return datetime.now()
|
||||
except:
|
||||
return datetime.now()
|
||||
|
||||
@property
|
||||
def license(self):
|
||||
if not os.path.exists('lic/license.lic'):
|
||||
return False
|
||||
|
||||
with open('lic/license.lic', 'r') as f:
|
||||
lic = f.read()[::-1]
|
||||
|
||||
lic_helper = LicenseHelper()
|
||||
try:
|
||||
lic_dic = lic_helper.read_license(lic)
|
||||
|
||||
if lic_helper.check_license_date(lic_dic['time_str']) and lic_helper.check_license_psw(lic_dic['psw']):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
@property
|
||||
def root(self):
|
||||
with Settings(Settings.General.PRE) as s:
|
||||
return s.value('root', './')
|
||||
|