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-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

View File

@ -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()

View File

@ -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):

View File

@ -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)

View File

@ -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())

View File

@ -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)

View File

@ -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)

View File

@ -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)

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
def singleton(cls):
_instance = {}

View File

@ -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
@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:
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