fix the layer tree

This commit is contained in:
copper 2022-05-23 19:23:35 +08:00
parent cb8e5fed1d
commit e7bf0b1fae
13 changed files with 501 additions and 480 deletions

View File

10
log.txt
View File

@ -1,6 +1,4 @@
2022-05-17 16:14:17,007 - root - INFO - lic data:2022-12-01 00:00:00 2022-05-23 19:21:48,894 - root - INFO - lic data:2022-12-01 00:00:00
2022-05-17 16:14:17,010 - root - INFO - remain_days: 197 2022-05-23 19:21:48,899 - root - INFO - remain_days: 191
2022-05-17 16:14:18,338 - root - INFO - lic data:2022-12-01 00:00:00 2022-05-23 19:21:50,164 - root - INFO - lic data:2022-12-01 00:00:00
2022-05-17 16:14:18,338 - root - INFO - remain_days: 197 2022-05-23 19:21:50,164 - root - INFO - remain_days: 191
2022-05-17 16:35:12,026 - root - INFO - Empty module name
2022-05-17 16:35:13,397 - root - INFO - None

View File

@ -5,7 +5,7 @@ from rscder.plugins.basic import BasicPlugin
from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton
from PyQt5.QtCore import pyqtSignal from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from rscder.utils.project import Project, PairLayer, ResultLayer from rscder.utils.project import BasicLayer, Project, PairLayer, ResultPointLayer, RasterLayer
from rscder.gui.layercombox import LayerCombox from rscder.gui.layercombox import LayerCombox
from osgeo import gdal, gdal_array from osgeo import gdal, gdal_array
from threading import Thread from threading import Thread
@ -77,32 +77,19 @@ class BasicMethod(BasicPlugin):
basic_diff_method.triggered.connect(self.basic_diff_alg) basic_diff_method.triggered.connect(self.basic_diff_alg)
self.message_send.connect(self.send_message) self.message_send.connect(self.send_message)
self.result_ok.connect(self.on_result_ok)
# self.result_ok.connect(self.on_result_ok)
self.gap = 250 self.gap = 250
def setup(self):
def on_data_load(self, layer_id):
self.basic_diff_method.setEnabled(True) self.basic_diff_method.setEnabled(True)
def send_message(self, s): def send_message(self, s):
self.message_box.info(s) self.message_box.info(s)
def on_result_ok(self, data): def run_basic_diff_alg(self, layer:PairLayer):
layer = Project().layers[data['layer_id']]
csv_result = ResultLayer('basic_diff_result', layer, ResultLayer.POINT)
csv_result.load_file(data['csv_file'])
raster_layer = ResultLayer('basic_diff_result-raster',layer, ResultLayer.RASTER) pth1 = layer.main_l1.path
raster_layer.load_file(data['raster_file']) pth2 = layer.main_l2.path
layer.results.append(csv_result)
layer.results.append(raster_layer)
self.layer_tree.update_layer(layer.id)
def run_basic_diff_alg(self, layer:PairLayer, out):
pth1 = layer.pth1
pth2 = layer.pth2
cell_size = layer.cell_size cell_size = layer.cell_size
@ -118,7 +105,7 @@ class BasicMethod(BasicPlugin):
geo = ds1.GetGeoTransform() geo = ds1.GetGeoTransform()
driver = gdal.GetDriverByName('GTiff') driver = gdal.GetDriverByName('GTiff')
out_tif = os.path.join(out, 'temp.tif') out_tif = os.path.join(Project().cmi_path, 'temp.tif')
out_ds = driver.Create(out_tif, xsize, ysize, 1, gdal.GDT_Float32) out_ds = driver.Create(out_tif, xsize, ysize, 1, gdal.GDT_Float32)
out_ds.SetGeoTransform(ds1.GetGeoTransform()) out_ds.SetGeoTransform(ds1.GetGeoTransform())
out_ds.SetProjection(ds1.GetProjection()) out_ds.SetProjection(ds1.GetProjection())
@ -155,7 +142,7 @@ class BasicMethod(BasicPlugin):
self.message_send.emit('归一化概率中...') self.message_send.emit('归一化概率中...')
temp_in_ds = gdal.Open(out_tif) temp_in_ds = gdal.Open(out_tif)
out_normal_tif = os.path.join(out, '{}.tif'.format(int(np.random.rand() * 100000))) out_normal_tif = os.path.join(Project().cmi_path, '{}-{}.tif'.format(layer.name, int(np.random.rand() * 100000)))
out_normal_ds = driver.Create(out_normal_tif, xsize, ysize, 1, gdal.GDT_Byte) out_normal_ds = driver.Create(out_normal_tif, xsize, ysize, 1, gdal.GDT_Byte)
out_normal_ds.SetGeoTransform(ds1.GetGeoTransform()) out_normal_ds.SetGeoTransform(ds1.GetGeoTransform())
out_normal_ds.SetProjection(ds1.GetProjection()) out_normal_ds.SetProjection(ds1.GetProjection())
@ -188,7 +175,7 @@ class BasicMethod(BasicPlugin):
self.message_send.emit('完成归一化概率') self.message_send.emit('完成归一化概率')
self.message_send.emit('计算变化表格中...') self.message_send.emit('计算变化表格中...')
out_csv = os.path.join(out, '{}.csv'.format(int(np.random.rand() * 100000))) out_csv = os.path.join(Project().bcdm_path, '{}-{}.csv'.format(layer.name, int(np.random.rand() * 100000)))
xblocks = xsize // cell_size[0] xblocks = xsize // cell_size[0]
normal_in_ds = gdal.Open(out_normal_tif) normal_in_ds = gdal.Open(out_normal_tif)
@ -215,11 +202,11 @@ class BasicMethod(BasicPlugin):
f.write(f'{center_x},{center_y},{block_data_xy.mean() / 255 * 100},1\n') f.write(f'{center_x},{center_y},{block_data_xy.mean() / 255 * 100},1\n')
self.result_ok.emit({ point_result_lalyer = ResultPointLayer(out_csv, enable=False, proj = layer.proj, geo = layer.geo)
'layer_id': layer.id, raster_result_layer = RasterLayer(None, True, out_normal_tif, BasicLayer.BOATH_VIEW)
'csv_file': out_csv,
'raster_file': out_normal_tif layer.add_result_layer(point_result_lalyer)
}) layer.add_result_layer(raster_result_layer)
self.message_send.emit('完成计算变化表格') self.message_send.emit('完成计算变化表格')
@ -237,11 +224,8 @@ class BasicMethod(BasicPlugin):
if not layer.check(): if not layer.check():
return return
out_dir =os.path.join(self.project.root, 'basic_diff_result')
if not os.path.exists(out_dir):
os.makedirs(out_dir, exist_ok=True)
t = Thread(target=self.run_basic_diff_alg, args=(layer, out_dir)) t = Thread(target=self.run_basic_diff_alg, args=(layer,))
t.start() t.start()

View File

@ -1,5 +1,5 @@
import shutil import shutil
from rscder.utils.project import Project, PairLayer, ResultLayer from rscder.utils.project import Project, PairLayer, ResultPointLayer
from rscder.plugins.basic import BasicPlugin from rscder.plugins.basic import BasicPlugin
from PyQt5.QtWidgets import QDialog, QHBoxLayout, QFileDialog, QComboBox, QVBoxLayout, QPushButton, QLabel, QLineEdit, QAction from PyQt5.QtWidgets import QDialog, QHBoxLayout, QFileDialog, QComboBox, QVBoxLayout, QPushButton, QLabel, QLineEdit, QAction
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
@ -20,8 +20,8 @@ class ExportDialog(QDialog):
result_layer_select.addItem('---', None) result_layer_select.addItem('---', None)
for layer in Project().layers.values(): for layer in Project().layers.values():
for result_layer in layer.results: for result_layer in layer.layers:
if result_layer.layer_type == ResultLayer.POINT: if isinstance(result_layer, ResultPointLayer):
result_layer_select.addItem( layer.name[:5] + '-' + result_layer.name, result_layer) result_layer_select.addItem( layer.name[:5] + '-' + result_layer.name, result_layer)
for i in range(result_layer_select.count() - 1): for i in range(result_layer_select.count() - 1):

View File

@ -12,7 +12,7 @@ from rscder.utils.project import PairLayer, Project
class LayerTree(QtWidgets.QWidget): class LayerTree(QtWidgets.QWidget):
LAYER_TOOT = 0 LAYER_TOOT = 0
SUB_RASTER = 1 SUB_LAYERS = 1
RESULT = 2 RESULT = 2
LEFT_RASTER = 0 LEFT_RASTER = 0
RIGHT_RASTER = 1 RIGHT_RASTER = 1
@ -32,194 +32,84 @@ class LayerTree(QtWidgets.QWidget):
self.tree.customContextMenuRequested.connect(self.right_menu_show) self.tree.customContextMenuRequested.connect(self.right_menu_show)
self.root=QTreeWidgetItem(self.tree) self.root=QTreeWidgetItem(self.tree)
self.tree.setHeaderHidden(True) self.tree.setHeaderHidden(True)
# self.tree.setHeaderLabels(['图层']) # self.tree.setHeaderLabels(['图层'])
self.root.setText(0,'图层') self.root.setText(0,'图层')
self.root.setIcon(0,QtGui.QIcon(':/icons/layer.png')) self.root.setIcon(0,QtGui.QIcon(':/icons/layer.png'))
self.tree.expandAll() self.tree.expandAll()
self.tree.addTopLevelItem(self.root) self.tree.addTopLevelItem(self.root)
self.tree.itemChanged.connect(self.onItemChanged) self.tree.itemChanged.connect(self.onItemChanged)
self.tree.itemExpanded.connect(self.onItemExpanded)
self._expand = True
self.root.setExpanded(self._expand)
layout = QtWidgets.QGridLayout() layout = QtWidgets.QGridLayout()
layout.addWidget(self.tree) layout.addWidget(self.tree)
self.setLayout(layout) self.setLayout(layout)
self.setLayoutDirection(Qt.LeftToRight) self.setLayoutDirection(Qt.LeftToRight)
self.is_in_add_layer = False self.is_update_layer = False
self.current_item = None self.current_item = None
def onItemExpanded(self, item:QtWidgets.QTreeWidgetItem):
if item == self.root:
self._expand = item.isExpanded()
return
if hasattr(item, 'item_update'):
item.item_update(item)
def onItemChanged(self, item:QtWidgets.QTreeWidgetItem, column): def onItemChanged(self, item:QtWidgets.QTreeWidgetItem, column):
if self.is_in_add_layer: if self.is_update_layer:
return return
if item == self.root: if item == self.root:
return self._expand = item.isExpanded()
root = item
if item.data(0, Qt.UserRole) != LayerTree.LAYER_TOOT:
root = item.parent()
layer = Project().layers[root.data(0, Qt.UserRole + 1)]
if item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT:
layer.enable = item.checkState(0) == Qt.Checked
if item.data(0, Qt.UserRole) == LayerTree.SUB_RASTER:
if item.data(0, Qt.UserRole + 1) == LayerTree.LEFT_RASTER:
layer.l1_enable = item.checkState(0) == Qt.Checked
elif item.data(0, Qt.UserRole + 1) == LayerTree.RIGHT_RASTER:
layer.l2_enable = item.checkState(0) == Qt.Checked
if item.data(0, Qt.UserRole) == LayerTree.RESULT:
layer.results[item.data(0, Qt.UserRole + 1)].enable = item.checkState(0) == Qt.Checked
if item.data(0, Qt.UserRole) == LayerTree.GRID:
layer.grid_enable = item.checkState(0) == Qt.Checked
self.tree_changed.emit()
def add_layer(self, layer:str):
# self.tree.it
self.is_in_add_layer = True
layer:PairLayer = Project().layers[layer]
item_root = QtWidgets.QTreeWidgetItem(self.root)
item_root.setText(0,layer.name)
item_root.setIcon(0, QtGui.QIcon(':/icons/document.png'))
item_root.setData(0, Qt.UserRole, LayerTree.LAYER_TOOT)
item_root.setData(0, Qt.UserRole + 1, layer.id)
item_root.setCheckState(0, Qt.Checked if layer.enable else Qt.Unchecked)
self.add_sub_layer(item_root, layer)
self.is_in_add_layer = False
def add_sub_layer(self, item_root, layer:PairLayer):
# print(item_root.text(0))
# print(layer.results.__len__())
grid_item = QtWidgets.QTreeWidgetItem(item_root)
grid_item.setText(0,'格网')
grid_item.setData(0, Qt.UserRole, LayerTree.GRID)
grid_item.setCheckState(0, Qt.Checked if layer.grid_enable else Qt.Unchecked)
grid_item.setIcon(0, QtGui.QIcon(':/icons/grid.png'))
item1 = QtWidgets.QTreeWidgetItem(item_root)
item1.setText(0, layer.l1_name)
item1.setCheckState(0, Qt.Checked if layer.l1_enable else Qt.Unchecked)
item1.setData(0, Qt.UserRole, LayerTree.SUB_RASTER)
item1.setData(0, Qt.UserRole + 1, LayerTree.LEFT_RASTER)
item1.setIcon(0, QtGui.QIcon(':/icons/layer.png'))
item2 = QtWidgets.QTreeWidgetItem(item_root)
item2.setText(0, layer.l2_name)
item2.setCheckState(0, Qt.Checked if layer.l2_enable else Qt.Unchecked)
item2.setData(0, Qt.UserRole, LayerTree.SUB_RASTER)
item2.setData(0, Qt.UserRole + 1, LayerTree.RIGHT_RASTER)
item2.setIcon(0, QtGui.QIcon(':/icons/layer.png'))
for ri, item in enumerate(layer.results):
item_result = QtWidgets.QTreeWidgetItem(item_root)
item_result.setText(0, item.name)
item_result.setCheckState(0, Qt.Checked if item.enable else Qt.Unchecked)
item_result.setData(0, Qt.UserRole, LayerTree.RESULT)
item_result.setData(0, Qt.UserRole + 1, ri)
item_result.setIcon(0, QtGui.QIcon(':/icons/vector.svg'))
self.tree.expandAll()
def update_layer(self, layer:str):
self.is_in_add_layer = True
layer:PairLayer = Project().layers[layer]
layer_root = None
# pdb.set_trace()
for idx in range(self.root.childCount()):
item_root = self.root.child(idx)
if item_root.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT:
if item_root.data(0, Qt.UserRole + 1) == layer.id:
layer_root = item_root
break
logging.info(layer_root.text(0))
if layer_root is None:
self.add_layer(layer.id)
return return
layer_root.setText(0,layer.name) if hasattr(item, 'item_update'):
item.item_update(item)
while layer_root.childCount() > 0: def update_layer(self):
layer_root.removeChild(layer_root.child(0)) self.is_update_layer = True
self.clear()
self.add_sub_layer(layer_root, layer) for layer_group in Project().layers.values():
self.is_in_add_layer = False item_root = layer_group.get_item(self.root)
# self.root.addChild(item_root)
layer_group.grid.get_item(item_root)
for _, sub in enumerate(layer_group.layers):
sub.get_item(item_root)
# item_root.addChild(item)
self.is_update_layer = False
def clear(self): def clear(self):
self.tree.clear() self.tree.clear()
self.root = QTreeWidgetItem(self.tree) self.root = QTreeWidgetItem(self.tree)
self.root.setText(0,'图层')
self.tree.addTopLevelItem(self.root) self.tree.addTopLevelItem(self.root)
self.tree.expandAll()
def delete_layer(self): self.root.setText(0,'图层')
item = self.current_item self.root.setIcon(0,QtGui.QIcon(':/icons/layer.png'))
if item is None: self.root.setExpanded(self._expand)
return
if item == self.root:
return
root = item
if item.data(0, Qt.UserRole) != LayerTree.LAYER_TOOT:
root = item.parent()
if item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT:
self.root.takeChild(self.root.indexOfChild(item))
del Project().layers[root.data(0, Qt.UserRole + 1)]
elif item.data(0, Qt.UserRole) == LayerTree.SUB_RASTER:
return
elif item.data(0, Qt.UserRole) == LayerTree.GRID:
return
elif item.data(0, Qt.UserRole) == LayerTree.RESULT:
root.takeChild(root.indexOfChild(item))
del Project().layers[root.data(0, Qt.UserRole + 1)].results[item.data(0, Qt.UserRole + 1)]
self.update_layer(root.data(0, Qt.UserRole + 1))
self.tree_changed.emit(root.data(0, Qt.UserRole + 1))
def zoom_to_layer(self):
item = self.current_item
if item is None:
return
if item == self.root:
return
root = item
if item.data(0, Qt.UserRole) != LayerTree.LAYER_TOOT:
root = item.parent()
self.zoom_to_layer_signal.emit(root.data(0, Qt.UserRole + 1))
def right_menu_show(self, position): def right_menu_show(self, position):
rightMenu = QtWidgets.QMenu(self) rightMenu = QtWidgets.QMenu(self)
# QAction = QtWidgets.QAction(self.menuBar1)
item = self.tree.itemAt(position) item = self.tree.itemAt(position)
self.current_item = item self.current_item = item
action_manager = get_action_manager() action_manager = get_action_manager()
actions = [] actions = []
data_load_action = action_manager.get_action('&数据加载', 'File') data_load_action = action_manager.get_action('&数据加载', 'File')
actions.append(data_load_action) actions.append(data_load_action)
zoom_to_action = QtWidgets.QAction(QIcon(':/icons/full.svg'), '&缩放至该图层', self)
del_action = QtWidgets.QAction(QIcon(':/icons/delete.png'), '&删除该图层', self)
zoom_to_action.triggered.connect(self.zoom_to_layer)
del_action.triggered.connect(self.delete_layer)
if item is None: if item is None:
logging.info('nothing') logging.info('nothing')
else: else:
if item == self.root: if item == self.root:
pass pass
elif item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT: else:
actions.append(zoom_to_action) actions.extend(item.get_actions())
actions.append(QtWidgets.QAction('&重命名', self))
actions.append(del_action)
elif item.data(0, Qt.UserRole) == LayerTree.SUB_RASTER:
actions.append(zoom_to_action)
actions.append(QtWidgets.QAction('&重命名', self))
elif item.data(0, Qt.UserRole) == LayerTree.RESULT:
actions.append(zoom_to_action)
actions.append(QtWidgets.QAction('&重命名', self))
actions.append(del_action)
for action in actions: for action in actions:
rightMenu.addAction(action) rightMenu.addAction(action)

View File

@ -1,6 +1,6 @@
import pdb import pdb
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QToolBox from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QToolBox
from PyQt5.QtCore import Qt, QSize, QSettings from PyQt5.QtCore import Qt, QSize, QSettings, pyqtSignal
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5 import QtGui from PyQt5 import QtGui
from PyQtAds import QtAds from PyQtAds import QtAds
@ -15,6 +15,8 @@ from rscder.utils.project import Project
from rscder.gui.layercombox import LayerCombox from rscder.gui.layercombox import LayerCombox
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
closed = pyqtSignal()
def __init__(self, parent=None, **kargs): def __init__(self, parent=None, **kargs):
super().__init__(parent) super().__init__(parent)
# self.current_instance = kargs.get('current_instance', 0) # self.current_instance = kargs.get('current_instance', 0)
@ -33,11 +35,8 @@ class MainWindow(QMainWindow):
self.layer_tree, self.layer_tree,
self.message_box, self.message_box,
self.result_box) self.result_box)
self.layer_tree.tree_changed.connect(self.double_map.layer_changed)
self.layer_tree.result_clicked.connect(self.result_box.on_result) self.result_box.on_item_click.connect(self.double_map.zoom_to_extent)
self.result_box.on_item_click.connect(self.double_map.zoom_to_result)
self.result_box.on_item_changed.connect(Project().change_result)
self.layer_tree.zoom_to_layer_signal.connect(self.double_map.zoom_to_layer)
self.action_manager = ActionManager( self.action_manager = ActionManager(
self.double_map, self.double_map,
@ -126,7 +125,7 @@ class MainWindow(QMainWindow):
set_docker_fixed(self.message_dock) set_docker_fixed(self.message_dock)
def closeEvent(self, event): def closeEvent(self, event):
pass self.closed.emit()
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
Settings.General().size = (a0.size().width(), a0.size().height()) Settings.General().size = (a0.size().width(), a0.size().height())

View File

@ -20,7 +20,7 @@ import tempfile
import cv2 import cv2
import os import os
from rscder.utils.project import PairLayer, Project from rscder.utils.project import BasicLayer, PairLayer, Project
class DoubleCanvas(QWidget): class DoubleCanvas(QWidget):
corr_changed = pyqtSignal(str) corr_changed = pyqtSignal(str)
@ -106,18 +106,20 @@ class DoubleCanvas(QWidget):
layer_list_2 = [] layer_list_2 = []
for layer in layers.values(): for layer in layers.values():
if layer.enable: if layer.enable:
if layer.grid_enable and self.grid_show: if layer.grid.enable and self.grid_show:
layer_list_1.append(layer.grid_layer.grid_layer) layer_list_1.append(layer.grid.layer)
layer_list_2.append(layer.grid_layer.grid_layer) layer_list_2.append(layer.grid.layer)
if layer.l1_enable:
layer_list_1.append(layer.l1) for sub_layer in layer.layers:
if layer.l2_enable: if sub_layer.enable:
layer_list_2.append(layer.l2) if sub_layer.view_mode == BasicLayer.LEFT_VIEW:
layer_list_1.append(sub_layer.layer)
elif sub_layer.view_mode == BasicLayer.RIGHT_VIEW:
layer_list_2.append(sub_layer.layer)
elif sub_layer.view_mode == BasicLayer.BOATH_VIEW:
layer_list_2.append(sub_layer.layer)
layer_list_1.append(sub_layer.layer)
for result in layer.results:
if result.enable:
layer_list_1.append(result.layer)
layer_list_2.append(result.layer)
self.mapcanva1.setLayers(layer_list_1) self.mapcanva1.setLayers(layer_list_1)
self.mapcanva2.setLayers(layer_list_2) self.mapcanva2.setLayers(layer_list_2)
@ -149,22 +151,14 @@ class DoubleCanvas(QWidget):
self.mapcanva1.refresh() self.mapcanva1.refresh()
self.mapcanva2.refresh() self.mapcanva2.refresh()
def zoom_to_result(self, xydict:dict): def zoom_to_extent(self, extent):
x = xydict['x'] # extent = QgsRectangle(x - layer.cell_size[0] * layer.xres, y - layer.cell_size[1] * layer.yres, x + layer.cell_size[0] * layer.xres, y + layer.cell_size[1] * layer.yres)
y = xydict['y']
if Project().current_layer is not None:
layer = Project().current_layer
else:
layer = Project().layers[list(Project().layers.keys())[0]]
extent = QgsRectangle(x - layer.cell_size[0] * layer.xres, y - layer.cell_size[1] * layer.yres, x + layer.cell_size[0] * layer.xres, y + layer.cell_size[1] * layer.yres)
self.mapcanva1.set_extent(extent) self.mapcanva1.set_extent(extent)
self.mapcanva2.set_extent(extent) self.mapcanva2.set_extent(extent)
def zoom_to_layer(self, layer:str): def zoom_to_layer(self, layer):
layer:PairLayer = Project().layers[layer] self.mapcanva1.set_extent(layer.extent())
self.mapcanva1.set_extent(layer.l1.extent()) self.mapcanva2.set_extent(layer.extent())
self.mapcanva2.set_extent(layer.l2.extent())
def layer_changed(self, layer:str): def layer_changed(self, layer:str):
self.add_layer(layer) self.add_layer(layer)

View File

@ -4,29 +4,26 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt,QModelIndex, pyqtSignal from PyQt5.QtCore import Qt,QModelIndex, pyqtSignal
from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import (QTableWidgetItem, QTableWidget, QMessageBox, QAbstractItemView, QHeaderView, QStyleFactory) from PyQt5.QtWidgets import (QTableWidgetItem, QTableWidget, QMessageBox, QAbstractItemView, QHeaderView, QStyleFactory)
from qgis.core import QgsRectangle
from rscder.utils.project import PairLayer, Project, ResultLayer from rscder.utils.project import PairLayer, Project, ResultPointLayer
class ResultTable(QtWidgets.QWidget): class ResultTable(QtWidgets.QWidget):
on_item_click = pyqtSignal(dict) on_item_click = pyqtSignal(QgsRectangle)
on_item_changed = pyqtSignal(dict) on_item_changed = pyqtSignal(dict)
def __init__(self, parent=None): def __init__(self, parent=None):
super(ResultTable, self).__init__(parent) super(ResultTable, self).__init__(parent)
# self.tableview = QTableView(self) # self.tableview = QTableView(self)
self.tablewidget = QTableWidget(self) self.tablewidget = QTableWidget(self)
self.tablewidget.setColumnCount(4) self.tablewidget.setColumnCount(3)
self.tablewidget.setRowCount(0) self.tablewidget.setRowCount(0)
self.tablewidget.setHorizontalHeaderLabels(['X', 'Y', '概率', '变化']) self.tablewidget.setHorizontalHeaderLabels(['变化位置(x,y)', '疑似变化概率', '目视判读'])
self.tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.tablewidget.cellDoubleClicked.connect(self.onDoubleClicked) self.tablewidget.cellClicked.connect(self.onClicked)
# self.tablewidget.cellClicked.connect(self.onClicked) # self.tablewidget.cellClicked.connect(self.onClicked)
self.tablewidget.cellChanged.connect(self.onChanged) self.tablewidget.cellChanged.connect(self.onChanged)
# self.tablewidget.setModel(self.tableview)
# self.tableview
layout = QtWidgets.QVBoxLayout(self) layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tablewidget) layout.addWidget(self.tablewidget)
self.setLayout(layout) self.setLayout(layout)
@ -36,27 +33,44 @@ class ResultTable(QtWidgets.QWidget):
def clear(self): def clear(self):
self.tablewidget.clear() self.tablewidget.clear()
self.tablewidget.setColumnCount(3)
self.tablewidget.setRowCount(0)
self.tablewidget.setHorizontalHeaderLabels(['变化位置(x,y)', '疑似变化概率', '目视判读'])
def onChanged(self, row, col): def onChanged(self, row, col):
if self.is_in_set_data or self.no_change: if self.is_in_set_data or self.no_change:
return return
if col == 3: if col == 2:
self.no_change = True self.no_change = True
item_idx = row item_idx = row
item_status = self.tablewidget.item(row, col).checkState() == Qt.Checked item_status = self.tablewidget.item(row, col).checkState() == Qt.Checked
if item_status: if item_status:
self.tablewidget.item(row, col).setBackground(Qt.yellow) self.tablewidget.item(row, col).setBackground(Qt.yellow)
self.tablewidget.item(row, col).setText('YES')
else: else:
self.tablewidget.item(row, col).setBackground(Qt.green) self.tablewidget.item(row, col).setBackground(Qt.green)
self.tablewidget.item(row, col).setText('NO')
# logging # logging
# logging.info() # logging.info()
self.result.update({'row':item_idx, 'value':item_status}) self.result.update({'row':item_idx, 'value':item_status})
self.no_change = False self.no_change = False
def onDoubleClicked(self, row, col): def onClicked(self, row, col):
x = self.tablewidget.item(row, 0).text() if col != 0:
y = self.tablewidget.item(row, 1).text() return
self.on_item_click.emit({'x':float(x), 'y':float(y)}) data = self.result.data[row]
x = data[0]
y = data[1]
xres = self.result.layer_parent.geo[1]
yres = self.result.layer_parent.geo[5]
cell_size = self.result.layer_parent.cell_size
x_min = x - xres * cell_size[0]
x_max = x + xres * cell_size[0]
y_min = y - yres * cell_size[1]
y_max = y + yres * cell_size[1]
extent = QgsRectangle(x_min, y_min, x_max, y_max)
self.on_item_click.emit(extent)
def save(self): def save(self):
if self.result is None: if self.result is None:
@ -70,26 +84,27 @@ class ResultTable(QtWidgets.QWidget):
self.save() self.save()
self.result = result self.result = result
self.clear() self.clear()
self.set_data(result) self.show_result(result)
def set_data(self, data:ResultLayer):
def show_result(self, data:ResultPointLayer):
self.is_in_set_data = True self.is_in_set_data = True
if data.layer_type != ResultLayer.POINT: self.result = data
return
self.tablewidget.setRowCount(len(data.data)) self.tablewidget.setRowCount(len(data.data))
# print(len(data.data)) # print(len(data.data))
self.tablewidget.setVerticalHeaderLabels([ str(i+1) for i in range(len(data.data))]) self.tablewidget.setVerticalHeaderLabels([ str(i+1) for i in range(len(data.data))])
for i, d in enumerate(data.data): for i, d in enumerate(data.data):
self.tablewidget.setItem(i, 0, QTableWidgetItem(str(d[0]))) # X self.tablewidget.setItem(i, 0, QTableWidgetItem('%.3f,%.3f'%(d[0], d[1]))) # X
self.tablewidget.setItem(i, 1, QTableWidgetItem(str(d[1]))) # Y self.tablewidget.setItem(i, 1, QTableWidgetItem(str(d[2]))) # Y
self.tablewidget.setItem(i, 2, QTableWidgetItem(str(d[2]))) # 概率 status_item = QTableWidgetItem('')
status_item = QTableWidgetItem('变化')
if d[3] == 0: if d[3] == 0:
status_item.setBackground(Qt.green) status_item.setBackground(Qt.green)
status_item.setCheckState(Qt.Unchecked) status_item.setCheckState(Qt.Unchecked)
status_item.setText('NO')
elif d[3] == 1: elif d[3] == 1:
status_item.setBackground(Qt.yellow) status_item.setBackground(Qt.yellow)
status_item.setCheckState(Qt.Checked) status_item.setCheckState(Qt.Checked)
self.tablewidget.setItem(i, 3, status_item) # 变化 status_item.setText('YES')
self.tablewidget.setItem(i, 2, status_item) # 变化
self.tablewidget.resizeColumnsToContents() self.tablewidget.resizeColumnsToContents()
self.tablewidget.resizeRowsToContents() self.tablewidget.resizeRowsToContents()
self.tablewidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.tablewidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

View File

@ -34,7 +34,7 @@ class BasicPlugin(QObject):
self.project = ctx['project'] self.project = ctx['project']
self.mainwindow = ctx['mainwindow'] self.mainwindow = ctx['mainwindow']
self.set_action() self.set_action()
self.project.layer_load.connect(self.on_data_load) # self.project.layer_load.connect(self.on_data_load)
self.project.project_init.connect(self.setup) self.project.project_init.connect(self.setup)

13
rscder/utils/icons.py Normal file
View File

@ -0,0 +1,13 @@
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QObject
from .misc import singleton
@singleton
class IconInstance(QObject):
def __init__(self, parent) -> None:
super().__init__(parent)
self.GRID_ON = QIcon(':/icons/grid.png')
self.TABLE = QIcon(':/icons/table.png')
self.DELETE = QIcon(':/icons/delete.png')

View File

@ -1,4 +1,5 @@
from functools import wraps from functools import wraps
def singleton(cls): def singleton(cls):
_instance = {} _instance = {}

View File

@ -1,16 +1,21 @@
from cgitb import enable
from collections import OrderedDict from collections import OrderedDict
import inspect
import os import os
from pathlib import Path from pathlib import Path
import shutil
from threading import Thread from threading import Thread
from time import sleep, time from time import sleep, time
from typing import Dict, List from typing import Dict, List
import uuid import uuid
import numpy as np import numpy as np
from osgeo import gdal, gdal_array from osgeo import gdal, gdal_array
from rscder.utils.icons import IconInstance
from rscder.utils.setting import Settings from rscder.utils.setting import Settings
from qgis.core import QgsRasterLayer, QgsMarkerSymbol, QgsUnitTypes, QgsCategorizedSymbolRenderer, QgsRendererCategory, QgsPalLayerSettings, QgsRuleBasedLabeling, QgsTextFormat, QgsLineSymbol, QgsSingleSymbolRenderer, QgsSimpleLineSymbolLayer, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsFeature, QgsGeometry, QgsPointXY from qgis.core import QgsRasterLayer, QgsMarkerSymbol, QgsUnitTypes, QgsCategorizedSymbolRenderer, QgsRendererCategory, QgsPalLayerSettings, QgsRuleBasedLabeling, QgsTextFormat, QgsLineSymbol, QgsSingleSymbolRenderer, QgsSimpleLineSymbolLayer, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsFeature, QgsGeometry, QgsPointXY
from PyQt5.QtCore import QObject, pyqtSignal from PyQt5.QtCore import QObject, pyqtSignal, Qt, QThread
from PyQt5.QtGui import QColor from PyQt5.QtWidgets import QTreeWidgetItem, QAction
from PyQt5.QtGui import QColor, QIcon
import yaml import yaml
from .misc import singleton from .misc import singleton
@ -20,26 +25,32 @@ def relative_path(path: str, root:str) -> str:
@singleton @singleton
class Project(QObject): class Project(QObject):
instance:'Project'
project_init = pyqtSignal(bool) project_init = pyqtSignal(bool)
layer_load = pyqtSignal(str) # layer_load = pyqtSignal()
layer_tree_update = pyqtSignal()
layer_show_update = pyqtSignal()
ABSOLUTE_MODE = 'absolute' ABSOLUTE_MODE = 'absolute'
RELATIVE_MODE = 'relative' RELATIVE_MODE = 'relative'
def run_auto_save(self): def run_auto_save(self):
# t = QThread(self)
if self.in_save:
return
if not self.is_init:
return
if not Settings.General().auto_save:
return
t = Thread(target=self.auto_save) t = Thread(target=self.auto_save)
t.run() t.start()
def auto_save(self): def auto_save(self):
# pre = time() # pre = time()
while True: self.in_save = True
if Settings.General().auto_save and self.is_init:
self.save() self.save()
if self.is_closed: self.in_save = False
break
sleep(Settings.General().auto_save_intervel)
def __init__(self, def __init__(self,
parent=None): parent=None):
@ -53,9 +64,17 @@ class Project(QObject):
self.layers:Dict[str, PairLayer] = OrderedDict() self.layers:Dict[str, PairLayer] = OrderedDict()
self.current_layer = None self.current_layer = None
self.is_closed = False self.is_closed = False
self.in_save = False
def set_close():
self.is_closed = True
parent.closed.connect(set_close)
self.run_auto_save() self.run_auto_save()
def connect(self, pair_canvas, def connect(self,
pair_canvas,
layer_tree, layer_tree,
message_box, message_box,
result_table): result_table):
@ -63,21 +82,14 @@ class Project(QObject):
self.layer_tree = layer_tree self.layer_tree = layer_tree
self.message_box = message_box self.message_box = message_box
self.result_table = result_table self.result_table = result_table
IconInstance(self)
self.layer_load.connect(layer_tree.add_layer) self.layer_tree_update.connect(layer_tree.update_layer)
self.layer_load.connect(pair_canvas.add_layer) self.layer_show_update.connect(pair_canvas.update_layer)
self.layer_tree_update.connect(self.run_auto_save)
def change_result(self, layer_id, result_id, data): self.layer_show_update.connect(self.run_auto_save)
if layer_id in self.layers:
result = self.layers[layer_id].results[result_id]
if result.layer_type == ResultLayer.POINT:
result.update(data)
elif result.layer_type == ResultLayer.RASTER:
pass
def setup(self, path = None, name = None): def setup(self, path = None, name = None):
self.is_init = True
if path is not None: if path is not None:
self.root = path self.root = path
if name is None: if name is None:
@ -91,8 +103,8 @@ class Project(QObject):
pass pass
else: else:
self.load() self.load()
# self.cmi_dir = str(Path(self.root)/'cmi')
# self.project_created.emit() self.is_init = True
self.project_init.emit(True) self.project_init.emit(True)
def save(self): def save(self):
@ -101,20 +113,18 @@ class Project(QObject):
'max_memory': self.max_memory, 'max_memory': self.max_memory,
'max_threads': self.max_threads, 'max_threads': self.max_threads,
'root': self.root, 'root': self.root,
'layers': [ layer.to_dict(None if self.file_mode == Project.ABSOLUTE_MODE else self.root) for layer in self.layers.values() ], 'layers': [ layer.to_dict() for layer in self.layers.values() ],
} }
for layer in self.layers.values(): # for layer in self.layers.values():
layer.save() # layer.save()
with open(self.file, 'w') as f: with open(self.file, 'w') as f:
yaml.safe_dump(data_dict, f) yaml.safe_dump(data_dict, f)
# yaml.safe_dump(data_dict, open(self.file, 'w'))
def clear(self): def clear(self):
''' '''
clear all layers clear all layers
''' '''
self.layers = dict() self.layers:Dict[str, PairLayer] = dict()
self.layer_tree.clear() self.layer_tree.clear()
self.pair_canvas.clear() self.pair_canvas.clear()
self.message_box.clear() self.message_box.clear()
@ -133,48 +143,181 @@ class Project(QObject):
self.root = data['root'] self.root = data['root']
self.layers = dict() self.layers = dict()
for layer in data['layers']: for layer in data['layers']:
player = PairLayer.from_dict(layer, None if self.file_mode == Project.ABSOLUTE_MODE else self.root) player = PairLayer.from_dict(layer)
if player.check(): if player.check():
self.layers[player.id] = player self.layers[player.id] = player
self.layer_load.emit(player.id)
self.layer_show_update.emit()
self.layer_tree_update.emit()
if len(list(self.layers.values())) > 0:
self.current_layer = list(self.layers.values())[0]
self.pair_canvas.zoom_to_layer(list(self.layers.values())[0].main_l1.layer)
except Exception as e: except Exception as e:
self.message_box.error(str(e)) self.message_box.error(str(e))
self.clear() self.clear()
def zoom_to_layer(self, data):
self.pair_canvas.zoom_to_layer(data['layer'])
@property
def cmi_path(self):
pth = os.path.join(self.root, 'cmi')
if not os.path.exists(pth):
os.makedirs(pth)
return pth
@property
def bcdm_path(self):
pth = os.path.join(self.root, 'bcdm')
if not os.path.exists(pth):
os.makedirs(pth)
return pth
@property
def evalution_path(self):
pth = os.path.join(self.root, 'evalution')
if not os.path.exists(pth):
os.makedirs(pth)
return pth
@property
def other_path(self):
pth = os.path.join(self.root, 'other')
if not os.path.exists(pth):
os.makedirs(pth)
return pth
def add_layer(self, pth1, pth2): def add_layer(self, pth1, pth2):
# self.root = str(Path(pth1).parent) player = PairLayer(pth1, pth2)
player = PairLayer(pth1, pth2, self.cell_size)
if player.check(): if player.check():
# self.layers.append(player)
self.layers[player.id] = player self.layers[player.id] = player
self.layer_load.emit(player.id) self.layer_show_update.emit()
self.layer_tree_update.emit()
self.pair_canvas.zoom_to_layer(player.main_l1.layer)
else: else:
self.message_box.error(player.msg) self.message_box.error(f'{player.name} and {player.name} are not same size')
class GridLayer: def to_dict(obj:'BasicLayer'):
init_args = inspect.getfullargspec(obj.__class__.__init__)[0][1:]
data = {}
for args in init_args:
if hasattr(obj, args):
data[args] = getattr(obj, args)
data['type']=obj.__class__.__name__
return data
def from_dict(data:dict):
cls_type = data.pop('type')
if cls_type is not None and cls_type in globals():
return globals()[cls_type](**data)
class BasicLayer(QObject):
LEFT_VIEW=1
RIGHT_VIEW=2
BOATH_VIEW=3
IN_MEMORY=1
IN_FILE=2
layer_tree_update = pyqtSignal()
layer_show_update = pyqtSignal()
zoom_to_layer = pyqtSignal(dict)
def __init__(self,
name='未命名',
enable = False,
icon = None,
path = None,
path_mode = IN_MEMORY,
view_mode = BOATH_VIEW,):
super().__init__(Project())
self.enable = enable
self.name = name
self.icon = icon
self._path = path
self._expand = True
self.path_mode = path_mode
self.view_mode = view_mode
self.layer = None
self.layer_tree_update.connect(Project().layer_tree_update)
self.layer_show_update.connect(Project().layer_show_update)
self.zoom_to_layer.connect(Project().zoom_to_layer)
@property
def path(self):
if self.path_mode == BasicLayer.IN_FILE and Project().file_mode == Project.RELATIVE_MODE:
return os.path.relpath(self._path, Project().root)
return self._path
def get_item(self, root):
item = QTreeWidgetItem(root)
if self.icon is not None:
item.setIcon(0, QIcon(self.icon))
item.setText(0, self.name)
item.setCheckState(0, Qt.Checked if self.enable else Qt.Unchecked)
item.item_update = self.item_update
item.get_actions = self.get_actions
item.setExpanded(self._expand)
return item
def item_update(self, item:QTreeWidgetItem):
# item = self._item
print('start update')
self.name = item.text(0)
self._expand = item.isExpanded()
pre = self.enable
cur = item.checkState(0) == Qt.Checked
if pre != cur:
self.enable = cur
self.layer_show_update.emit()
print('end update')
def set_layer_parent(self, layer):
self.layer_parent = layer
def get_actions(self):
actions = []
zoom_to_action = QAction(IconInstance().GRID_ON, '缩放至所在图层', self)
actions.append(zoom_to_action)
def zoom_to():
self.zoom_to_layer.emit(dict(layer=self.layer))
zoom_to_action.triggered.connect(zoom_to)
del_action = QAction(IconInstance().DELETE, '删除图层', self)
def del_layer():
Project().remove_layer(self)
del_action.triggered.connect(del_layer)
actions.append(del_action)
return actions
class GridLayer(BasicLayer):
def set_render(self): def set_render(self):
symbol_layer = QgsSimpleLineSymbolLayer() symbol_layer = QgsSimpleLineSymbolLayer()
symbol_layer.setWidth(1 * self.x_res) symbol_layer.setWidth(1)
symbol_layer.setColor(QColor.fromRgb(255,255,255, 200)) symbol_layer.setColor(QColor.fromRgb(255,255,255, 200))
symbol = QgsLineSymbol() symbol = QgsLineSymbol()
symbol.changeSymbolLayer(0, symbol_layer) symbol.changeSymbolLayer(0, symbol_layer)
symbol.setWidthUnit(QgsUnitTypes.RenderMapUnits) # symbol.setWidthUnit(QgsUnitTypes.RenderMapUnits)
render = QgsSingleSymbolRenderer(symbol) render = QgsSingleSymbolRenderer(symbol)
self.lines_layer.setRenderer(render) self.layer.setRenderer(render)
def __init__(self, cell_size, ds): def __init__(self, proj, geo, x_size, y_size, enable=True, name='格网', cell_size=(100,100), style_opts={}):
super().__init__(name, enable, icon='')
self.cell_size = cell_size self.cell_size = cell_size
self.ds = ds
proj = ds.GetProjection()
geo = ds.GetGeoTransform()
self.proj = proj self.proj = proj
self.geo = geo self.geo = geo
self.x_size = ds.RasterXSize self.x_size = x_size
self.y_size = ds.RasterYSize self.y_size = y_size
self.x_min = geo[0] self.x_min = geo[0]
self.y_min = geo[3] self.y_min = geo[3]
@ -195,7 +338,7 @@ class GridLayer:
self.y_lines.pop() self.y_lines.pop()
self.y_lines.append(self.y_max) self.y_lines.append(self.y_max)
crs = QgsCoordinateReferenceSystem() crs = QgsCoordinateReferenceSystem()
crs.createFromString('WKT:{}'.format(ds.GetProjection())) crs.createFromString('WKT:{}'.format(proj))
# print(crs) # print(crs)
lines_layer = QgsVectorLayer('LineString?crs={}'.format(crs.toProj()), 'temp-grid-outline', "memory") lines_layer = QgsVectorLayer('LineString?crs={}'.format(crs.toProj()), 'temp-grid-outline', "memory")
if not lines_layer.isValid(): if not lines_layer.isValid():
@ -214,41 +357,78 @@ class GridLayer:
features.append(line) features.append(line)
lines_layer.addFeatures(features) lines_layer.addFeatures(features)
lines_layer.commitChanges() lines_layer.commitChanges()
self.lines_layer = lines_layer self.layer = lines_layer
self.set_render() self.set_render()
# self.x_lines = [ self.x_min + i * self.x_res for i in range(self.x_size) ]
class RasterLayer(BasicLayer):
def __init__(self, name=None, enable=False, path=None, view_mode=BasicLayer.BOATH_VIEW):
if name is None:
name = os.path.splitext(os.path.basename(path))[0]
super().__init__(name, enable, ':/icons/raster.png', path, BasicLayer.IN_FILE, view_mode)
self.layer = QgsRasterLayer(self.path, self.name)
def compare(self, other:'RasterLayer'):
ds1 = gdal.Open(self.path)
ds2 = gdal.Open(other.path)
if ds1 is None or ds2 is None:
Project().message_box.error('图层打开失败')
return False
if ds1.RasterXSize != ds2.RasterXSize or ds1.RasterYSize != ds2.RasterYSize:
Project().message_box.error('图层尺寸不一致')
return False
return True
@property @property
def grid_layer(self): def geo(self):
return self.lines_layer ds = gdal.Open(self.path)
if ds is None:
return None
geo = ds.GetGeoTransform()
del ds
return geo
def to_dict(self): @property
return { def proj(self):
'cell_size': self.cell_size ds = gdal.Open(self.path)
} if ds is None:
return None
proj = ds.GetProjection()
del ds
return proj
@staticmethod @property
def from_dict(data): def size(self):
return GridLayer() ds = gdal.Open(self.path)
if ds is None:
return None
s = (ds.RasterXSize, ds.RasterYSize)
del ds
return s
class ResultLayer: class VectorLayer(BasicLayer):
pass
POINT = 0
RASTER = 1
def __init__(self, name, parent, layer_type = POINT): class ResultPointLayer(BasicLayer):
self.layer_type = layer_type
def __init__(self, path, name=None, enable = False, proj = None, geo = None):
if name is None:
name = os.path.splitext(os.path.basename(path))[0]
super().__init__(name, enable, icon=':/icons/points.png', path=path, path_mode = BasicLayer.IN_FILE, view_mode=BasicLayer.BOATH_VIEW )
self.data = None self.data = None
self.layer = None self.wkt = proj
self.name = name self.geo = geo
self.path = None
self.wkt = None self.load_point_file()
self.enable = False
self.parent = parent
def save(self): def save(self):
if self.layer_type == ResultLayer.POINT:
with open(self.path, 'w') as f: with open(self.path, 'w') as f:
f.write('x,y,diff,status\n') f.write('x,y,diff,status\n')
for i in range(len(self.data)): for i in range(len(self.data)):
@ -256,32 +436,20 @@ class ResultLayer:
def update(self, data): def update(self, data):
if self.layer_type == ResultLayer.POINT:
row = data['row'] row = data['row']
value = data['value'] value = data['value']
self.data[row][-1] = value self.data[row][-1] = value
self.update_point_layer(row) self.update_point_layer(row)
elif self.layer_type == ResultLayer.RASTER:
pass
def load_file(self, path):
self.path = path
if self.layer_type == ResultLayer.POINT:
self.load_point_file()
elif self.layer_type == ResultLayer.RASTER:
self.load_raster_file()
else:
raise Exception('Unknown layer type')
def format_point_layer(self, layer): def format_point_layer(self, layer):
layer.setLabelsEnabled(True) layer.setLabelsEnabled(True)
lyr = QgsPalLayerSettings() lyr = QgsPalLayerSettings()
lyr.enabled = True lyr.enabled = True
lyr.fieldName = 'fid' lyr.fieldName = 'prob'
lyr.placement = QgsPalLayerSettings.OverPoint lyr.placement = QgsPalLayerSettings.OverPoint
lyr.textNamedStyle = 'Medium' lyr.textNamedStyle = 'Medium'
text_format = QgsTextFormat() text_format = QgsTextFormat()
text_format.color = QColor('#ffffff') text_format.color = QColor.fromRgb(255,0,0)
text_format.background().color = QColor('#000000') text_format.background().color = QColor('#000000')
text_format.buffer().setEnabled(True) text_format.buffer().setEnabled(True)
text_format.buffer().setSize(1) text_format.buffer().setSize(1)
@ -296,8 +464,9 @@ class ResultLayer:
layer.setLabeling(rules) layer.setLabeling(rules)
def set_render(self, layer): def set_render(self, layer):
symbol_change = QgsMarkerSymbol.createSimple({'color': '#ffff00', 'size': 5 * self.parent.xres }) # xres = self.geo[1]
symbol_change.setSizeUnit(QgsUnitTypes.RenderUnit.RenderMetersInMapUnits) symbol_change = QgsMarkerSymbol.createSimple({'color': '#ffff00', 'size': 5 })
symbol_change.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPixels)
category_change = QgsRendererCategory(1, symbol_change,'change') category_change = QgsRendererCategory(1, symbol_change,'change')
symbol_unchange = QgsMarkerSymbol.createSimple({'color': '#00000000', 'size': '0'}) symbol_unchange = QgsMarkerSymbol.createSimple({'color': '#00000000', 'size': '0'})
@ -320,7 +489,7 @@ class ResultLayer:
else: else:
crs = QgsCoordinateReferenceSystem() crs = QgsCoordinateReferenceSystem()
uri = 'Point?crs={}&field=status:integer'.format(crs.toProj()) uri = 'Point?crs={}&field=status:integer&field=prob:string'.format(crs.toProj())
layer = QgsVectorLayer(uri, self.name, "memory") layer = QgsVectorLayer(uri, self.name, "memory")
if not layer.isValid(): if not layer.isValid():
Project().message_box.error('Failed to create layer') Project().message_box.error('Failed to create layer')
@ -334,6 +503,7 @@ class ResultLayer:
point.setId(i) point.setId(i)
point.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(d[0], d[1]))) point.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(d[0], d[1])))
point.setAttribute('status', int(d[-1])) point.setAttribute('status', int(d[-1]))
point.setAttribute('prob', '%.2f'%(d[2]))
# point.setAttribute('id', i) # point.setAttribute('id', i)
features.append(point) features.append(point)
layer.addFeatures(features) layer.addFeatures(features)
@ -357,146 +527,103 @@ class ResultLayer:
# print(feature) # print(feature)
if feature is None: if feature is None:
return return
# print(feature.fields().names())
# self.layer.deleteFeature(feature.id())
# del feature
feature.setAttribute('status', int(self.data[row][-1])) feature.setAttribute('status', int(self.data[row][-1]))
self.layer.updateFeature(feature) self.layer.updateFeature(feature)
self.layer.commitChanges() self.layer.commitChanges()
def load_raster_file(self): def get_actions(self):
ds = gdal.Open(self.path) actions = super().get_actions()
if ds is None: show_in_table = QAction(IconInstance().TABLE, '显示在表格中')
return actions.insert(0, show_in_table)
self.layer = QgsRasterLayer(self.path, self.name)
@staticmethod def show_to_table():
def from_dict(data, parent, root = None): Project().result_table.show_result(self)
result = ResultLayer(data['name'], parent, data['layer_type'])
result.wkt = data['wkt']
if root is not None:
result.load_file(str(Path(root) / data['path']))
else:
result.load_file(data['path'])
return result
def to_dict(self, root=None): show_in_table.triggered.connect(show_to_table)
return {
'name': self.name, return actions
'layer_type': self.layer_type,
'wkt': self.wkt,
'path': self.path if root is None else str(Path(self.path).relative_to(root))
}
# def load_file(self, path): # def load_file(self, path):
class PairLayer: class PairLayer(BasicLayer):
def to_dict(self, root = None): def __init__(self, pth1, pth2) -> 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,
'results': [r.to_dict(root) for r in self.results],
'name': self.name
} self.layers:List[BasicLayer] = []
else:
return {
'pth1': relative_path(self.pth1, root),
'pth2': relative_path(self.pth2, root),
'name': self.name,
'l1_name': self.l1_name,
'l2_name': self.l2_name,
'cell_size': self.cell_size,
'results': [r.to_dict(root) for r in self.results]
}
@staticmethod
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.name = data['name']
for r in data['results']:
layer.results.append(ResultLayer.from_dict(r, layer, root))
# layer.grid_layer = GridLayer.from_dict(data['grid_layer'])
return layer
def save(self):
for r in self.results:
r.save()
def __init__(self, pth1, pth2, cell_size) -> None:
self.pth1 = pth1
self.pth2 = pth2
self.enable = True
self.l1_enable = True
self.l2_enable = True
self.grid_enable = True
self.id = str(uuid.uuid1()) self.id = str(uuid.uuid1())
self.name = '{}-{}'.format(os.path.basename(pth1), os.path.basename(pth2))
self.l1_name = os.path.basename(pth1)
self.l2_name = os.path.basename(pth2)
self.l1_pres = [] # 预处理数据
self.l2_pres = []
self.cell_size = cell_size
self.msg = ''
self.checked = False self.checked = False
self.main_l1 = RasterLayer(path = pth1, enable=True, view_mode=BasicLayer.LEFT_VIEW)
self.main_l2 = RasterLayer(path = pth2, enable=True, view_mode=BasicLayer.RIGHT_VIEW)
self.main_l1.set_layer_parent(self)
self.main_l2.set_layer_parent(self)
self.grid = None
self.cell_size = Project().cell_size
name = os.path.basename(pth1)[:4] + '-' + os.path.basename(pth2)[:4]
# self.layer_update.connect(Project().layer_updated)
self.xsize = 0 super().__init__(name, True, ':/icons/document.png')
self.ysize = 0 self.layer = self.main_l1.layer
self.xres = 0 if self.check():
self.yres = 0 self.geo = self.main_l1.geo
self.proj = self.main_l1.proj
self.wkt = None self.size = self.main_l1.size
self.layers.append(self.main_l1)
self.results:List[ResultLayer] = [] self.layers.append(self.main_l2)
self.grid = GridLayer(self.proj, self.geo, self.size[0], self.size[1], cell_size=Project().cell_size)
self.grid.set_layer_parent(self)
# self.layers.append(self.grid)
def check(self): def check(self):
if self.checked: if self.checked:
return self.checked return self.checked
if not os.path.exists(self.pth1): self.checked = self.main_l1.compare(self.main_l2)
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
self.xsize = ds1.RasterXSize
self.ysize = ds1.RasterYSize
self.xres = ds1.GetGeoTransform()[1]
self.yres = ds1.GetGeoTransform()[5]
self.wkt = ds1.GetProjection()
self.grid_layer = GridLayer(self.cell_size, ds1)
del ds1
del ds2
self.l1 = QgsRasterLayer(self.pth1, self.l1_name)
self.l2 = QgsRasterLayer(self.pth2, self.l2_name)
self.checked = True
return True return True
def add_result_layer(self, result):
result.set_layer_parent(self)
self.layers.append(result)
self.layer_show_update.emit()
self.layer_tree_update.emit()
def has_layer(self, layer):
for ilayer in self.layers:
if ilayer is layer:
return True
return False
def remove_layer(self, layer):
idx = -1
for ilayer in self.layers:
idx += 1
if ilayer is layer:
break
if idx >= len(self.layers):
return
if layer is self.grid or layer is self.main_l1 or layer is self.main_l2:
return
self.layers.pop(idx)
del layer
def to_dict(self):
data=dict(
name=self.name,
enable=self.enable,
pth1=self.main_l1.path,
pth2=self.main_l2.path,
layers=[to_dict(l) for l in self.layers if not (l is self.grid or l is self.main_l1 or l is self.main_l2) ]
)
return data
@staticmethod
def from_dict(data):
player = PairLayer(data['pth1'], data['pth2'])
player.name = data['name']
for layer in data['layers']:
l = from_dict(layer)
l.set_layer_parent(player)
player.layers.append(l)
return player

View File

@ -151,7 +151,7 @@ class Settings(QSettings):
with Settings(Settings.General.PRE) as s: with Settings(Settings.General.PRE) as s:
return s.value('auto_save', True) return s.value('auto_save', True)
@property.setter @auto_save.setter
def auto_save(self, value): def auto_save(self, value):
if isinstance(value, bool): if isinstance(value, bool):
pass pass
@ -168,7 +168,7 @@ class Settings(QSettings):
with Settings(Settings.General.PRE) as s: with Settings(Settings.General.PRE) as s:
return s.value('auto_save_intervel', 30) return s.value('auto_save_intervel', 30)
@property.setter @auto_save_intervel.setter
def auto_save_intervel(self, value): def auto_save_intervel(self, value):
if isinstance(value, int) and value > 0: if isinstance(value, int) and value > 0:
pass pass