diff --git a/run.py b/RSCDer.py similarity index 100% rename from run.py rename to RSCDer.py diff --git a/build.bat b/build.bat index 85084c1..7aea222 100644 --- a/build.bat +++ b/build.bat @@ -1,4 +1,5 @@ -nuitka run.py --standalone --plugin-enable=qt-plugins --plugin-enable=numpy --show-progress --include-package=qgis --plugin-enable=pylint-warnings --output-dir=package --windows-disable-console --windows-icon-from-ico=logo.ico --no-pyi-file +nuitka RSCDer.py --standalone --plugin-enable=qt-plugins --plugin-enable=numpy --show-progress --include-package=qgis --plugin-enable=pylint-warnings --output-dir=package --windows-disable-console --windows-icon-from-ico=logo.ico --no-pyi-file +@REM nuitka keygen.py --standalone --plugin-enable=qt-plugins --plugin-enable=numpy --show-progress --include-package=qgis --plugin-enable=pylint-warnings --output-dir=package --windows-disable-console --windows-icon-from-ico=logo.ico --no-pyi-file REM Win7 with console diff --git a/icons/logo.png b/icons/logo.png new file mode 100644 index 0000000..79985f3 Binary files /dev/null and b/icons/logo.png differ diff --git a/icons/splash.png b/icons/splash.png index 82266d9..5b17fa0 100644 Binary files a/icons/splash.png and b/icons/splash.png differ diff --git a/keygen.py b/keygen.py new file mode 100644 index 0000000..c6ed530 --- /dev/null +++ b/keygen.py @@ -0,0 +1,9 @@ +from rscder.gui.keygen import LicenseGen +from PyQt5.QtWidgets import QApplication +import sys + +if __name__ == "__main__": + app =QApplication(sys.argv) + license = LicenseGen() + license.show() + sys.exit(app.exec_()) \ No newline at end of file diff --git a/lic/license.lic b/lic/license.lic index 175477c..02a42fd 100644 --- a/lic/license.lic +++ b/lic/license.lic @@ -1 +1 @@ -pGZJMmJtule8fwDCz4mnyHoQa7N6pl5GRdLqfoXREBqG4Xb1jbvgf7RmC8f1+sNpiCFSIt7NgvU362tKhB5UBXn/vUAadG1lOGC70dUhprGzBoqJN7VkAHkNGg0XjoE8H0SCVynr8To7ciwcnmK6HJXre6i+mBdTjACmKseTMlWp480XOt7uHysltORbTA3J \ No newline at end of file +IieXktda+1nRK9zLwe87uPPn2VpCwmUrEOPfyenaW/Sek70/CqqbCr7nangL1+pVXSkzDELia7Qq8e+pDMuHCXzxyJOALRj4j3bhFVExwqSTLuXwdev1e26nr7vnECl7H0SCVynr8To7ciwcnmK6HJXre6i+mBdTjACmKseTMlWp480XOt7uHysltORbTA3J \ No newline at end of file diff --git a/lic_t.lic b/lic_t.lic new file mode 100644 index 0000000..02a42fd --- /dev/null +++ b/lic_t.lic @@ -0,0 +1 @@ +IieXktda+1nRK9zLwe87uPPn2VpCwmUrEOPfyenaW/Sek70/CqqbCr7nangL1+pVXSkzDELia7Qq8e+pDMuHCXzxyJOALRj4j3bhFVExwqSTLuXwdev1e26nr7vnECl7H0SCVynr8To7ciwcnmK6HJXre6i+mBdTjACmKseTMlWp480XOt7uHysltORbTA3J \ No newline at end of file diff --git a/logo.ico b/logo.ico index a468498..d41874a 100644 Binary files a/logo.ico and b/logo.ico differ diff --git a/plugins/about/main.py b/plugins/about/main.py index a6e0e4e..0847560 100644 --- a/plugins/about/main.py +++ b/plugins/about/main.py @@ -63,6 +63,6 @@ class AboutPlugin(BasicPlugin): menu.addAction(action) def on_about(self): - print('on_about') + # print('on_about') dialog = AboutDialog(self.ctx['mainwindow']) dialog.show() \ No newline at end of file diff --git a/plugins/basic_change/main.py b/plugins/basic_change/main.py index c206c33..f111d0a 100644 --- a/plugins/basic_change/main.py +++ b/plugins/basic_change/main.py @@ -10,7 +10,7 @@ from rscder.gui.layercombox import LayerCombox from osgeo import gdal, gdal_array from threading import Thread import numpy as np - +from basic_change.otsu import OTSU class MyDialog(QDialog): def __init__(self, parent=None): @@ -154,7 +154,7 @@ class BasicMethod(BasicPlugin): out_normal_tif = os.path.join(out, 'diff_0_255.tif') out_normal_ds = driver.Create(out_normal_tif, xsize, ysize, 1, gdal.GDT_Byte) - + hist = np.zeros(256, dtype=np.int32) for j in range(yblocks): block_xy = (0, j * cell_size[1]) block_size = (xsize, cell_size[1]) @@ -164,7 +164,13 @@ class BasicMethod(BasicPlugin): block_data = (block_data - min_diff) / (max_diff - min_diff) * 255 block_data = block_data.astype(np.uint8) out_normal_ds.GetRasterBand(1).WriteArray(block_data, *block_xy) + hist_t, _ = np.histogram(block_data, bins=256) + hist += hist_t + self.gap = OTSU(hist) + + self.message_send.emit('OTSU:' + str(self.gap)) + out_normal_ds.FlushCache() del out_normal_ds self.message_send.emit('完成归一化概率') @@ -194,7 +200,7 @@ class BasicMethod(BasicPlugin): center_y = j * cell_size[1] + cell_size[1] // 2 center_x = center_x * geo[1] + geo [0] center_y = center_y * geo[5] + geo [3] - f.write(f'{center_x},{center_y},{block_data_xy.mean() / 255},1\n') + f.write(f'{center_x},{center_y},{block_data_xy.mean() / 255 * 100},1\n') self.result_ok.emit({ diff --git a/plugins/export_to/__init__.py b/plugins/export_to/__init__.py new file mode 100644 index 0000000..00be9b3 --- /dev/null +++ b/plugins/export_to/__init__.py @@ -0,0 +1 @@ +from export_to.main import * \ No newline at end of file diff --git a/plugins/export_to/main.py b/plugins/export_to/main.py new file mode 100644 index 0000000..90f7558 --- /dev/null +++ b/plugins/export_to/main.py @@ -0,0 +1,100 @@ +import shutil +from rscder.utils.project import Project, PairLayer, ResultLayer +from rscder.plugins.basic import BasicPlugin +from PyQt5.QtWidgets import QDialog, QHBoxLayout, QFileDialog, QComboBox, QVBoxLayout, QPushButton, QLabel, QLineEdit, QAction +from PyQt5.QtGui import QIcon +class ExportDialog(QDialog): + + def __init__(self, parent=None): + super().__init__(parent) + + self.setWindowTitle('Export') + self.setWindowIcon(QIcon(":/icons/logo.png")) + + self.out_path = None + self.result_layer = None + + result_layer_select_label = QLabel('选择结果:') + + result_layer_select = QComboBox(self) + + result_layer_select.addItem('---', None) + for layer in Project().layers.values(): + for result_layer in layer.results: + if result_layer.layer_type == ResultLayer.POINT: + result_layer_select.addItem( layer.name[5:] + '-' + result_layer.name, result_layer) + + for i in range(result_layer_select.count() - 1): + result_layer_select.setItemIcon(i + 1, QIcon(":/icons/layer.png")) + + + def on_result_layer_select(index): + self.result_layer = result_layer_select.currentData() + + result_layer_select.currentIndexChanged.connect(on_result_layer_select) + + out_path_label = QLabel('输出路径:') + out_path_text = QLineEdit(self) + out_path_text.setReadOnly(True) + out_path_text.setPlaceholderText('选择输出路径') + + def on_out_path_btn(): + select_file = QFileDialog.getSaveFileName(self, '选择输出路径', '', '*.txt') + if select_file[0]: + out_path_text.setText(select_file[0]) + self.out_path = select_file[0] + out_path_btn = QPushButton('...', self) + out_path_btn.clicked.connect(on_out_path_btn) + + ok_btn = QPushButton('OK', self) + ok_btn.clicked.connect(self.accept) + cancel_btn = QPushButton('Cancel', self) + cancel_btn.clicked.connect(self.reject) + + + hbox1 = QHBoxLayout() + hbox1.addWidget(result_layer_select_label) + hbox1.addWidget(result_layer_select) + + hbox2 = QHBoxLayout() + hbox2.addWidget(out_path_label) + hbox2.addWidget(out_path_text) + hbox2.addWidget(out_path_btn) + + hbox3 = QHBoxLayout() + hbox3.addWidget(ok_btn) + hbox3.addWidget(cancel_btn) + + vbox = QVBoxLayout() + vbox.addLayout(hbox1) + vbox.addLayout(hbox2) + vbox.addLayout(hbox3) + + self.setLayout(vbox) + +class ExportPlugin(BasicPlugin): + + @staticmethod + def info(): + return { + 'name': 'Export', + 'description': 'Export to other format', + 'author': 'RSCDER', + } + + def set_action(self): + self.export_txt = QAction(QIcon(":/icons/document.png"), '导出为 Arcgis 兼容的TXT', self.mainwindow) + self.export_txt.triggered.connect(self.export_txt_action) + + self.ctx['postop_menu'].addAction(self.export_txt) + + self.ctx['toolbar'].addAction(self.export_txt) + + def export_txt_action(self): + dialog = ExportDialog(self.mainwindow) + if dialog.exec_(): + result = dialog.result_layer + out = dialog.out_path + if result: + shutil.copy(result.path, out) + self.message_box.info('导出成功') \ No newline at end of file diff --git a/plugins/plugins.yaml b/plugins/plugins.yaml index c94f978..11706d8 100644 --- a/plugins/plugins.yaml +++ b/plugins/plugins.yaml @@ -3,12 +3,19 @@ enabled: true module: about name: "\u5173\u4E8E" - path: ./plugin-build\about + path: ./plugin\about version: 1.0.0 - author: RSCDER description: BasicMethod enabled: true module: basic_change name: BasicMethod - path: ./plugin-build\basic_change + path: ./plugin\basic_change version: 1.0.0 +- author: RSCDER + description: ExportTo + enabled: true + module: export_to + name: ExportTo + path: ./plugin\export_to + version: 1.0.0 \ No newline at end of file diff --git a/res.qrc b/res.qrc index 5664d29..7bd32c7 100644 --- a/res.qrc +++ b/res.qrc @@ -28,7 +28,7 @@ icons\zoom_out.png icons\zoom_to.png icons\load.svg - icons\logo.svg + icons\logo.png icons\model.svg icons\ok.svg icons\outline.svg diff --git a/rscder/gui/keygen.py b/rscder/gui/keygen.py new file mode 100644 index 0000000..ad4d27c --- /dev/null +++ b/rscder/gui/keygen.py @@ -0,0 +1,83 @@ +from PyQt5.QtWidgets import QDialog, QLineEdit, QDateTimeEdit, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTextEdit, QFileDialog, QMessageBox +from PyQt5 import QtCore +from PyQt5.QtGui import QIcon + +from rscder.utils.license import LicenseHelper +import re +class LicenseGen(QDialog): + + def __init__(self, parent = None, flags = QtCore.Qt.WindowFlags() ) -> None: + super().__init__(parent, flags) + + self.setWindowTitle("License Generator") + self.setWindowIcon(QIcon(':/icons/logo.png')) + + mac_address_label = QLabel("MAC Address:") + self.mac_address_text = QLineEdit() + + hbox1 = QHBoxLayout() + hbox1.addWidget(mac_address_label) + hbox1.addWidget(self.mac_address_text) + + end_date_label = QLabel("End Date:") + self.end_date_text = QDateTimeEdit() + + hbox2 = QHBoxLayout() + hbox2.addWidget(end_date_label) + hbox2.addWidget(self.end_date_text) + + + self.license_file_path_text = QLineEdit() + self.license_file_path_text.setReadOnly(True) + + btn_open = QPushButton("Open") + btn_open.clicked.connect(self.open_file) + + hbox3 = QHBoxLayout() + hbox3.addWidget(btn_open) + hbox3.addWidget(self.license_file_path_text) + # hbox3.addWidget(btn_open) + + + self.btn_generate = QPushButton("Generate") + self.btn_generate.clicked.connect(self.generate_license) + + self.btn_cancel = QPushButton("Cancel") + self.btn_cancel.clicked.connect(self.reject) + + hbox4 = QHBoxLayout() + hbox4.addWidget(self.btn_generate, alignment = QtCore.Qt.AlignRight, stretch= 0) + hbox4.addWidget(self.btn_cancel, alignment = QtCore.Qt.AlignRight, stretch= 0) + + vbox = QVBoxLayout() + vbox.addLayout(hbox1) + vbox.addLayout(hbox2) + vbox.addLayout(hbox3) + vbox.addLayout(hbox4) + + self.setLayout(vbox) + + def open_file(self) -> None: + file_path, _ = QFileDialog.getSaveFileName(self, "Save License File", "", "License Files (*.lic)") + if file_path: + self.license_file_path_text.setText(file_path) + + def isValidMac(self,mac): + if re.match(r"^\s*([0-9a-fA-F]{2,2}:){5,5}[0-9a-fA-F]{2,2}\s*$", mac): + return True + return False + + + def generate_license(self) -> None: + if self.mac_address_text.text() and self.license_file_path_text.text() and \ + self.end_date_text.dateTime().isValid(): + if not self.isValidMac(self.mac_address_text.text()): + QMessageBox.warning(self, "Warning", "Invalid MAC Address") + + end_date = self.end_date_text.dateTime().toPyDateTime().strftime("%Y-%m-%d %H:%M:%S") + + lic = LicenseHelper().generate_license(end_date, self.mac_address_text.text()) + with open(self.license_file_path_text.text(), 'w') as f: + f.write(lic[::-1]) + + QMessageBox.information(self, "Information", "License Generated") \ No newline at end of file diff --git a/rscder/gui/layertree.py b/rscder/gui/layertree.py index eec75d7..760f4d7 100644 --- a/rscder/gui/layertree.py +++ b/rscder/gui/layertree.py @@ -1,8 +1,9 @@ +import logging import pdb from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt,QModelIndex -from PyQt5.QtGui import QStandardItemModel, QStandardItem, QCursor +from PyQt5.QtGui import QStandardItemModel, QStandardItem, QCursor, QIcon from PyQt5.QtWidgets import (QTreeView, QTreeWidgetItem, QAbstractItemView, QHeaderView, QStyleFactory) from rscder.gui.actions import get_action_manager @@ -18,6 +19,7 @@ class LayerTree(QtWidgets.QWidget): GRID = 3 tree_changed = QtCore.pyqtSignal(str) + zoom_to_layer_signal = QtCore.pyqtSignal(str) result_clicked = QtCore.pyqtSignal(str, int) def __init__(self, parent=None): super().__init__(parent) @@ -52,6 +54,7 @@ class LayerTree(QtWidgets.QWidget): self.setLayout(layout) self.setLayoutDirection(Qt.LeftToRight) self.is_in_add_layer = False + self.current_item = None def onItemClicked(self, item:QtWidgets.QTreeWidgetItem, column): if item == self.root: @@ -153,7 +156,7 @@ class LayerTree(QtWidgets.QWidget): if item_root.data(0, Qt.UserRole + 1) == layer.id: layer_root = item_root break - print(layer_root.text(0)) + logging.info(layer_root.text(0)) if layer_root is None: self.add_layer(layer.id) return @@ -172,38 +175,72 @@ class LayerTree(QtWidgets.QWidget): 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)) + + 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: - print('nothing') + logging.info('nothing') else: if item == self.root: pass elif item.data(0, Qt.UserRole) == LayerTree.LAYER_TOOT: - actions.append(QtWidgets.QAction('&缩放至该图层', self)) - + actions.append(zoom_to_action) actions.append(QtWidgets.QAction('&重命名', self)) - actions.append(QtWidgets.QAction('&删除', self)) + actions.append(del_action) elif item.data(0, Qt.UserRole) == LayerTree.SUB_RASTER: - actions.append(QtWidgets.QAction('&缩放至该图层', self)) - + actions.append(zoom_to_action) actions.append(QtWidgets.QAction('&重命名', self)) - actions.append(QtWidgets.QAction('&删除', self)) elif item.data(0, Qt.UserRole) == LayerTree.RESULT: - actions.append(QtWidgets.QAction('&缩放至该图层', self)) - + actions.append(zoom_to_action) actions.append(QtWidgets.QAction('&重命名', self)) - actions.append(QtWidgets.QAction('&导出', self)) - actions.append(QtWidgets.QAction('&删除', self)) + actions.append(del_action) - for action in actions: rightMenu.addAction(action) diff --git a/rscder/gui/license.py b/rscder/gui/license.py index f231334..302f13f 100644 --- a/rscder/gui/license.py +++ b/rscder/gui/license.py @@ -11,7 +11,7 @@ class License(QtWidgets.QDialog): def __init__(self, parent = None, flags = QtCore.Qt.WindowFlags() ) -> None: super().__init__(parent, flags) self.setWindowTitle("License") - self.setWindowIcon(QIcon(':/icons/license.png')) + self.setWindowIcon(QIcon(':/icons/logo.png')) self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint) self.setFixedSize(600, 400) diff --git a/rscder/gui/mainwindow.py b/rscder/gui/mainwindow.py index 598f43e..620c516 100644 --- a/rscder/gui/mainwindow.py +++ b/rscder/gui/mainwindow.py @@ -37,6 +37,7 @@ class MainWindow(QMainWindow): 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.action_manager = ActionManager( self.double_map, diff --git a/rscder/gui/mapcanvas.py b/rscder/gui/mapcanvas.py index ec38b12..68bbfbb 100644 --- a/rscder/gui/mapcanvas.py +++ b/rscder/gui/mapcanvas.py @@ -1,6 +1,7 @@ # from alg.utils import random_color # from mul.mulgrubcut import GrabCut +import logging import multiprocessing # from alg.grubcut import grubcut # from gui.layerselect import LayerSelect @@ -82,24 +83,27 @@ class DoubleCanvas(QWidget): zoom_out.triggered.connect( self.set_zoom_out) def set_pan_tool(self, s): - print('set pan tool') + # print('set pan tool') if s: self.mapcanva1.setMapTool(QgsMapToolPan(self.mapcanva1)) self.mapcanva2.setMapTool(QgsMapToolPan(self.mapcanva2)) def set_zoom_in(self, s): - print('set zoom in') + # print('set zoom in') if s: self.mapcanva1.setMapTool(QgsMapToolZoom(self.mapcanva1, False)) self.mapcanva2.setMapTool(QgsMapToolZoom(self.mapcanva2, False)) def set_zoom_out(self, s): - print('set zoom out') + # print('set zoom out') if s: self.mapcanva1.setMapTool(QgsMapToolZoom(self.mapcanva1, True)) self.mapcanva2.setMapTool(QgsMapToolZoom(self.mapcanva2, True)) def add_layer(self, layer:str): + if not layer in Project().layers: + self.clear() + return layer:PairLayer = Project().layers[layer] if not layer.enable: return @@ -209,431 +213,4 @@ class CanvasWidget(QgsMapCanvas): def coordinates2text(pt:QgsPointXY): return self.update_coordinates_text.emit("X: {:.5f}, Y: {:.5f}".format(pt.x(), pt.y())) self.xyCoordinates.connect(coordinates2text) - self.scaleChanged.connect(lambda _ : self.update_scale_text.emit("1 : {:.3f}".format(self.scale()))) - - self.total_f = 0 - self.start_extract = False - self.label_pal = None - # self.result_layers = [] - - def dragEnterEvent(self, e:QDragEnterEvent) -> None: - ''' - Can drag - ''' - candidates = [".tif", ".tiff", ".jpg", ".jpeg", ".bmp", ".png"] - - if e.mimeData().hasUrls(): - if Path(e.mimeData().urls()[0].toLocalFile()).suffix in candidates: - e.accept() - return - - e.ignore() - - def dropEvent(self, e:QDropEvent) -> None: - ''' - Drop image to the canvas - ''' - url_path = e.mimeData().urls()[0] - image_path = QUrl(url_path).toLocalFile() - self.load_image(image_path) - - def load_image(self, path) -> None: - if not Path(path).exists(): - return - - raster_layer = QgsRasterLayer(path, Path(path).name) - if not raster_layer.isValid(): - print("栅格图层加载失败!") - raster_layer.file_path = path - - # self.layers.insert(0, raster_layer) - # self.layers.insert(0, vector_layer) - # if self.current_raster_layer: - # del self.current_raster_layer - # if self.current_vector_layer: - # del self.current_vector_layer - - QgsProject.instance().addMapLayer(raster_layer) - self.current_raster_layer = raster_layer - # self.current_vector_layer = vector_layer - self.setExtent(raster_layer.extent()) - # self.setLayers([vector_layer, raster_layer]) - self.zoomToFeatureExtent(raster_layer.extent()) - self.have_current_image.emit(True) - - def load_result_from_txt(self, path) -> None: - if not Path(path).exists(): - return - # vector_layer = QgsVectorLayer("Polygon?field=category:string(20)&field=confidence:double", Path(path).name, "memory") - vector_layer = QgsVectorLayer("Polygon?field=category:string(20)&field=confidence:double&field=renderkey:string(32)&field=isman:boolean&field=isauto:boolean&field=label:string(64)", Path(path).name + ' outline', "memory") - - if not vector_layer.isValid(): - print("矢量图层加载失败!") - vector_layer.setLabelsEnabled(True) - lyr = QgsPalLayerSettings() - lyr.enabled = True - lyr.fieldName = 'label' # default in data sources - # lyr.textFont = self._TestFont - lyr.textNamedStyle = 'Medium' - text_format = QgsTextFormat() - text_format.color = QColor('#ffffff') - text_format.background().color = QColor('#000000') - text_format.buffer().setEnabled(True) - text_format.buffer().setSize(1) - text_format.buffer().setOpacity(0.5) - lyr.setFormat(text_format) - self.label_pal = lyr - root = QgsRuleBasedLabeling.Rule(QgsPalLayerSettings()) - rule = QgsRuleBasedLabeling.Rule(lyr) - rule.setDescription('label') - root.appendChild(rule) - #Apply label configuration - rules = QgsRuleBasedLabeling(root) - vector_layer.setLabeling(rules) - vector_layer.triggerRepaint() - # lyr.writeToLayer(vector_layer) - vector_layer.setRenderer(self.__get_categorical_renderer("renderkey")) - QgsProject.instance().addMapLayer(vector_layer) - self.current_vector_layer = vector_layer - # provider = self.current_vector_layer.dataProvider() - # provider.truncate() - self.current_vector_layer.startEditing() - # objects = [] - features = [] - with open(path) as f: - for line in f.readlines(): - item_data = line.split("\n")[0].split(" ") - if len(item_data) == 1 + 4 * 2: - cls_name = item_data[0] - item_data[2] = -1.0 * float(item_data[2]) - item_data[4] = -1.0 * float(item_data[4]) - item_data[6] = -1.0 * float(item_data[6]) - item_data[8] = -1.0 * float(item_data[8]) - wkt = "POLYGON (({} {}, {} {}, {} {}, {} {}))".format(*item_data[1:]) - conf = 1.0 - else: - cls_name = item_data[8] - # print(cls_name) - # print(cls_name[0]) - # print(cls_name[0].isalpha()) - if cls_name[0].isalpha(): - item_data[1] = -1.0 * float(item_data[1]) - item_data[3] = -1.0 * float(item_data[3]) - item_data[5] = -1.0 * float(item_data[5]) - item_data[7] = -1.0 * float(item_data[7]) - conf = 1.0 - wkt = "POLYGON (({} {}, {} {}, {} {}, {} {}))".format(*item_data[:8]) - else: - cls_name = item_data[0] - conf = float(item_data[1]) - item_data[3] = -1.0 * float(item_data[3]) - item_data[5] = -1.0 * float(item_data[5]) - item_data[7] = -1.0 * float(item_data[7]) - item_data[9] = -1.0 * float(item_data[9]) - wkt = "POLYGON (({} {}, {} {}, {} {}, {} {}))".format(*item_data[2:]) - - feat = QgsFeature(self.current_vector_layer.fields()) - feat.setGeometry(QgsGeometry.fromWkt(wkt)) - feat.setAttribute('category', cls_name) - feat.setAttribute('confidence', conf) - feat.setAttribute('renderkey', cls_name) - feat.setAttribute('isman', False) - feat.setAttribute('isauto', True) - feat.setAttribute('label', f'{ cls_name},{conf:.3f}') - features.append(feat) - # objects.append({ - # "category": item_data[0], - # "confidence": item_data[1], - # "fid": feat.id() - # }) - self.current_vector_layer.addFeatures(features) - self.current_vector_layer.commitChanges() - self.have_current_vector.emit(True) - self.layer_update() - - def clear_vector(self): - if self.current_vector_layer is not None: - provider = self.current_vector_layer.dataProvider() - provider.truncate() - self.layer_update() - - def change_current_vector_layer(self, vector_layer): - if self.current_vector_layer is not None: - self.current_vector_layer.removeSelection() - self.current_vector_layer = vector_layer - - self.layer_update() - - def layer_update(self): - if self.current_vector_layer is None: - self.object_updated.emit([]) - return - self.current_vector_layer.updateExtents() - self.refresh() - objects = [] - for feature in self.current_vector_layer.getFeatures(): - objects.append({ - "category": feature['category'], - "confidence": feature['confidence'], - "renderkey": feature['renderkey'], - 'isman': feature['isman'], - 'isauto': feature['isauto'], - "fid": feature.id() - }) - self.object_updated.emit(objects) - - def selectd_changed(self, items:list): - if len(items) == 0: - self.current_vector_layer.removeSelection() - else: - self.current_vector_layer.selectByIds(list(item['fid'] for item in items)) - - def item_change(self, items:list): - self.current_vector_layer.startEditing() - features = list(self.current_vector_layer.getFeatures()) - for f in features: - has_f = False - for item in items: - if f.id() == item['fid']: - # f = QgsFeature(f) - has_f = True - f.setAttribute('category', item['category']) - f.setAttribute('confidence', item['confidence']) - f.setAttribute('renderkey', item['renderkey']) - f.setAttribute('isman', item['isman']) - f.setAttribute('isauto', item['isauto']) - self.current_vector_layer.updateFeature(f) - break - if has_f: - continue - - self.current_vector_layer.deleteFeature(f.id()) - - self.current_vector_layer.commitChanges() - self.current_vector_layer.updateExtents() - # print(self.current_vector_layer.fields()) - - self.refresh() - - def zoom_to_full_extent(self) -> None: - if self.current_raster_layer: - self.zoomToFeatureExtent(self.current_raster_layer.extent()) - - def __get_categorical_renderer(self, fieldname:str) -> QgsCategorizedSymbolRenderer: - settings = QSettings(self) - - category_keys = settings.value("keys", get_default_category_keys()) - category_colors = settings.value("colors", get_default_category_colors()) - - settings.beginGroup("Category") - if len(category_colors) < len(category_keys): - for _ in range(len(category_keys) - len(category_colors)): - category_colors.append(random_color()) - settings.setValue('colors', category_colors) - settings.endGroup() - categorized_renderer = QgsCategorizedSymbolRenderer() - for key, color in zip(category_keys, category_colors): - fill_color = QColor(color) - fill_color.setAlphaF(0.3) - categorized_renderer.addCategory(\ - QgsRendererCategory( - key, - QgsFillSymbol.createSimple( - {"color":fill_color.name(QColor.HexArgb),"outline_color":color, "outline_width":"1"}), '')) - categorized_renderer.setClassAttribute(fieldname) - return categorized_renderer - - def export_to_raster(self, path) -> None: - if self.current_vector_layer is None: - return - - def load_extract_result(self, res): - r = self.current_raster_layer - vector_layer = QgsVectorLayer("Polygon?field=category:string(20)&field=confidence:double&field=renderkey:string(32)&field=isman:boolean&field=isauto:boolean", Path(r.file_path).name + ' outline', "memory") - # vector_layer = QgsVectorLayer(tempfile) - if not vector_layer.isValid(): - print("矢量图层加载失败!") - - vector_layer.setRenderer(self.__get_categorical_renderer("renderkey")) - lyr = QgsPalLayerSettings() - lyr.enabled = True - lyr.fieldName = 'label' # default in data sources - # lyr.textFont = self._TestFont - lyr.textNamedStyle = 'Medium' - text_format = QgsTextFormat() - text_format.color = QColor('#ffffff') - text_format.background().color = QColor('#000000') - text_format.buffer().setEnabled(True) - text_format.buffer().setSize(1) - text_format.buffer().setOpacity(0.5) - lyr.setFormat(text_format) - self.label_pal = lyr - root = QgsRuleBasedLabeling.Rule(QgsPalLayerSettings()) - rule = QgsRuleBasedLabeling.Rule(lyr) - rule.setDescription('label') - root.appendChild(rule) - #Apply label configuration - rules = QgsRuleBasedLabeling(root) - vector_layer.setLabeling(rules) - vector_layer.triggerRepaint() - vector_layer.startEditing() - features = [] - for f in res: - pts = f[0] - prop = f[1] - # pts = grubcut(img_path, pts, False, True, False ) - pts = list( f'{p[0]} {p[1]}' for p in pts ) - wkt = f'POLYGON (( {",".join(pts)} ))' - # geometry = QgsGeometry.fromWkt(wkt) - feat = QgsFeature(vector_layer.fields()) - feat.setGeometry(QgsGeometry.fromWkt(wkt)) - feat.setAttribute('category', prop['category']) - feat.setAttribute('confidence', prop['confidence']) - feat.setAttribute('renderkey', prop['category']) - feat.setAttribute('isman', False) - feat.setAttribute('isauto', True) - features.append(feat) - - vector_layer.addFeatures(features) - vector_layer.commitChanges() - QgsProject.instance().addMapLayer(vector_layer) - self.process_end.emit() - r.has_extract = True - self.start_extract = False - r.extract_layer = vector_layer - self.layer_update() - - def run_thread(self, conn, pp): - all_ok = False - # print(pp.is_alive) - while pp.is_alive: - r = conn.recv() - # print(r) - if all_ok: - self.extract_end.emit(r) - break - if int(r) == self.total_f - 1: - all_ok = True - self.process_update.emit(r) - # print(conn.recv()) - def grubcut(self, v, r): - # for f in v.getFeatures(): - if self.start_extract: - return - self.current_raster_layer = r - img_path = r.file_path - if getattr(r, 'has_extract', False): - vector_layer = r.extract_layer - try: - QgsProject.instance().removeMapLayer(vector_layer) - except: - pass - - # self.current_vector_layer = vector_layer - features = [] - points = [] - for f in v.getFeatures(): - pts = f.geometry().vertices() - pts = list([ vr.x(), vr.y() ] for vr in pts) - points.append(pts) - features.append({ - 'category': f['category'], - 'confidence': f['confidence'] - }) - self.total_f = len(points) - self.start_extract = True - self.process_start.emit([0, self.total_f]) - parent_conn, child_conn = multiprocessing.Pipe() - t = GrabCut(child_conn, img_path, points, features) - p = threading.Thread(target=self.run_thread, args=(parent_conn,t)) - t.start() - p.start() - - def export_to(self, path, filter_name) -> None: - if filter_name == 'Shp (*.shp)': - if self.current_vector_layer is None: - return - ls = LayerSelect(self) - ls.show() - ls.exec() - if ls.result() == LayerSelect.OK: - save_options = QgsVectorFileWriter.SaveVectorOptions() - save_options.driverName = "ESRI Shapefile" - save_options.fileEncoding = "UTF-8" - transform_context = QgsProject.instance().transformContext() - error = QgsVectorFileWriter.writeAsVectorFormatV2(ls.value, - path, - transform_context, - save_options) - if error[0] == QgsVectorFileWriter.NoError: - print("又成功了!") - else: - print(error) - - if filter_name == 'JPEG Images(*.jpg)': - file_name = path + '.tif' - extent = self.current_raster_layer.extent() - width, height = self.current_raster_layer.width(), self.current_raster_layer.height() - - pipe = QgsRasterPipe() - provider = self.current_raster_layer.dataProvider() - pipe.set(provider.clone()) - - file_writer = QgsRasterFileWriter(file_name) - error = file_writer.writeRaster(pipe, - width, - height, - extent, - self.current_raster_layer.crs()) - - target_img = cv2.imread(file_name) - jpg_file_name = path + '.jpg' - cv2.imwrite(jpg_file_name, target_img) - os.remove(file_name) - if error == QgsRasterFileWriter.NoError: - QMessageBox.about(self, 'Export Files', '导出JPEG图像成功!') - else: - QMessageBox.about(self, 'Export Files', '导出JPEG图像失败!') - - if filter_name == 'TIFF Images(*.tif)': - file_name = path + '.tif' - extent = self.current_raster_layer.extent() - width, height = self.current_raster_layer.width(), self.current_raster_layer.height() - - pipe = QgsRasterPipe() - provider = self.current_raster_layer.dataProvider() - pipe.set(provider.clone()) - - file_writer = QgsRasterFileWriter(file_name) - error = file_writer.writeRaster(pipe, - width, - height, - extent, - self.current_raster_layer.crs()) - if error == QgsRasterFileWriter.NoError: - QMessageBox.about(self, 'Export Files', '导出TIFF图像成功!') - else: - QMessageBox.about(self, 'Export Files', '导出TIFF图像失败!') - if filter_name == 'PNG Images(*.png)': - file_name = path + '.tif' - extent = self.current_raster_layer.extent() - width, height = self.current_raster_layer.width(), self.current_raster_layer.height() - - pipe = QgsRasterPipe() - provider = self.current_raster_layer.dataProvider() - pipe.set(provider.clone()) - - file_writer = QgsRasterFileWriter(file_name) - error = file_writer.writeRaster(pipe, - width, - height, - extent, - self.current_raster_layer.crs()) - target_img = cv2.imread(file_name) - jpg_file_name = path + '.png' - cv2.imwrite(jpg_file_name, target_img) - os.remove(file_name) - if error == QgsRasterFileWriter.NoError: - QMessageBox.about(self, 'Export Files', '导出PNG图像成功!') - else: - QMessageBox.about(self, 'Export Files', '导出PNG图像失败!') \ No newline at end of file + self.scaleChanged.connect(lambda _ : self.update_scale_text.emit("1 : {:.3f}".format(self.scale()))) \ No newline at end of file diff --git a/rscder/gui/messagebox.py b/rscder/gui/messagebox.py index 56da67e..b06c0d9 100644 --- a/rscder/gui/messagebox.py +++ b/rscder/gui/messagebox.py @@ -1,6 +1,6 @@ from PyQt5.QtWidgets import QTextEdit from PyQt5 import QtWidgets -from PyQt5.QtGui import QTextCursor +from PyQt5.QtGui import QTextCursor, QIcon from PyQt5.QtCore import Qt from datetime import datetime, time class MessageBox(QTextEdit): @@ -24,7 +24,7 @@ class MessageBox(QTextEdit): def right_menu_show(self, position): rightMenu = QtWidgets.QMenu(self) # QAction = QtWidgets.QAction(self.menuBar1) - action = QtWidgets.QAction('清空') + action = QtWidgets.QAction(QIcon(':/icons/exit.png'), '清空') action.triggered.connect(self.clear) rightMenu.addAction(action) diff --git a/rscder/gui/plugins.py b/rscder/gui/plugins.py index 0a93405..bd9d01a 100644 --- a/rscder/gui/plugins.py +++ b/rscder/gui/plugins.py @@ -1,3 +1,4 @@ +import logging import os import shutil from PyQt5.QtWidgets import * @@ -11,7 +12,7 @@ class PluginDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle('Plugins') - self.setWindowIcon(QIcon(":/icons/logo.svg")) + self.setWindowIcon(QIcon(":/icons/logo.png")) self.setMinimumWidth(900) self.setMinimumHeight(600) self.plugins = list(Settings.Plugin().plugins) @@ -57,7 +58,7 @@ class PluginDialog(QDialog): plugin_directory = QFileDialog.getExistingDirectory(self, 'Select Plugin Directory', '.') if plugin_directory is not None: info = PluginLoader.load_plugin_info(plugin_directory) - print(info) + logging.info(info) if info is not None: try: @@ -73,6 +74,7 @@ class PluginDialog(QDialog): self.plugin_table.insertRow(self.plugin_table.rowCount()) name_item = QTableWidgetItem(info['name']) + name_item.setIcon(QIcon(':/icons/tools.png')) module_item = QTableWidgetItem(info['module']) enabled_item = QTableWidgetItem('启用') enabled_item.setCheckState(Qt.Checked) @@ -94,7 +96,8 @@ class PluginDialog(QDialog): try: shutil.rmtree(info['path']) except Exception as e: - print(e) + # logging + logging.info(e) pass # for idx in self.plugins diff --git a/rscder/gui/result.py b/rscder/gui/result.py index 8464cf1..097ac65 100644 --- a/rscder/gui/result.py +++ b/rscder/gui/result.py @@ -1,4 +1,5 @@ +import logging from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt,QModelIndex, pyqtSignal from PyQt5.QtGui import QStandardItemModel, QStandardItem @@ -47,7 +48,8 @@ class ResultTable(QtWidgets.QWidget): self.tablewidget.item(row, col).setBackground(Qt.yellow) else: self.tablewidget.item(row, col).setBackground(Qt.green) - print(item_idx, item_status) + # logging + logging.info(item_idx, item_status) self.result.update({'row':item_idx, 'value':item_status}) self.no_change = False diff --git a/rscder/mul/mulstart.py b/rscder/mul/mulstart.py index e0a4717..55b4838 100644 --- a/rscder/mul/mulstart.py +++ b/rscder/mul/mulstart.py @@ -39,12 +39,16 @@ class MulStart: sys.exit(0) # Create and display the splash screen splash_pix = QPixmap(':/icons/splash.png') + # splash_pix.scaledToWidth(800) + # splash_pix.scaledToHeight(600) splash = QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint) + # splash + # splash.setFixedSize(800, 600) progressBar = QProgressBar(splash) progressBar.setMaximum(10) progressBar.setTextVisible(False) - progressBar.setGeometry(46, splash_pix.height() - 60, splash_pix.width()-92, 10) + progressBar.setGeometry(106, splash_pix.height() - 60, splash_pix.width()-212, 10) splash.show() for i in range(1, 11): diff --git a/rscder/plugins/loader.py b/rscder/plugins/loader.py index 635e133..985da81 100644 --- a/rscder/plugins/loader.py +++ b/rscder/plugins/loader.py @@ -1,3 +1,4 @@ +import logging import shutil from rscder.utils.setting import Settings from PyQt5.QtWidgets import QMessageBox @@ -34,12 +35,13 @@ class PluginLoader(QObject): module = importlib.import_module(os.path.basename(path)) mes = inspect.getmembers(module) for name, obj in mes: - print(name, obj) + # logging + logging.info(name, obj) if inspect.isclass(obj) and issubclass(obj, BasicPlugin): info = obj.info() break except Exception as e: - print(e) + logging.info(str(e)) QMessageBox.critical(None, 'Error', f'{path} load error: {e}') finally: sys.path.pop(0) diff --git a/rscder/utils/license.py b/rscder/utils/license.py index c731d90..c280516 100644 --- a/rscder/utils/license.py +++ b/rscder/utils/license.py @@ -1,4 +1,5 @@ import base64 +import logging from Crypto.Cipher import AES import uuid import hashlib @@ -78,7 +79,7 @@ def get_aes(): class LicenseHelper(object): def generate_license(self, end_date, mac_addr): - print("Received end_date: {}, mac_addr: {}".format(end_date, mac_addr)) + logging.info("Received end_date: {}, mac_addr: {}".format(end_date, mac_addr)) psw = self.hash_msg('smartant' + str(mac_addr)) license_str = {} license_str['mac'] = mac_addr @@ -110,8 +111,8 @@ class LicenseHelper(object): lic_date_array = datetime.datetime.strptime(lic_date, "%Y-%m-%d %H:%M:%S") remain_days = lic_date_array - current_time_array remain_days = remain_days.days - print('lic data:{}'.format(lic_date)) - print('remain_days: {}'.format(remain_days)) + logging.info('lic data:{}'.format(lic_date)) + logging.info('remain_days: {}'.format(remain_days)) if remain_days < 0 or remain_days == 0: return False else: @@ -138,6 +139,6 @@ if __name__ == '__main__': license_dic = LicenseHelper().read_license(license_result) - print(license_dic) + logging.info(license_dic) \ No newline at end of file diff --git a/使用手册.txt b/使用手册.txt new file mode 100644 index 0000000..6bc28e8 --- /dev/null +++ b/使用手册.txt @@ -0,0 +1,19 @@ +1. lic文件生成 +点击RSCDer.exe,复制所显示的mac地址 +点击keygen.exe,粘贴mac地址,设置截止日期,并选择保存路径(无需输入后缀名) +点击Generate + +2. lic文件导入 +点击RSCDer.exe,点击Open,选择生成的lic文件 +点击OK + +3. 新建项目与数据导入 +点击RSCDer.exe +点击新建项目 +选择路径、名称 +将会在路径下看到与名称相同的文件夹 + +点击载入数据 +选择两幅不同时相的影像 + +4. 基本变化检测 \ No newline at end of file diff --git a/未命名.prj b/未命名.prj deleted file mode 100644 index 35c901f..0000000 --- a/未命名.prj +++ /dev/null @@ -1,8 +0,0 @@ -cell_size: -- 100 -- 100 -layers: [] -max_memory: 100 -max_threads: 4 -results: [] -root: F:\LZY_DATA\p122r032