From e7bf0b1fae4182838a335c4d03f2c7cee416fa91 Mon Sep 17 00:00:00 2001 From: copper Date: Mon, 23 May 2022 19:23:35 +0800 Subject: [PATCH 1/2] fix the layer tree --- RSCDer.py => ECD.py | 0 log.txt | 10 +- plugins/basic_change/main.py | 50 +-- plugins/export_to/main.py | 6 +- rscder/gui/layertree.py | 190 +++--------- rscder/gui/mainwindow.py | 13 +- rscder/gui/mapcanvas.py | 46 ++- rscder/gui/result.py | 61 ++-- rscder/plugins/basic.py | 2 +- rscder/utils/icons.py | 13 + rscder/utils/misc.py | 1 + rscder/utils/project.py | 585 +++++++++++++++++++++-------------- rscder/utils/setting.py | 4 +- 13 files changed, 501 insertions(+), 480 deletions(-) rename RSCDer.py => ECD.py (100%) create mode 100644 rscder/utils/icons.py diff --git a/RSCDer.py b/ECD.py similarity index 100% rename from RSCDer.py rename to ECD.py diff --git a/log.txt b/log.txt index 8e8333e..32ce7a2 100644 --- a/log.txt +++ b/log.txt @@ -1,6 +1,4 @@ -2022-05-17 16:14:17,007 - root - INFO - lic data:2022-12-01 00:00:00 -2022-05-17 16:14:17,010 - root - INFO - remain_days: 197 -2022-05-17 16:14:18,338 - root - INFO - lic data:2022-12-01 00:00:00 -2022-05-17 16:14:18,338 - root - INFO - remain_days: 197 -2022-05-17 16:35:12,026 - root - INFO - Empty module name -2022-05-17 16:35:13,397 - root - INFO - None +2022-05-23 19:21:48,894 - root - INFO - lic data:2022-12-01 00:00:00 +2022-05-23 19:21:48,899 - root - INFO - remain_days: 191 +2022-05-23 19:21:50,164 - root - INFO - lic data:2022-12-01 00:00:00 +2022-05-23 19:21:50,164 - root - INFO - remain_days: 191 diff --git a/plugins/basic_change/main.py b/plugins/basic_change/main.py index e049520..af8428b 100644 --- a/plugins/basic_change/main.py +++ b/plugins/basic_change/main.py @@ -5,7 +5,7 @@ from rscder.plugins.basic import BasicPlugin from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton from PyQt5.QtCore import pyqtSignal 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 osgeo import gdal, gdal_array from threading import Thread @@ -77,32 +77,19 @@ class BasicMethod(BasicPlugin): basic_diff_method.triggered.connect(self.basic_diff_alg) 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 - - def on_data_load(self, layer_id): + def setup(self): self.basic_diff_method.setEnabled(True) - + def send_message(self, s): self.message_box.info(s) - - def on_result_ok(self, data): - 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) - raster_layer.load_file(data['raster_file']) - 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): + def run_basic_diff_alg(self, layer:PairLayer): - pth1 = layer.pth1 - pth2 = layer.pth2 + pth1 = layer.main_l1.path + pth2 = layer.main_l2.path cell_size = layer.cell_size @@ -118,7 +105,7 @@ class BasicMethod(BasicPlugin): geo = ds1.GetGeoTransform() 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.SetGeoTransform(ds1.GetGeoTransform()) out_ds.SetProjection(ds1.GetProjection()) @@ -155,7 +142,7 @@ class BasicMethod(BasicPlugin): self.message_send.emit('归一化概率中...') 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.SetGeoTransform(ds1.GetGeoTransform()) out_normal_ds.SetProjection(ds1.GetProjection()) @@ -188,7 +175,7 @@ class BasicMethod(BasicPlugin): 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] 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') - self.result_ok.emit({ - 'layer_id': layer.id, - 'csv_file': out_csv, - 'raster_file': out_normal_tif - }) + point_result_lalyer = ResultPointLayer(out_csv, enable=False, proj = layer.proj, geo = layer.geo) + raster_result_layer = RasterLayer(None, True, out_normal_tif, BasicLayer.BOATH_VIEW) + + layer.add_result_layer(point_result_lalyer) + layer.add_result_layer(raster_result_layer) self.message_send.emit('完成计算变化表格') @@ -237,11 +224,8 @@ class BasicMethod(BasicPlugin): if not layer.check(): 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() diff --git a/plugins/export_to/main.py b/plugins/export_to/main.py index 4dd979c..b3fbe5d 100644 --- a/plugins/export_to/main.py +++ b/plugins/export_to/main.py @@ -1,5 +1,5 @@ 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 PyQt5.QtWidgets import QDialog, QHBoxLayout, QFileDialog, QComboBox, QVBoxLayout, QPushButton, QLabel, QLineEdit, QAction from PyQt5.QtGui import QIcon @@ -20,8 +20,8 @@ class ExportDialog(QDialog): result_layer_select.addItem('---', None) for layer in Project().layers.values(): - for result_layer in layer.results: - if result_layer.layer_type == ResultLayer.POINT: + for result_layer in layer.layers: + if isinstance(result_layer, ResultPointLayer): result_layer_select.addItem( layer.name[:5] + '-' + result_layer.name, result_layer) for i in range(result_layer_select.count() - 1): diff --git a/rscder/gui/layertree.py b/rscder/gui/layertree.py index 87dbdfd..63bc22f 100644 --- a/rscder/gui/layertree.py +++ b/rscder/gui/layertree.py @@ -12,7 +12,7 @@ from rscder.utils.project import PairLayer, Project class LayerTree(QtWidgets.QWidget): LAYER_TOOT = 0 - SUB_RASTER = 1 + SUB_LAYERS = 1 RESULT = 2 LEFT_RASTER = 0 RIGHT_RASTER = 1 @@ -32,195 +32,85 @@ class LayerTree(QtWidgets.QWidget): self.tree.customContextMenuRequested.connect(self.right_menu_show) self.root=QTreeWidgetItem(self.tree) self.tree.setHeaderHidden(True) + # self.tree.setHeaderLabels(['图层']) self.root.setText(0,'图层') self.root.setIcon(0,QtGui.QIcon(':/icons/layer.png')) - - + self.tree.expandAll() self.tree.addTopLevelItem(self.root) self.tree.itemChanged.connect(self.onItemChanged) + self.tree.itemExpanded.connect(self.onItemExpanded) + + self._expand = True + self.root.setExpanded(self._expand) layout = QtWidgets.QGridLayout() layout.addWidget(self.tree) self.setLayout(layout) self.setLayoutDirection(Qt.LeftToRight) - self.is_in_add_layer = False + self.is_update_layer = False 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): - if self.is_in_add_layer: + if self.is_update_layer: return if item == self.root: - return - 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) + self._expand = item.isExpanded() return - layer_root.setText(0,layer.name) + if hasattr(item, 'item_update'): + item.item_update(item) - while layer_root.childCount() > 0: - layer_root.removeChild(layer_root.child(0)) - - self.add_sub_layer(layer_root, layer) - self.is_in_add_layer = False + def update_layer(self): + self.is_update_layer = True + self.clear() + for layer_group in Project().layers.values(): + 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): self.tree.clear() self.root = QTreeWidgetItem(self.tree) - self.root.setText(0,'图层') self.tree.addTopLevelItem(self.root) - - def delete_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() - - 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)) - + self.tree.expandAll() + self.root.setText(0,'图层') + self.root.setIcon(0,QtGui.QIcon(':/icons/layer.png')) + self.root.setExpanded(self._expand) def right_menu_show(self, position): rightMenu = QtWidgets.QMenu(self) - # QAction = QtWidgets.QAction(self.menuBar1) + item = self.tree.itemAt(position) self.current_item = item action_manager = get_action_manager() actions = [] data_load_action = action_manager.get_action('&数据加载', 'File') 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: logging.info('nothing') else: if item == self.root: pass - elif item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT: - actions.append(zoom_to_action) - 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) - - + else: + actions.extend(item.get_actions()) + for action in actions: rightMenu.addAction(action) diff --git a/rscder/gui/mainwindow.py b/rscder/gui/mainwindow.py index b052df6..979addc 100644 --- a/rscder/gui/mainwindow.py +++ b/rscder/gui/mainwindow.py @@ -1,6 +1,6 @@ import pdb 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 import QtGui from PyQtAds import QtAds @@ -15,6 +15,8 @@ from rscder.utils.project import Project from rscder.gui.layercombox import LayerCombox class MainWindow(QMainWindow): + closed = pyqtSignal() + def __init__(self, parent=None, **kargs): super().__init__(parent) # self.current_instance = kargs.get('current_instance', 0) @@ -33,11 +35,8 @@ class MainWindow(QMainWindow): self.layer_tree, self.message_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_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.result_box.on_item_click.connect(self.double_map.zoom_to_extent) self.action_manager = ActionManager( self.double_map, @@ -126,7 +125,7 @@ class MainWindow(QMainWindow): set_docker_fixed(self.message_dock) def closeEvent(self, event): - pass + self.closed.emit() def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: Settings.General().size = (a0.size().width(), a0.size().height()) diff --git a/rscder/gui/mapcanvas.py b/rscder/gui/mapcanvas.py index fe9f0ad..43aa9c1 100644 --- a/rscder/gui/mapcanvas.py +++ b/rscder/gui/mapcanvas.py @@ -20,7 +20,7 @@ import tempfile import cv2 import os -from rscder.utils.project import PairLayer, Project +from rscder.utils.project import BasicLayer, PairLayer, Project class DoubleCanvas(QWidget): corr_changed = pyqtSignal(str) @@ -106,18 +106,20 @@ class DoubleCanvas(QWidget): layer_list_2 = [] for layer in layers.values(): if layer.enable: - if layer.grid_enable and self.grid_show: - layer_list_1.append(layer.grid_layer.grid_layer) - layer_list_2.append(layer.grid_layer.grid_layer) - if layer.l1_enable: - layer_list_1.append(layer.l1) - if layer.l2_enable: - layer_list_2.append(layer.l2) - - for result in layer.results: - if result.enable: - layer_list_1.append(result.layer) - layer_list_2.append(result.layer) + if layer.grid.enable and self.grid_show: + layer_list_1.append(layer.grid.layer) + layer_list_2.append(layer.grid.layer) + + for sub_layer in layer.layers: + if sub_layer.enable: + 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) + self.mapcanva1.setLayers(layer_list_1) self.mapcanva2.setLayers(layer_list_2) @@ -149,22 +151,14 @@ class DoubleCanvas(QWidget): self.mapcanva1.refresh() self.mapcanva2.refresh() - def zoom_to_result(self, xydict:dict): - x = xydict['x'] - 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) + def zoom_to_extent(self, extent): + # 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.mapcanva2.set_extent(extent) - def zoom_to_layer(self, layer:str): - layer:PairLayer = Project().layers[layer] - self.mapcanva1.set_extent(layer.l1.extent()) - self.mapcanva2.set_extent(layer.l2.extent()) + def zoom_to_layer(self, layer): + self.mapcanva1.set_extent(layer.extent()) + self.mapcanva2.set_extent(layer.extent()) def layer_changed(self, layer:str): self.add_layer(layer) diff --git a/rscder/gui/result.py b/rscder/gui/result.py index ebd02a4..38bcf9f 100644 --- a/rscder/gui/result.py +++ b/rscder/gui/result.py @@ -4,29 +4,26 @@ from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt,QModelIndex, pyqtSignal from PyQt5.QtGui import QStandardItemModel, QStandardItem from PyQt5.QtWidgets import (QTableWidgetItem, QTableWidget, QMessageBox, QAbstractItemView, QHeaderView, QStyleFactory) - -from rscder.utils.project import PairLayer, Project, ResultLayer +from qgis.core import QgsRectangle +from rscder.utils.project import PairLayer, Project, ResultPointLayer class ResultTable(QtWidgets.QWidget): - on_item_click = pyqtSignal(dict) + on_item_click = pyqtSignal(QgsRectangle) on_item_changed = pyqtSignal(dict) def __init__(self, parent=None): super(ResultTable, self).__init__(parent) # self.tableview = QTableView(self) self.tablewidget = QTableWidget(self) - self.tablewidget.setColumnCount(4) + self.tablewidget.setColumnCount(3) self.tablewidget.setRowCount(0) - self.tablewidget.setHorizontalHeaderLabels(['X', 'Y', '概率', '变化']) + self.tablewidget.setHorizontalHeaderLabels(['变化位置(x,y)', '疑似变化概率', '目视判读']) 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.cellChanged.connect(self.onChanged) - - # self.tablewidget.setModel(self.tableview) - # self.tableview layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.tablewidget) self.setLayout(layout) @@ -36,27 +33,44 @@ class ResultTable(QtWidgets.QWidget): def clear(self): self.tablewidget.clear() + self.tablewidget.setColumnCount(3) + self.tablewidget.setRowCount(0) + self.tablewidget.setHorizontalHeaderLabels(['变化位置(x,y)', '疑似变化概率', '目视判读']) def onChanged(self, row, col): if self.is_in_set_data or self.no_change: return - if col == 3: + if col == 2: self.no_change = True item_idx = row item_status = self.tablewidget.item(row, col).checkState() == Qt.Checked if item_status: self.tablewidget.item(row, col).setBackground(Qt.yellow) + self.tablewidget.item(row, col).setText('YES') else: self.tablewidget.item(row, col).setBackground(Qt.green) + self.tablewidget.item(row, col).setText('NO') # logging # logging.info() self.result.update({'row':item_idx, 'value':item_status}) self.no_change = False - def onDoubleClicked(self, row, col): - x = self.tablewidget.item(row, 0).text() - y = self.tablewidget.item(row, 1).text() - self.on_item_click.emit({'x':float(x), 'y':float(y)}) + def onClicked(self, row, col): + if col != 0: + return + 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): if self.result is None: @@ -70,26 +84,27 @@ class ResultTable(QtWidgets.QWidget): self.save() self.result = result self.clear() - self.set_data(result) - def set_data(self, data:ResultLayer): + self.show_result(result) + + def show_result(self, data:ResultPointLayer): self.is_in_set_data = True - if data.layer_type != ResultLayer.POINT: - return + self.result = data self.tablewidget.setRowCount(len(data.data)) # print(len(data.data)) self.tablewidget.setVerticalHeaderLabels([ str(i+1) for i in range(len(data.data))]) for i, d in enumerate(data.data): - self.tablewidget.setItem(i, 0, QTableWidgetItem(str(d[0]))) # X - self.tablewidget.setItem(i, 1, QTableWidgetItem(str(d[1]))) # Y - self.tablewidget.setItem(i, 2, QTableWidgetItem(str(d[2]))) # 概率 - status_item = QTableWidgetItem('变化') + self.tablewidget.setItem(i, 0, QTableWidgetItem('%.3f,%.3f'%(d[0], d[1]))) # X + self.tablewidget.setItem(i, 1, QTableWidgetItem(str(d[2]))) # Y + status_item = QTableWidgetItem('') if d[3] == 0: status_item.setBackground(Qt.green) status_item.setCheckState(Qt.Unchecked) + status_item.setText('NO') elif d[3] == 1: status_item.setBackground(Qt.yellow) 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.resizeRowsToContents() self.tablewidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) diff --git a/rscder/plugins/basic.py b/rscder/plugins/basic.py index 905cc32..e449f68 100644 --- a/rscder/plugins/basic.py +++ b/rscder/plugins/basic.py @@ -34,7 +34,7 @@ class BasicPlugin(QObject): self.project = ctx['project'] self.mainwindow = ctx['mainwindow'] 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) diff --git a/rscder/utils/icons.py b/rscder/utils/icons.py new file mode 100644 index 0000000..e5e494a --- /dev/null +++ b/rscder/utils/icons.py @@ -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') \ No newline at end of file diff --git a/rscder/utils/misc.py b/rscder/utils/misc.py index 9061769..7c29923 100644 --- a/rscder/utils/misc.py +++ b/rscder/utils/misc.py @@ -1,4 +1,5 @@ from functools import wraps + def singleton(cls): _instance = {} diff --git a/rscder/utils/project.py b/rscder/utils/project.py index 86dcf41..28b84d0 100644 --- a/rscder/utils/project.py +++ b/rscder/utils/project.py @@ -1,16 +1,21 @@ +from cgitb import enable from collections import OrderedDict +import inspect import os from pathlib import Path +import shutil from threading import Thread from time import sleep, time from typing import Dict, List import uuid import numpy as np from osgeo import gdal, gdal_array +from rscder.utils.icons import IconInstance 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 PyQt5.QtCore import QObject, pyqtSignal -from PyQt5.QtGui import QColor +from PyQt5.QtCore import QObject, pyqtSignal, Qt, QThread +from PyQt5.QtWidgets import QTreeWidgetItem, QAction +from PyQt5.QtGui import QColor, QIcon import yaml from .misc import singleton @@ -20,27 +25,33 @@ def relative_path(path: str, root:str) -> str: @singleton class Project(QObject): + instance:'Project' + project_init = pyqtSignal(bool) - layer_load = pyqtSignal(str) + # layer_load = pyqtSignal() + layer_tree_update = pyqtSignal() + layer_show_update = pyqtSignal() ABSOLUTE_MODE = 'absolute' RELATIVE_MODE = 'relative' 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.run() + t.start() def auto_save(self): # pre = time() - while True: - if Settings.General().auto_save and self.is_init: - self.save() - if self.is_closed: - break - sleep(Settings.General().auto_save_intervel) + self.in_save = True + self.save() + self.in_save = False - def __init__(self, parent=None): super().__init__(parent) @@ -53,9 +64,17 @@ class Project(QObject): self.layers:Dict[str, PairLayer] = OrderedDict() self.current_layer = None self.is_closed = False + self.in_save = False + + def set_close(): + self.is_closed = True + + parent.closed.connect(set_close) + self.run_auto_save() - def connect(self, pair_canvas, + def connect(self, + pair_canvas, layer_tree, message_box, result_table): @@ -63,21 +82,14 @@ class Project(QObject): self.layer_tree = layer_tree self.message_box = message_box self.result_table = result_table - - self.layer_load.connect(layer_tree.add_layer) - self.layer_load.connect(pair_canvas.add_layer) - - def change_result(self, layer_id, result_id, data): - 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 + IconInstance(self) + self.layer_tree_update.connect(layer_tree.update_layer) + self.layer_show_update.connect(pair_canvas.update_layer) + self.layer_tree_update.connect(self.run_auto_save) + self.layer_show_update.connect(self.run_auto_save) def setup(self, path = None, name = None): - self.is_init = True + if path is not None: self.root = path if name is None: @@ -91,8 +103,8 @@ class Project(QObject): pass else: self.load() - # self.cmi_dir = str(Path(self.root)/'cmi') - # self.project_created.emit() + + self.is_init = True self.project_init.emit(True) def save(self): @@ -101,20 +113,18 @@ class Project(QObject): 'max_memory': self.max_memory, 'max_threads': self.max_threads, '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(): - layer.save() + # for layer in self.layers.values(): + # layer.save() with open(self.file, 'w') as f: yaml.safe_dump(data_dict, f) - # yaml.safe_dump(data_dict, open(self.file, 'w')) - def clear(self): ''' clear all layers ''' - self.layers = dict() + self.layers:Dict[str, PairLayer] = dict() self.layer_tree.clear() self.pair_canvas.clear() self.message_box.clear() @@ -133,48 +143,181 @@ class Project(QObject): self.root = data['root'] self.layers = dict() 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(): 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: self.message_box.error(str(e)) self.clear() - def add_layer(self, pth1, pth2): - # self.root = str(Path(pth1).parent) - - player = PairLayer(pth1, pth2, self.cell_size) - if player.check(): - # self.layers.append(player) - self.layers[player.id] = player - self.layer_load.emit(player.id) - else: - self.message_box.error(player.msg) -class GridLayer: + 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): + player = PairLayer(pth1, pth2) + if player.check(): + self.layers[player.id] = player + self.layer_show_update.emit() + self.layer_tree_update.emit() + self.pair_canvas.zoom_to_layer(player.main_l1.layer) + else: + self.message_box.error(f'{player.name} and {player.name} are not same size') + +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): symbol_layer = QgsSimpleLineSymbolLayer() - symbol_layer.setWidth(1 * self.x_res) + symbol_layer.setWidth(1) symbol_layer.setColor(QColor.fromRgb(255,255,255, 200)) symbol = QgsLineSymbol() symbol.changeSymbolLayer(0, symbol_layer) - symbol.setWidthUnit(QgsUnitTypes.RenderMapUnits) + # symbol.setWidthUnit(QgsUnitTypes.RenderMapUnits) render = QgsSingleSymbolRenderer(symbol) - self.lines_layer.setRenderer(render) + self.layer.setRenderer(render) - def __init__(self, cell_size, ds): - self.cell_size = cell_size - self.ds = ds + def __init__(self, proj, geo, x_size, y_size, enable=True, name='格网', cell_size=(100,100), style_opts={}): - proj = ds.GetProjection() - geo = ds.GetGeoTransform() + super().__init__(name, enable, icon='') + + self.cell_size = cell_size self.proj = proj self.geo = geo - self.x_size = ds.RasterXSize - self.y_size = ds.RasterYSize + self.x_size = x_size + self.y_size = y_size self.x_min = geo[0] self.y_min = geo[3] @@ -195,7 +338,7 @@ class GridLayer: self.y_lines.pop() self.y_lines.append(self.y_max) crs = QgsCoordinateReferenceSystem() - crs.createFromString('WKT:{}'.format(ds.GetProjection())) + crs.createFromString('WKT:{}'.format(proj)) # print(crs) lines_layer = QgsVectorLayer('LineString?crs={}'.format(crs.toProj()), 'temp-grid-outline', "memory") if not lines_layer.isValid(): @@ -214,74 +357,99 @@ class GridLayer: features.append(line) lines_layer.addFeatures(features) lines_layer.commitChanges() - self.lines_layer = lines_layer + self.layer = lines_layer 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 + def geo(self): + ds = gdal.Open(self.path) + if ds is None: + return None + geo = ds.GetGeoTransform() + del ds + return geo @property - def grid_layer(self): - return self.lines_layer - - def to_dict(self): - return { - 'cell_size': self.cell_size - } + def proj(self): + ds = gdal.Open(self.path) + if ds is None: + return None + proj = ds.GetProjection() + del ds + return proj - @staticmethod - def from_dict(data): - return GridLayer() + @property + def size(self): + 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): - self.layer_type = layer_type +class ResultPointLayer(BasicLayer): + + 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.layer = None - self.name = name - self.path = None - self.wkt = None - self.enable = False - self.parent = parent + self.wkt = proj + self.geo = geo + + self.load_point_file() def save(self): - if self.layer_type == ResultLayer.POINT: - with open(self.path, 'w') as f: - f.write('x,y,diff,status\n') - for i in range(len(self.data)): - f.write('{},{},{},{}\n'.format(self.data[i][0], self.data[i][1], self.data[i][2], int(self.data[i][3]))) + + with open(self.path, 'w') as f: + f.write('x,y,diff,status\n') + for i in range(len(self.data)): + f.write('{},{},{},{}\n'.format(self.data[i][0], self.data[i][1], self.data[i][2], int(self.data[i][3]))) def update(self, data): - if self.layer_type == ResultLayer.POINT: - row = data['row'] - value = data['value'] - self.data[row][-1] = value - 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') + row = data['row'] + value = data['value'] + self.data[row][-1] = value + self.update_point_layer(row) def format_point_layer(self, layer): layer.setLabelsEnabled(True) lyr = QgsPalLayerSettings() lyr.enabled = True - lyr.fieldName = 'fid' + lyr.fieldName = 'prob' lyr.placement = QgsPalLayerSettings.OverPoint lyr.textNamedStyle = 'Medium' text_format = QgsTextFormat() - text_format.color = QColor('#ffffff') + text_format.color = QColor.fromRgb(255,0,0) text_format.background().color = QColor('#000000') text_format.buffer().setEnabled(True) text_format.buffer().setSize(1) @@ -296,8 +464,9 @@ class ResultLayer: layer.setLabeling(rules) def set_render(self, layer): - symbol_change = QgsMarkerSymbol.createSimple({'color': '#ffff00', 'size': 5 * self.parent.xres }) - symbol_change.setSizeUnit(QgsUnitTypes.RenderUnit.RenderMetersInMapUnits) + # xres = self.geo[1] + symbol_change = QgsMarkerSymbol.createSimple({'color': '#ffff00', 'size': 5 }) + symbol_change.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPixels) category_change = QgsRendererCategory(1, symbol_change,'change') symbol_unchange = QgsMarkerSymbol.createSimple({'color': '#00000000', 'size': '0'}) @@ -320,7 +489,7 @@ class ResultLayer: else: 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") if not layer.isValid(): Project().message_box.error('Failed to create layer') @@ -334,6 +503,7 @@ class ResultLayer: point.setId(i) point.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(d[0], d[1]))) point.setAttribute('status', int(d[-1])) + point.setAttribute('prob', '%.2f'%(d[2])) # point.setAttribute('id', i) features.append(point) layer.addFeatures(features) @@ -357,146 +527,103 @@ class ResultLayer: # print(feature) if feature is None: return - # print(feature.fields().names()) - # self.layer.deleteFeature(feature.id()) - # del feature feature.setAttribute('status', int(self.data[row][-1])) self.layer.updateFeature(feature) self.layer.commitChanges() - def load_raster_file(self): - ds = gdal.Open(self.path) - if ds is None: - return - self.layer = QgsRasterLayer(self.path, self.name) + def get_actions(self): + actions = super().get_actions() + show_in_table = QAction(IconInstance().TABLE, '显示在表格中') + actions.insert(0, show_in_table) - @staticmethod - def from_dict(data, parent, root = None): - 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 show_to_table(): + Project().result_table.show_result(self) + + show_in_table.triggered.connect(show_to_table) - def to_dict(self, root=None): - return { - 'name': self.name, - 'layer_type': self.layer_type, - 'wkt': self.wkt, - 'path': self.path if root is None else str(Path(self.path).relative_to(root)) - } + return actions # def load_file(self, path): -class PairLayer: +class PairLayer(BasicLayer): - 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, - 'results': [r.to_dict(root) for r in self.results], - 'name': self.name - - } - 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 + def __init__(self, pth1, pth2) -> None: + + self.layers:List[BasicLayer] = [] 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.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 - self.ysize = 0 - self.xres = 0 - self.yres = 0 - - self.wkt = None - - self.results:List[ResultLayer] = [] - + super().__init__(name, True, ':/icons/document.png') + self.layer = self.main_l1.layer + if self.check(): + self.geo = self.main_l1.geo + self.proj = self.main_l1.proj + self.size = self.main_l1.size + self.layers.append(self.main_l1) + 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): if self.checked: return self.checked - if not os.path.exists(self.pth1): - self.msg = '图层1不存在' - return False - if not os.path.exists(self.pth2): - self.msg = '图层2不存在' - return False + self.checked = self.main_l1.compare(self.main_l2) + 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 - ds1 = gdal.Open(self.pth1) - ds2 = gdal.Open(self.pth2) - if ds1 is None or ds2 is None: - self.msg = '图层打开失败' - return False + if layer is self.grid or layer is self.main_l1 or layer is self.main_l2: + return - if ds1.RasterXSize != ds2.RasterXSize or ds1.RasterYSize != ds2.RasterYSize: - self.msg = '图层尺寸不一致' - return False + self.layers.pop(idx) + del layer - self.xsize = ds1.RasterXSize - self.ysize = ds1.RasterYSize + 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 + - 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 \ No newline at end of file + @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 \ No newline at end of file diff --git a/rscder/utils/setting.py b/rscder/utils/setting.py index 8ecd3d4..9eca375 100644 --- a/rscder/utils/setting.py +++ b/rscder/utils/setting.py @@ -151,7 +151,7 @@ class Settings(QSettings): with Settings(Settings.General.PRE) as s: return s.value('auto_save', True) - @property.setter + @auto_save.setter def auto_save(self, value): if isinstance(value, bool): pass @@ -168,7 +168,7 @@ class Settings(QSettings): with Settings(Settings.General.PRE) as s: return s.value('auto_save_intervel', 30) - @property.setter + @auto_save_intervel.setter def auto_save_intervel(self, value): if isinstance(value, int) and value > 0: pass From 6030c8a73e8e5c776839e672132cd363ee1a5d6e Mon Sep 17 00:00:00 2001 From: copper Date: Mon, 23 May 2022 20:22:38 +0800 Subject: [PATCH 2/2] fix current bugs --- log.txt | 8 ++++---- rscder/gui/result.py | 2 +- rscder/utils/project.py | 41 +++++++++++++++++++++++++++-------------- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/log.txt b/log.txt index 32ce7a2..d59c2a5 100644 --- a/log.txt +++ b/log.txt @@ -1,4 +1,4 @@ -2022-05-23 19:21:48,894 - root - INFO - lic data:2022-12-01 00:00:00 -2022-05-23 19:21:48,899 - root - INFO - remain_days: 191 -2022-05-23 19:21:50,164 - root - INFO - lic data:2022-12-01 00:00:00 -2022-05-23 19:21:50,164 - root - INFO - remain_days: 191 +2022-05-23 19:52:34,008 - root - INFO - lic data:2022-12-01 00:00:00 +2022-05-23 19:52:34,012 - root - INFO - remain_days: 191 +2022-05-23 19:52:35,282 - root - INFO - lic data:2022-12-01 00:00:00 +2022-05-23 19:52:35,282 - root - INFO - remain_days: 191 diff --git a/rscder/gui/result.py b/rscder/gui/result.py index 38bcf9f..82765fa 100644 --- a/rscder/gui/result.py +++ b/rscder/gui/result.py @@ -94,7 +94,7 @@ class ResultTable(QtWidgets.QWidget): self.tablewidget.setVerticalHeaderLabels([ str(i+1) for i in range(len(data.data))]) for i, d in enumerate(data.data): self.tablewidget.setItem(i, 0, QTableWidgetItem('%.3f,%.3f'%(d[0], d[1]))) # X - self.tablewidget.setItem(i, 1, QTableWidgetItem(str(d[2]))) # Y + self.tablewidget.setItem(i, 1, QTableWidgetItem('%.2f'%d[2])) # Y status_item = QTableWidgetItem('') if d[3] == 0: status_item.setBackground(Qt.green) diff --git a/rscder/utils/project.py b/rscder/utils/project.py index 28b84d0..bbae413 100644 --- a/rscder/utils/project.py +++ b/rscder/utils/project.py @@ -15,7 +15,7 @@ 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 PyQt5.QtCore import QObject, pyqtSignal, Qt, QThread from PyQt5.QtWidgets import QTreeWidgetItem, QAction -from PyQt5.QtGui import QColor, QIcon +from PyQt5.QtGui import QColor, QIcon, QFont import yaml from .misc import singleton @@ -265,7 +265,7 @@ class BasicLayer(QObject): def item_update(self, item:QTreeWidgetItem): # item = self._item - print('start update') + # print('start update') self.name = item.text(0) self._expand = item.isExpanded() pre = self.enable @@ -273,7 +273,7 @@ class BasicLayer(QObject): if pre != cur: self.enable = cur self.layer_show_update.emit() - print('end update') + # print('end update') def set_layer_parent(self, layer): self.layer_parent = layer @@ -326,13 +326,13 @@ class GridLayer(BasicLayer): self.x_max = self.x_min + self.x_res * self.x_size self.y_max = self.y_min + self.y_res * self.y_size self.x_lines = [] - for xi in range(self.x_size // self.cell_size[0]): + for xi in range(self.x_size // self.cell_size[0] +1): self.x_lines.append(self.x_min + self.x_res * xi * self.cell_size[0]) if self.x_lines[-1] == self.x_max: self.x_lines.pop() self.x_lines.append(self.x_max) self.y_lines = [] - for yi in range(self.y_size // self.cell_size[1]): + for yi in range(self.y_size // self.cell_size[1]+1): self.y_lines.append(self.y_min + self.y_res * yi * self.cell_size[1]) if self.y_lines[-1] == self.y_max: self.y_lines.pop() @@ -438,7 +438,7 @@ class ResultPointLayer(BasicLayer): def update(self, data): row = data['row'] value = data['value'] - self.data[row][-1] = value + self.data[row][-1] = int(value) self.update_point_layer(row) def format_point_layer(self, layer): @@ -447,7 +447,9 @@ class ResultPointLayer(BasicLayer): lyr.enabled = True lyr.fieldName = 'prob' lyr.placement = QgsPalLayerSettings.OverPoint - lyr.textNamedStyle = 'Medium' + lyr.xOffset = 2 + lyr.yOffset = -2 + lyr.textFont = QFont('Times New Roman', 16) text_format = QgsTextFormat() text_format.color = QColor.fromRgb(255,0,0) text_format.background().color = QColor('#000000') @@ -465,11 +467,11 @@ class ResultPointLayer(BasicLayer): def set_render(self, layer): # xres = self.geo[1] - symbol_change = QgsMarkerSymbol.createSimple({'color': '#ffff00', 'size': 5 }) - symbol_change.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPixels) + symbol_change = QgsMarkerSymbol.createSimple({'color': '#ffff00', 'size': 2 }) + symbol_change.setSizeUnit(QgsUnitTypes.RenderUnit.RenderMillimeters) category_change = QgsRendererCategory(1, symbol_change,'change') - symbol_unchange = QgsMarkerSymbol.createSimple({'color': '#00000000', 'size': '0'}) + symbol_unchange = QgsMarkerSymbol.createSimple({'color': '#00000000', 'size': 0}) category_unchange = QgsRendererCategory(0, symbol_unchange, 'unchange') render = QgsCategorizedSymbolRenderer('status', [category_change, category_unchange]) @@ -503,7 +505,10 @@ class ResultPointLayer(BasicLayer): point.setId(i) point.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(d[0], d[1]))) point.setAttribute('status', int(d[-1])) - point.setAttribute('prob', '%.2f'%(d[2])) + if d[-1] == 0: + point.setAttribute('prob', '') + else: + point.setAttribute('prob', '%.2f'%(d[2])) # point.setAttribute('id', i) features.append(point) layer.addFeatures(features) @@ -521,13 +526,21 @@ class ResultPointLayer(BasicLayer): feature = self.layer.getFeature(i+1) if feature is None: continue - feature.setAttribute('status', d[-1]) + feature.setAttribute('status', int(d[-1])) + if d[-1] == 0: + feature.setAttribute('prob', '') + else: + feature.setAttribute('prob', '%.2f'%(d[2])) else: feature = self.layer.getFeature(row+1) # print(feature) if feature is None: return feature.setAttribute('status', int(self.data[row][-1])) + if self.data[row][-1] == 0: + feature.setAttribute('prob', '') + else: + feature.setAttribute('prob', '%.2f'%(self.data[row][2])) self.layer.updateFeature(feature) self.layer.commitChanges() @@ -582,7 +595,7 @@ class PairLayer(BasicLayer): def add_result_layer(self, result): result.set_layer_parent(self) - self.layers.append(result) + self.layers.insert(0, result) self.layer_show_update.emit() self.layer_tree_update.emit() @@ -625,5 +638,5 @@ class PairLayer(BasicLayer): for layer in data['layers']: l = from_dict(layer) l.set_layer_parent(player) - player.layers.append(l) + player.layers.insert(0,l) return player \ No newline at end of file