change build

This commit is contained in:
copper 2022-05-10 14:28:48 +08:00
parent df27d40587
commit dbfe278b66
41 changed files with 9740 additions and 252 deletions

8
build.bat Normal file
View File

@ -0,0 +1,8 @@
nuitka run.py --standalone --plugin-enable=qt-plugins --plugin-enable=numpy --show-progress --include-package=qgis --plugin-enable=pylint-warnings --output-dir=package --windows-disable-console --windows-icon-from-ico=logo.ico --no-pyi-file
REM Win7 with console
REM nuitka gui.py --mingw64 --standalone --plugin-enable=qt-plugins --plugin-enable=numpy --recurse-all --show-progress --include-package=qgis --output-dir=package --windows-icon=icons/logo.ico
REM Win7
@REM nuitka gui.py --mingw64 --standalone --plugin-enable=qt-plugins --plugin-enable=numpy --recurse-all --show-progress --include-package=qgis --output-dir=package --windows-disable-console --windows-icon=icons/logo.ico

View File

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 622 B

After

Width:  |  Height:  |  Size: 622 B

View File

Before

Width:  |  Height:  |  Size: 606 B

After

Width:  |  Height:  |  Size: 606 B

View File

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 508 B

View File

Before

Width:  |  Height:  |  Size: 632 B

After

Width:  |  Height:  |  Size: 632 B

View File

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

View File

Before

Width:  |  Height:  |  Size: 642 B

After

Width:  |  Height:  |  Size: 642 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 699 B

After

Width:  |  Height:  |  Size: 699 B

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

Before

Width:  |  Height:  |  Size: 514 B

After

Width:  |  Height:  |  Size: 514 B

View File

Before

Width:  |  Height:  |  Size: 654 B

After

Width:  |  Height:  |  Size: 654 B

View File

Before

Width:  |  Height:  |  Size: 756 B

After

Width:  |  Height:  |  Size: 756 B

View File

Before

Width:  |  Height:  |  Size: 730 B

After

Width:  |  Height:  |  Size: 730 B

BIN
logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1 @@
from basic_change.main import *

9112
plugins/basic_change/main.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,148 @@
import math
import os
from rscder.plugins.basic import BasicPlugin
from PyQt5.QtWidgets import QAction
from PyQt5.QtCore import pyqtSignal
from rscder.utils.project import PairLayer
from osgeo import gdal, gdal_array
from threading import Thread
import numpy as np
class BasicMethod(BasicPlugin):
message_send = pyqtSignal(str)
@staticmethod
def info():
return {
'name': 'BasicMethod',
'description': 'BasicMethod',
'author': 'RSCDER',
'version': '1.0.0',
}
def set_action(self):
menubar = self.ctx['menubar']
basic_change_detection_menu = self.ctx['basic_change_detection_menu']
basic_diff_method = QAction('差分法')
basic_change_detection_menu.addAction(basic_diff_method)
basic_diff_method.setEnabled(False)
self.basic_diff_method = basic_diff_method
self.message_send.connect(self.send_message)
self.gap = 128
def on_data_load(self, layer_id):
self.basic_diff_method.setEnabled(True)
def send_message(self, s):
self.message_box.info(s)
def run_basic_diff_alg(self, pth1, pth2, cell_size, out):
self.message_send.emit('开始计算差分法')
ds1 = gdal.Open(pth1)
ds2 = gdal.Open(pth2)
xsize = ds1.RasterXSize
ysize = ds1.RasterYSize
band = ds1.RasterCount
yblocks = ysize // cell_size[1]
geo = ds1.GetGeoTransform()
driver = gdal.GetDriverByName('GTiff')
out_tif = os.path.join(out, 'temp.tif')
out_ds = driver.Create(out_tif, xsize, ysize, 1, gdal.GDT_Float32)
out_ds.SetGeoTransform(ds1.GetGeoTransform())
out_ds.SetProjection(ds1.GetProjection())
max_diff = 0
min_diff = math.inf
for j in range(yblocks):
self.message_send.emit(f'计算{j}/{yblocks}')
block_xy = (0, j * cell_size[1])
block_size = (xsize, cell_size[1])
if block_xy[1] + block_size[1] > ysize:
block_size = (xsize, ysize - block_xy[1])
block_data1 = ds1.ReadAsArray(*block_xy, *block_size)
block_data2 = ds2.ReadAsArray(*block_xy, *block_size)
if band == 1:
block_data1 = block_data1[None, ...]
block_data2 = block_data2[None, ...]
block_diff = block_data1 - block_data2
block_diff = block_diff.astype(np.float32)
block_diff = block_diff.abs().sum(0)
min_diff = min(min_diff, block_diff.min())
max_diff = max(max_diff, block_diff.max())
out_ds.GetRasterBand(1).WriteArray(block_diff, *block_xy)
self.message_send.emit(f'完成{j}/{yblocks}')
out_ds.FlushCache()
out_ds = None
self.message_send.emit('归一化概率中...')
temp_in_ds = gdal.Open(out_tif)
out_normal_tif = os.path.join(out, 'diff_0_255.tif')
out_normal_ds = driver.Create(out_normal_tif, xsize, ysize, 1, gdal.GDT_Byte)
for j in range(yblocks):
block_xy = (0, j * cell_size[1])
block_size = (xsize, cell_size[1])
if block_xy[1] + block_size[1] > ysize:
block_size = (xsize, ysize - block_xy[1])
block_data = temp_in_ds.ReadAsArray(*block_xy, *block_size)
block_data = (block_data - min_diff) / (max_diff - min_diff) * 255
block_data = block_data.astype(np.uint8)
out_normal_ds.GetRasterBand(1).WriteArray(block_data, *block_xy)
out_normal_ds.FlushCache()
out_normal_ds = None
self.message_send.emit('完成归一化概率')
self.message_send.emit('计算变化表格中...')
out_csv = os.path.join(out, 'diff_table.csv')
xblocks = xsize // cell_size[0]
with open(out_csv, 'w') as f:
f.write('x,y,diff,status\n')
for j in range(yblocks):
block_xy = (0, j * cell_size[1])
block_size = (xsize, cell_size[1])
if block_xy[1] + block_size[1] > ysize:
block_size = (xsize, ysize - block_xy[1])
block_data = temp_in_ds.ReadAsArray(*block_xy, *block_size)
for i in range(xblocks):
start_x = i * cell_size[0]
end_x = start_x + cell_size[0]
if end_x > xsize:
end_x = xsize
block_data_xy = block_data[:, start_x:end_x]
if block_data_xy.mean() > self.gap:
center_x = start_x + cell_size[0] // 2
center_y = j * cell_size[1] + cell_size[1] // 2
center_x = center_x * geo[1] + geo [0]
center_y = center_y * geo[5] + geo [3]
f.write(f'{center_x},{center_y},{block_data_xy.mean()},1\n')
self.message_send.emit('完成计算变化表格')
self.message_send.emit('差分法计算完成')
def basic_diff_alg(self):
# layer_select =
layer:PairLayer = self.project.layers.values()[0]
img1 = layer.pth2
img2 = layer.pth2
if not layer.check():
return
t = Thread(target=self.run_basic_diff_alg, args=(img1, img2, layer.pth1))
t.start()

247
pyinstaller.help Normal file
View File

@ -0,0 +1,247 @@
usage: pyinstaller [-h] [-v] [-D] [-F] [--specpath DIR] [-n NAME]
[--add-data <SRC;DEST or SRC:DEST>]
[--add-binary <SRC;DEST or SRC:DEST>] [-p DIR]
[--hidden-import MODULENAME]
[--collect-submodules MODULENAME]
[--collect-data MODULENAME] [--collect-binaries MODULENAME]
[--collect-all MODULENAME] [--copy-metadata PACKAGENAME]
[--recursive-copy-metadata PACKAGENAME]
[--additional-hooks-dir HOOKSPATH]
[--runtime-hook RUNTIME_HOOKS] [--exclude-module EXCLUDES]
[--key KEY] [--splash IMAGE_FILE]
[-d {all,imports,bootloader,noarchive}] [-s] [--noupx]
[--upx-exclude FILE] [-c] [-w]
[-i <FILE.ico or FILE.exe,ID or FILE.icns or "NONE">]
[--disable-windowed-traceback] [--version-file FILE]
[-m <FILE or XML>] [-r RESOURCE] [--uac-admin]
[--uac-uiaccess] [--win-private-assemblies]
[--win-no-prefer-redirects]
[--osx-bundle-identifier BUNDLE_IDENTIFIER]
[--target-architecture ARCH] [--codesign-identity IDENTITY]
[--osx-entitlements-file FILENAME] [--runtime-tmpdir PATH]
[--bootloader-ignore-signals] [--distpath DIR]
[--workpath WORKPATH] [-y] [--upx-dir UPX_DIR] [-a]
[--clean] [--log-level LEVEL]
scriptname [scriptname ...]
positional arguments:
scriptname name of scriptfiles to be processed or exactly one
.spec-file. If a .spec-file is specified, most options
are unnecessary and are ignored.
optional arguments:
-h, --help show this help message and exit
-v, --version Show program version info and exit.
--distpath DIR Where to put the bundled app (default: .\dist)
--workpath WORKPATH Where to put all the temporary work files, .log, .pyz
and etc. (default: .\build)
-y, --noconfirm Replace output directory (default:
SPECPATH\dist\SPECNAME) without asking for
confirmation
--upx-dir UPX_DIR Path to UPX utility (default: search the execution
path)
-a, --ascii Do not include unicode encoding support (default:
included if available)
--clean Clean PyInstaller cache and remove temporary files
before building.
--log-level LEVEL Amount of detail in build-time console messages. LEVEL
may be one of TRACE, DEBUG, INFO, WARN, ERROR,
CRITICAL (default: INFO).
What to generate:
-D, --onedir Create a one-folder bundle containing an executable
(default)
-F, --onefile Create a one-file bundled executable.
--specpath DIR Folder to store the generated spec file (default:
current directory)
-n NAME, --name NAME Name to assign to the bundled app and spec file
(default: first script's basename)
What to bundle, where to search:
--add-data <SRC;DEST or SRC:DEST>
Additional non-binary files or folders to be added to
the executable. The path separator is platform
specific, ``os.pathsep`` (which is ``;`` on Windows
and ``:`` on most unix systems) is used. This option
can be used multiple times.
--add-binary <SRC;DEST or SRC:DEST>
Additional binary files to be added to the executable.
See the ``--add-data`` option for more details. This
option can be used multiple times.
-p DIR, --paths DIR A path to search for imports (like using PYTHONPATH).
Multiple paths are allowed, separated by ``';'``, or
use this option multiple times. Equivalent to
supplying the ``pathex`` argument in the spec file.
--hidden-import MODULENAME, --hiddenimport MODULENAME
Name an import not visible in the code of the
script(s). This option can be used multiple times.
--collect-submodules MODULENAME
Collect all submodules from the specified package or
module. This option can be used multiple times.
--collect-data MODULENAME, --collect-datas MODULENAME
Collect all data from the specified package or module.
This option can be used multiple times.
--collect-binaries MODULENAME
Collect all binaries from the specified package or
module. This option can be used multiple times.
--collect-all MODULENAME
Collect all submodules, data files, and binaries from
the specified package or module. This option can be
used multiple times.
--copy-metadata PACKAGENAME
Copy metadata for the specified package. This option
can be used multiple times.
--recursive-copy-metadata PACKAGENAME
Copy metadata for the specified package and all its
dependencies. This option can be used multiple times.
--additional-hooks-dir HOOKSPATH
An additional path to search for hooks. This option
can be used multiple times.
--runtime-hook RUNTIME_HOOKS
Path to a custom runtime hook file. A runtime hook is
code that is bundled with the executable and is
executed before any other code or module to set up
special features of the runtime environment. This
option can be used multiple times.
--exclude-module EXCLUDES
Optional module or package (the Python name, not the
path name) that will be ignored (as though it was not
found). This option can be used multiple times.
--key KEY The key used to encrypt Python bytecode.
--splash IMAGE_FILE (EXPERIMENTAL) Add an splash screen with the image
IMAGE_FILE to the application. The splash screen can
show progress updates while unpacking.
How to generate:
-d {all,imports,bootloader,noarchive}, --debug {all,imports,bootloader,noarchive}
Provide assistance with debugging a frozen
application. This argument may be provided multiple
times to select several of the following options.
- all: All three of the following options.
- imports: specify the -v option to the underlying
Python interpreter, causing it to print a message
each time a module is initialized, showing the
place (filename or built-in module) from which it
is loaded. See
https://docs.python.org/3/using/cmdline.html#id4.
- bootloader: tell the bootloader to issue progress
messages while initializing and starting the
bundled app. Used to diagnose problems with
missing imports.
- noarchive: instead of storing all frozen Python
source files as an archive inside the resulting
executable, store them as files in the resulting
output directory.
-s, --strip Apply a symbol-table strip to the executable and
shared libs (not recommended for Windows)
--noupx Do not use UPX even if it is available (works
differently between Windows and *nix)
--upx-exclude FILE Prevent a binary from being compressed when using upx.
This is typically used if upx corrupts certain
binaries during compression. FILE is the filename of
the binary without path. This option can be used
multiple times.
Windows and Mac OS X specific options:
-c, --console, --nowindowed
Open a console window for standard i/o (default). On
Windows this option will have no effect if the first
script is a '.pyw' file.
-w, --windowed, --noconsole
Windows and Mac OS X: do not provide a console window
for standard i/o. On Mac OS X this also triggers
building an OS X .app bundle. On Windows this option
will be set if the first script is a '.pyw' file. This
option is ignored in *NIX systems.
-i <FILE.ico or FILE.exe,ID or FILE.icns or "NONE">, --icon <FILE.ico or FILE.exe,ID or FILE.icns or "NONE">
FILE.ico: apply that icon to a Windows executable.
FILE.exe,ID, extract the icon with ID from an exe.
FILE.icns: apply the icon to the .app bundle on Mac OS
X. Use "NONE" to not apply any icon, thereby making
the OS to show some default (default: apply
PyInstaller's icon)
--disable-windowed-traceback
Disable traceback dump of unhandled exception in
windowed (noconsole) mode (Windows and macOS only),
and instead display a message that this feature is
disabled.
Windows specific options:
--version-file FILE add a version resource from FILE to the exe
-m <FILE or XML>, --manifest <FILE or XML>
add manifest FILE or XML to the exe
-r RESOURCE, --resource RESOURCE
Add or update a resource to a Windows executable. The
RESOURCE is one to four items,
FILE[,TYPE[,NAME[,LANGUAGE]]]. FILE can be a data file
or an exe/dll. For data files, at least TYPE and NAME
must be specified. LANGUAGE defaults to 0 or may be
specified as wildcard * to update all resources of the
given TYPE and NAME. For exe/dll files, all resources
from FILE will be added/updated to the final
executable if TYPE, NAME and LANGUAGE are omitted or
specified as wildcard *.This option can be used
multiple times.
--uac-admin Using this option creates a Manifest which will
request elevation upon application restart.
--uac-uiaccess Using this option allows an elevated application to
work with Remote Desktop.
Windows Side-by-side Assembly searching options (advanced):
--win-private-assemblies
Any Shared Assemblies bundled into the application
will be changed into Private Assemblies. This means
the exact versions of these assemblies will always be
used, and any newer versions installed on user
machines at the system level will be ignored.
--win-no-prefer-redirects
While searching for Shared or Private Assemblies to
bundle into the application, PyInstaller will prefer
not to follow policies that redirect to newer
versions, and will try to bundle the exact versions of
the assembly.
Mac OS X specific options:
--osx-bundle-identifier BUNDLE_IDENTIFIER
Mac OS X .app bundle identifier is used as the default
unique program name for code signing purposes. The
usual form is a hierarchical name in reverse DNS
notation. For example:
com.mycompany.department.appname (default: first
script's basename)
--target-architecture ARCH, --target-arch ARCH
Target architecture (macOS only; valid values: x86_64,
arm64, universal2). Enables switching between
universal2 and single-arch version of frozen
application (provided python installation supports the
target architecture). If not target architecture is
not specified, the current running architecture is
targeted.
--codesign-identity IDENTITY
Code signing identity (macOS only). Use the provided
identity to sign collected binaries and generated
executable. If signing identity is not provided, ad-
hoc signing is performed instead.
--osx-entitlements-file FILENAME
Entitlements file to use when code-signing the
collected binaries (macOS only).
Rarely used special options:
--runtime-tmpdir PATH
Where to extract libraries and support files in
`onefile`-mode. If this option is given, the
bootloader will ignore any temp-folder location
defined by the run-time OS. The ``_MEIxxxxxx``-folder
will be created here. Please use this option only if
you know what you are doing.
--bootloader-ignore-signals
Tell the bootloader to ignore signals rather than
forwarding them to the child process. Useful in
situations where e.g. a supervisor process signals
both the bootloader and child (e.g. via a process
group) to avoid signalling the child twice.

View File

@ -4,9 +4,18 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QAction, QActionGroup, QLabel, QFileDialog
from rscder.gui.project import Create
from rscder.utils.project import Project
from rscder.utils.misc import singleton
from rscder.gui.plugins import PluginDialog
from rscder.utils.setting import Settings
def get_action_manager() -> 'ActionManager':
return ActionManager()
@singleton
class ActionManager(QtCore.QObject):
instance = None
def __init__(self,
double_map,
layer_tree,
@ -137,147 +146,9 @@ class ActionManager(QtCore.QObject):
plugin_list.triggered.connect(self.plugin_list)
self.plugin_menu.addAction(plugin_list)
# 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')
# filter_action_group = self.get_action_group('filter')
# filter_menu = self.basic_menu.addMenu('&滤波处理')
# for action in filter_action_group.actions():
# filter_menu.addAction(action)
# rgb2rgb = self.add_action(QAction('&光学-影像', self.w_parent), 'align')
# sar2sar = self.add_action(QAction('&SAR-影像', self.w_parent), 'align')
# multi_source = self.add_action(QAction('&多源影像', self.w_parent), 'align')
# align_menu = self.basic_menu.addMenu('&图像配准')
# align_menu.addAction(rgb2rgb)
# align_menu.addAction(sar2sar)
# align_menu.addAction(multi_source)
# cloud_menu = self.basic_menu.addMenu('&去云处理')
# defogging_menu = self.basic_menu.addMenu('&去雾处理')
# # self.preop_menu.addActionGroup(filter_action_group)
# # self.basic_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.postop_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
@ -299,6 +170,11 @@ class ActionManager(QtCore.QObject):
self.double_map.corr_changed.connect(corr_widget.setText)
self.double_map.scale_changed.connect(scale_widget.setText)
lic_end_date = QLabel(self.status_bar)
lic_end_date.setFixedWidth(200)
lic_end_date.setText('有效期至:%s' % (Settings.General().end_date))
self.status_bar.addPermanentWidget(lic_end_date)
def plugin_list(self):
dialog = PluginDialog(self.w_parent)
dialog.show()
@ -365,7 +241,7 @@ class ActionManager(QtCore.QObject):
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):
@ -375,10 +251,10 @@ class ActionManager(QtCore.QObject):
if group_name is None:
return None
else:
if group_name not in self.action_group_actions:
if group_name not in self.action_groups:
return None
else:
group = self.action_group_actions[group_name]
group = self.action_groups[group_name]
for action in group.actions():
if action.text() == action_name:
return action
@ -387,11 +263,5 @@ class ActionManager(QtCore.QObject):
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

View File

@ -3,6 +3,7 @@ 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 rscder.gui.actions import get_action_manager
from rscder.utils.project import PairLayer, Project
@ -10,8 +11,9 @@ class LayerTree(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.tree_view = QTreeView(self)
# 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)
@ -38,6 +40,13 @@ class LayerTree(QtWidgets.QWidget):
def onClicked(self,index):
print(index.row())
item = self.tree.currentItem()
if item == self.root:
return
layer_id = str(item.data(0, Qt.UserRole))
layer = Project().layers[layer_id]
print(layer.l1_name)
print(layer.l2_name)
def add_layer(self, layer:str):
layer:PairLayer = Project().layers[layer]
@ -61,10 +70,17 @@ class LayerTree(QtWidgets.QWidget):
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)
item = self.tree.currentItem()
action_manager = get_action_manager()
actions = []
if item == self.root:
data_load_action = action_manager.get_action('&数据加载', 'File')
actions.append(data_load_action)
else:
pass
for action in actions:
rightMenu.addAction(action)
rightMenu.exec_(self.mapToGlobal(position))

View File

@ -69,6 +69,8 @@ class License(QtWidgets.QDialog):
if not os.path.exists(pth):
QtWidgets.QMessageBox.warning(self, "Warning", "The selected file does not exist.")
else:
if not os.path.exists('lic'):
os.mkdir('lic')
shutil.copy(pth, os.path.join("lic", "license.lic"))
self.accept()
self.close()

View File

@ -1,5 +1,5 @@
import pdb
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QToolBox
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QIcon
from PyQt5 import QtGui
@ -63,11 +63,10 @@ class MainWindow(QMainWindow):
self.toolbar = self.addToolBar('Toolbar')
self.toolbar.setMovable(False)
self.toolbar.setFloatable(False)
self.toolbar.setIconSize(QSize(16, 16))
self.toolbar.setIconSize(QSize(32, 32))
self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
self.toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
self.toolbar.setLayoutDirection(Qt.LeftToRight)
pass
def set_pannels(self):
@ -82,8 +81,17 @@ class MainWindow(QMainWindow):
central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas)
self.double_map.setContextMenuPolicy(Qt.CustomContextMenu)
self.layer_tree = LayerTree(self)
left_tool_box = QToolBox(self)
self.follow_box = QWidget(self)
self.eye_box = QWidget(self)
left_tool_box.setContextMenuPolicy(Qt.CustomContextMenu)
left_tool_box.addItem(self.layer_tree, self.tr("图层树"))
left_tool_box.addItem(self.follow_box, self.tr("流程"))
left_tool_box.addItem(self.eye_box, self.tr("鹰眼"))
# self.layer_tree.setContextMenuPolicy(Qt.CustomContextMenu)
def set_docker_fixed(docker):
@ -91,15 +99,11 @@ class MainWindow(QMainWindow):
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 = QtAds.CDockWidget(self.tr("面板"), self)
self.layer_tree_dock.setWidget(self.layer_tree)
self.layer_tree_dock.setWidget(left_tool_box)
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)
@ -109,11 +113,9 @@ class MainWindow(QMainWindow):
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)

View File

@ -60,10 +60,17 @@ class PluginDialog(QDialog):
print(info)
if info is not None:
try:
dst = PluginLoader.copy_plugin_to_3rd(plugin_directory)
except:
QMessageBox.warning(self, 'Warning', 'Failed to copy plugin to 3rd party directory')
return
info['module'] = os.path.basename(plugin_directory)
info['enabled'] = True
info['path'] = dst
self.has_change = True
self.plugins.append(info)
self.plugin_table.insertRow(self.plugin_table.rowCount())
name_item = QTableWidgetItem(info['name'])
module_item = QTableWidgetItem(info['module'])
@ -72,11 +79,7 @@ class PluginDialog(QDialog):
self.plugin_table.setItem(self.plugin_table.rowCount() - 1, 0, name_item)
self.plugin_table.setItem(self.plugin_table.rowCount() - 1, 1, module_item)
self.plugin_table.setItem(self.plugin_table.rowCount() - 1, 2, enabled_item)
dst = PluginLoader.copy_plugin_to_3rd(plugin_directory)
if dst is not None:
self.plugins[-1]['path'] = dst
self.plugins.append(info)
else:
pass

View File

@ -2,6 +2,7 @@ from PyQt5.QtWidgets import QDialog, QFileDialog, QLineEdit, QPushButton, QVBoxL
from PyQt5.QtGui import QIcon, QIntValidator
from PyQt5.QtCore import Qt
from rscder.utils.setting import Settings
class Create(QDialog):
def __init__(self, parent=None) -> None:

View File

@ -1,44 +1,72 @@
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt,QModelIndex
from PyQt5.QtCore import Qt,QModelIndex, pyqtSignal
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import (QTreeView, QTreeWidgetItem, QAbstractItemView, QHeaderView, QStyleFactory)
from PyQt5.QtWidgets import (QTableWidgetItem, QTableWidget, QAbstractItemView, QHeaderView, QStyleFactory)
from rscder.utils.project import PairLayer
from rscder.utils.project import PairLayer, ResultLayer
class ResultTable(QtWidgets.QWidget):
on_item_click = pyqtSignal(dict)
on_item_changed = pyqtSignal(dict)
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')
# self.tableview = QTableView(self)
self.tablewidget = QTableWidget(self)
self.tablewidget.setColumnCount(5)
self.tablewidget.setRowCount(0)
self.tablewidget.setHorizontalHeaderLabels(['序号', 'X', 'Y', '概率', '变化'])
self.tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
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)
self.tablewidget.cellDoubleClicked.connect(self.onDoubleClicked)
self.tablewidget.cellClicked.connect(self.onClicked)
self.tablewidget.cellChanged.connect(self.onChanged)
# self.tablewidget.setModel(self.tableview)
# self.tableview
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tree)
layout.addWidget(self.tablewidget)
self.setLayout(layout)
def clear(self):
pass
def onChanged(self, row, col):
if col == 4:
item_idx = row
item_status = self.tablewidget.item(row, col).checkState() == Qt.Checked
self.on_item_changed.emit({'idx':item_idx, 'status':item_status})
def onClicked(self, row, col):
if col == 4:
self.tablewidget.item(row, col).setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
def onDoubleClicked(self, row, col):
x = self.tablewidget.item(row, 1).text()
y = self.tablewidget.item(row, 2).text()
self.on_item_click.emit({'x':x, 'y':y})
def set_data(self, data:ResultLayer):
self.tablewidget.setRowCount(len(data.data))
for i, d in enumerate(data.data):
self.tablewidget.setItem(i, 0, QTableWidgetItem(str(i+1)))
self.tablewidget.setItem(i, 1, QTableWidgetItem(str(d[0]))) # X
self.tablewidget.setItem(i, 2, QTableWidgetItem(str(d[1]))) # Y
self.tablewidget.setItem(i, 3, QTableWidgetItem(str(d[2]))) # 概率
status_item = QTableWidgetItem('变化')
if d[3] == 0:
status_item.setBackground(Qt.green)
status_item.setCheckState(Qt.Unchecked)
elif d[3] == 1:
status_item.setBackground(Qt.yellow)
status_item.setCheckState(Qt.Checked)
self.tablewidget.setItem(i, 4, status_item) # 变化
self.tablewidget.resizeColumnsToContents()
self.tablewidget.resizeRowsToContents()
self.tablewidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.tablewidget.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
def onClicked(self,index):
print(index.row())
def add_layer(self, layer:PairLayer):
pass

View File

@ -12,5 +12,4 @@ with open(os.path.join(path, '..', 'res.qrc'), 'w') as f:
f.write(f'</RCC>\n')
subprocess.run(['pyrcc5', 'res.qrc', '-o', 'rc.py'], cwd=os.path.join(path, '..'))
shutil.rmtree(icon_path)

12
rscder/utils/misc.py Normal file
View File

@ -0,0 +1,12 @@
from functools import wraps
def singleton(cls):
_instance = {}
@wraps(cls)
def inner(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
cls.instance = _instance[cls]
return _instance[cls]
return inner

View File

@ -8,22 +8,20 @@ from qgis.core import QgsRasterLayer, QgsLineSymbol, QgsSingleSymbolRenderer, Qg
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtGui import QColor
import yaml
def singleton(cls):
_instance = {}
from .misc import singleton
def inner(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return inner
def relative_path(path: str, root:str) -> str:
return os.path.relpath(path, root)
@singleton
class Project(QObject):
project_init = pyqtSignal(bool)
layer_load = pyqtSignal(str)
ABSOLUTE_MODE = 'absolute'
RELATIVE_MODE = 'relative'
def __init__(self,
parent=None):
super().__init__(parent)
@ -31,9 +29,9 @@ class Project(QObject):
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
self.layers:Dict = dict()
# self.layers:List[PairLayer] = []
self.root = str(Path(Settings.General().root)/'default')
self.file_mode = Project.ABSOLUTE_MODE
self.layers:Dict[str, PairLayer] = dict()
def connect(self, pair_canvas,
layer_tree,
@ -46,18 +44,17 @@ class Project(QObject):
self.layer_load.connect(layer_tree.add_layer)
self.layer_load.connect(pair_canvas.add_layer)
# self.layer_load.connect(message_box.add_layer)
# self.layer_load.connect(message_box.add_layer)
def setup(self, file=None):
def setup(self, path = None, name = None):
self.is_init = True
self.file = file
if file is None:
self.file = Path(self.root)/'project'/'untitled.prj'
self.root = str(Path(self.file).parent)
dir_name = os.path.dirname(self.file)
if not os.path.exists(dir_name):
os.makedirs(dir_name, exist_ok=True)
if path is not None:
self.root = path
if name is None:
self.file = str(Path(self.root)/'default.prj')
if not os.path.exists(self.root):
os.makedirs(self.root, exist_ok=True)
if not os.path.exists(self.file):
with open(self.file, 'w') as f:
pass
@ -72,7 +69,7 @@ class Project(QObject):
'max_memory': self.max_memory,
'max_threads': self.max_threads,
'root': self.root,
'layers': [ layer.to_dict() for layer in self.layers.values() ],
'layers': [ layer.to_dict(None if self.file_mode == Project.ABSOLUTE_MODE else self.root) for layer in self.layers.values() ],
'results': []
}
with open(self.file, 'w') as f:
@ -94,7 +91,7 @@ class Project(QObject):
with open(self.file, 'r') as f:
data = yaml.safe_load(f)
if data is None:
return
return
# data = yaml.safe_load(open(self.file, 'r'))
self.cell_size = data['cell_size']
self.max_memory = data['max_memory']
@ -102,13 +99,14 @@ class Project(QObject):
self.root = data['root']
self.layers = dict()
for layer in data['layers']:
player = PairLayer.from_dict(layer)
player = PairLayer.from_dict(layer, None if self.file_mode == Project.ABSOLUTE_MODE else self.root)
if player.check():
self.layers[player.id] = player
self.layer_load.emit(player.id)
def add_layer(self, pth1, pth2):
self.root = str(Path(pth1).parent)
# self.root = str(Path(pth1).parent)
player = PairLayer(pth1, pth2, self.cell_size)
if player.check():
# self.layers.append(player)
@ -201,20 +199,41 @@ class GridLayer:
def from_dict(data):
return GridLayer()
class ResultLayer:
POINT = 0
RASTER = 1
def __init__(self, layer_type):
self.layer_type = layer_type
self.data = []
class PairLayer:
def to_dict(self):
return {
'pth1': self.pth1,
'pth2': self.pth2,
'l1_name': self.l1_name,
'l2_name': self.l2_name,
'cell_size': self.cell_size,
}
def to_dict(self, root = None):
if root is None:
return {
'pth1': self.pth1,
'pth2': self.pth2,
'l1_name': self.l1_name,
'l2_name': self.l2_name,
'cell_size': self.cell_size,
}
else:
return {
'pth1': relative_path(self.pth1, root),
'pth2': relative_path(self.pth2, root),
'l1_name': self.l1_name,
'l2_name': self.l2_name,
'cell_size': self.cell_size,
}
@staticmethod
def from_dict(data):
layer = PairLayer(data['pth1'], data['pth2'], data['cell_size'])
def from_dict(data, root = None):
if root is None:
layer = PairLayer(data['pth1'], data['pth2'], data['cell_size'])
else:
layer = PairLayer(os.path.join(root, data['pth1']), os.path.join(root, data['pth2']), data['cell_size'])
layer.l1_name = data['l1_name']
layer.l2_name = data['l2_name']
# layer.grid_layer = GridLayer.from_dict(data['grid_layer'])
@ -232,8 +251,11 @@ class PairLayer:
# self.grid_layer = GridLayer(cell_size)
self.msg = ''
self.checked = False
def check(self):
if self.checked:
return self.checked
if not os.path.exists(self.pth1):
self.msg = '图层1不存在'
return False
@ -258,5 +280,5 @@ class PairLayer:
self.l1 = QgsRasterLayer(self.pth1, self.l1_name)
self.l2 = QgsRasterLayer(self.pth2, self.l2_name)
self.checked = True
return True

View File

@ -3,7 +3,7 @@ import os
from typing import Tuple
from PyQt5.QtCore import QSettings
from rscder.utils.license import LicenseHelper
import yaml
class Settings(QSettings):
def __init__(self, key):
@ -22,20 +22,25 @@ class Settings(QSettings):
@property
def root(self):
return './3rd'
_r = './3rd'
if not os.path.exists(_r):
os.makedirs(_r)
return _r
@property
def plugins(self):
with Settings(Settings.Plugin.PRE) as s:
pl = s.value('plugins', [])
if pl is None:
return []
return pl
plugins_file = os.path.join(self.root, 'plugins.yaml')
if not os.path.exists(plugins_file):
with open(plugins_file, 'w') as f:
yaml.safe_dump([], f)
with open(plugins_file, 'r') as f:
return yaml.safe_load(f)
@plugins.setter
def plugins(self, value):
with Settings(Settings.Plugin.PRE) as s:
s.setValue('plugins', value)
plugins_file = os.path.join(self.root, 'plugins.yaml')
with open(plugins_file, 'w') as f:
yaml.safe_dump(value, f)
class Project:
@ -85,6 +90,16 @@ class Settings(QSettings):
with Settings(Settings.General.PRE) as s:
s.setValue('size', value)
@property
def last_path(self):
with Settings(Settings.General.PRE) as s:
return s.value('last_path', '')
@last_path.setter
def last_path(self, value):
with Settings(Settings.General.PRE) as s:
s.setValue('last_path', value)
@property
def end_date(self):
if not os.path.exists('lic/license.lic'):

15
setup.py Normal file
View File

@ -0,0 +1,15 @@
from distutils.core import setup
import shutil
from Cython.Build import cythonize
import sys
import os
for plugin in os.listdir('plugins'):
if os.path.isdir(os.path.join('plugins', plugin)):
setup(
name = plugin,
ext_modules = cythonize(os.path.join('plugins', plugin, '*.py'), exclude=[ f'plugins/{plugin}/__init__.py']),
script_args = ['build_ext', '-b', 'plugin-build'],
)
shutil.copy(os.path.join('plugins', plugin, '__init__.py'), os.path.join('plugin-build', plugin, '__init__.py'))

View File

@ -1,12 +1,7 @@
cell_size: &id001
cell_size:
- 100
- 100
layers:
- cell_size: *id001
l1_name: p122_r032_l5_20090915.tif
l2_name: p122_r032_l8_20170804.tif
pth1: F:/LZY_DATA/p122r032/p122_r032_l5_20090915.tif
pth2: F:/LZY_DATA/p122r032/p122_r032_l8_20170804.tif
layers: []
max_memory: 100
max_threads: 4
results: []