fix plugin bugs
This commit is contained in:
parent
00ffe0c03f
commit
df27d40587
1
3rd/about/__init__.py
Normal file
1
3rd/about/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from about.main import *
|
68
3rd/about/main.py
Normal file
68
3rd/about/main.py
Normal file
@ -0,0 +1,68 @@
|
||||
from rscder.plugins.basic import BasicPlugin
|
||||
|
||||
from PyQt5.QtWidgets import QDialog, QAction, QApplication, QLabel, QTextEdit, QVBoxLayout
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QIcon
|
||||
|
||||
class AboutDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super(AboutDialog, self).__init__(parent)
|
||||
self.setWindowTitle("About")
|
||||
self.setFixedSize(800, 400)
|
||||
|
||||
self.label = QLabel("<h1>"+ QApplication.applicationName() + "</h1>")
|
||||
self.label.setAlignment(Qt.AlignCenter)
|
||||
self.label.setStyleSheet("font-size: 20px;")
|
||||
|
||||
self.label2 = QLabel("<h2>Version: " + QApplication.applicationVersion() + "</h2>")
|
||||
self.label2.setAlignment(Qt.AlignCenter)
|
||||
self.label2.setStyleSheet("font-size: 15px;")
|
||||
|
||||
self.label3 = QLabel("<h2>" + QApplication.organizationName() + "</h2>")
|
||||
self.label3.setAlignment(Qt.AlignCenter)
|
||||
self.label3.setStyleSheet("font-size: 15px;")
|
||||
|
||||
self.label4 = QLabel("<h3>Copyright (c) 2020</h3>")
|
||||
self.label4.setAlignment(Qt.AlignCenter)
|
||||
self.label4.setStyleSheet("font-size: 10px;")
|
||||
|
||||
self.text = QTextEdit()
|
||||
self.text.setReadOnly(True)
|
||||
self.text.setText('''
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
''')
|
||||
|
||||
self.layout = QVBoxLayout()
|
||||
self.layout.addWidget(self.label)
|
||||
self.layout.addWidget(self.label2)
|
||||
self.layout.addWidget(self.label3)
|
||||
self.layout.addWidget(self.label4)
|
||||
self.layout.addWidget(self.text)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
|
||||
class AboutPlugin(BasicPlugin):
|
||||
|
||||
@staticmethod
|
||||
def info():
|
||||
return {
|
||||
'name': '关于',
|
||||
'author': 'RSCDER',
|
||||
'version': '1.0.0',
|
||||
'description': '关于'
|
||||
}
|
||||
|
||||
def set_action(self):
|
||||
menu = self.ctx['help_menu']
|
||||
action = QAction('&关于', self.ctx['menu_bar'])
|
||||
action.triggered.connect(self.on_about)
|
||||
|
||||
menu.addAction(action)
|
||||
|
||||
def on_about(self):
|
||||
print('on_about')
|
||||
dialog = AboutDialog(self.ctx['mainwindow'])
|
||||
dialog.show()
|
1
plugins/about/__init__.py
Normal file
1
plugins/about/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from about.main import *
|
@ -46,13 +46,23 @@ class AboutDialog(QDialog):
|
||||
|
||||
class AboutPlugin(BasicPlugin):
|
||||
|
||||
@staticmethod
|
||||
def info():
|
||||
return {
|
||||
'name': '关于',
|
||||
'author': 'RSCDER',
|
||||
'version': '1.0.0',
|
||||
'description': '关于'
|
||||
}
|
||||
|
||||
def set_action(self):
|
||||
menu = self.ctx['help_menu']
|
||||
action = QAction('&关于', self.ctx['menubar'])
|
||||
action = QAction('&关于', self.ctx['menu_bar'])
|
||||
action.triggered.connect(self.on_about)
|
||||
|
||||
menu.addAction(action)
|
||||
|
||||
def on_about(self):
|
||||
dialog = AboutDialog(self.ctx['main_window'])
|
||||
print('on_about')
|
||||
dialog = AboutDialog(self.ctx['mainwindow'])
|
||||
dialog.show()
|
@ -47,6 +47,21 @@ class ActionManager(QtCore.QObject):
|
||||
self.help_menu = menubar.addMenu('&帮助')
|
||||
|
||||
|
||||
@property
|
||||
def menus(self):
|
||||
return {
|
||||
'file_menu': self.file_menu,
|
||||
'basic_menu': self.basic_menu,
|
||||
'change_detection_menu': self.change_detection_menu,
|
||||
'special_chagne_detec_menu': self.special_chagne_detec_menu,
|
||||
'seg_chagne_detec_menu': self.seg_chagne_detec_menu,
|
||||
'postop_menu': self.postop_menu,
|
||||
'view_menu': self.view_menu,
|
||||
'plugin_menu': self.plugin_menu,
|
||||
'help_menu': self.help_menu,
|
||||
'menu_bar': self.menubar
|
||||
}
|
||||
|
||||
def set_toolbar(self, toolbar):
|
||||
self.toolbar = toolbar
|
||||
self.toolbar.setIconSize(QtCore.QSize(24, 24))
|
||||
|
@ -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(os.path.join("gui", "icons", "license.png")))
|
||||
self.setWindowIcon(QIcon(':/icons/license.png'))
|
||||
self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint)
|
||||
self.setFixedSize(600, 400)
|
||||
|
||||
|
@ -9,6 +9,7 @@ from rscder.gui.layertree import LayerTree
|
||||
from rscder.gui.mapcanvas import DoubleCanvas
|
||||
from rscder.gui.messagebox import MessageBox
|
||||
from rscder.gui.result import ResultTable
|
||||
from rscder.plugins.loader import PluginLoader
|
||||
from rscder.utils import Settings
|
||||
from rscder.utils.project import Project
|
||||
|
||||
@ -43,6 +44,18 @@ class MainWindow(QMainWindow):
|
||||
self.action_manager.set_status_bar(self.statusBar())
|
||||
self.action_manager.set_actions()
|
||||
|
||||
PluginLoader(dict(
|
||||
layer_tree=self.layer_tree,
|
||||
pair_canvas=self.double_map,
|
||||
message_box=self.message_box,
|
||||
result_table=self.result_box,
|
||||
project=Project(self),
|
||||
mainwindow=self,
|
||||
toolbar=self.toolbar,
|
||||
statusbar=self.statusBar(),
|
||||
**self.action_manager.menus
|
||||
)).load_plugin()
|
||||
|
||||
self.resize(*Settings.General().size)
|
||||
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
import os
|
||||
import shutil
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5.QtGui import QIcon, Qt
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtCore import Qt
|
||||
from rscder.plugins.loader import PluginLoader
|
||||
from rscder.utils.setting import Settings
|
||||
|
||||
class PluginDialog(QDialog):
|
||||
@ -8,25 +12,25 @@ class PluginDialog(QDialog):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle('Plugins')
|
||||
self.setWindowIcon(QIcon(":/icons/logo.svg"))
|
||||
self.setMinimumWidth(800)
|
||||
self.setMinimumWidth(900)
|
||||
self.setMinimumHeight(600)
|
||||
self.plugins = Settings.Plugin().plugins
|
||||
self.plugins = list(Settings.Plugin().plugins)
|
||||
|
||||
self.plugin_table = QTableWidget(len(self.plugins), 2, self)
|
||||
self.plugin_table = QTableWidget(len(self.plugins), 3, self)
|
||||
self.plugin_table.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.plugin_table.setColumnWidth(0, 200)
|
||||
self.plugin_table.setColumnWidth(1, 500)
|
||||
self.plugin_table.setHorizontalHeaderLabels(['Name', 'Path', 'Enabled'])
|
||||
self.plugin_table.setEditTriggers(QAbstractItemView.DoubleClicked)
|
||||
self.plugin_table.setHorizontalHeaderLabels(['Name', 'Module', 'Enabled'])
|
||||
self.plugin_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||
self.plugin_table.cellDoubleClicked.connect(self.edit_plugin)
|
||||
for idx, plugin in enumerate(self.plugins):
|
||||
name_item = QTableWidgetItem(plugin['name'])
|
||||
path_item = QTableWidgetItem(plugin['path'])
|
||||
module_item = QTableWidgetItem(plugin['module'])
|
||||
enabled_item = QTableWidgetItem()
|
||||
enabled_item.setCheckState(Qt.Checked if plugin['enabled'] else Qt.Unchecked)
|
||||
|
||||
self.plugin_table.setItem(idx, 0, name_item)
|
||||
self.plugin_table.setItem(idx, 1, path_item)
|
||||
self.plugin_table.setItem(idx, 1, module_item)
|
||||
self.plugin_table.setItem(idx, 2, enabled_item)
|
||||
|
||||
self.add_button = QPushButton('Add', self)
|
||||
@ -50,39 +54,58 @@ class PluginDialog(QDialog):
|
||||
self.has_change = False
|
||||
|
||||
def add_plugin(self):
|
||||
self.has_change = True
|
||||
self.plugin_table.insertRow(self.plugin_table.rowCount())
|
||||
plugin_directory = QFileDialog.getExistingDirectory(self, 'Select Plugin Directory', '.')
|
||||
if plugin_directory is not None:
|
||||
info = PluginLoader.load_plugin_info(plugin_directory)
|
||||
print(info)
|
||||
|
||||
if info is not None:
|
||||
info['module'] = os.path.basename(plugin_directory)
|
||||
info['enabled'] = True
|
||||
self.has_change = True
|
||||
self.plugins.append(info)
|
||||
self.plugin_table.insertRow(self.plugin_table.rowCount())
|
||||
name_item = QTableWidgetItem(info['name'])
|
||||
module_item = QTableWidgetItem(info['module'])
|
||||
enabled_item = QTableWidgetItem('启用')
|
||||
enabled_item.setCheckState(Qt.Checked)
|
||||
self.plugin_table.setItem(self.plugin_table.rowCount() - 1, 0, name_item)
|
||||
self.plugin_table.setItem(self.plugin_table.rowCount() - 1, 1, module_item)
|
||||
self.plugin_table.setItem(self.plugin_table.rowCount() - 1, 2, enabled_item)
|
||||
|
||||
dst = PluginLoader.copy_plugin_to_3rd(plugin_directory)
|
||||
if dst is not None:
|
||||
self.plugins[-1]['path'] = dst
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def remove_plugin(self):
|
||||
self.has_change = True
|
||||
for row in self.plugin_table.selectedItems():
|
||||
self.plugin_table.removeRow(row.row())
|
||||
|
||||
row_ids = list( row.row() for row in self.plugin_table.selectionModel().selectedRows())
|
||||
row_ids.sort(reverse=True)
|
||||
for row in row_ids:
|
||||
self.plugin_table.removeRow(row)
|
||||
info = self.plugins.pop(row)
|
||||
try:
|
||||
shutil.rmtree(info['path'])
|
||||
except:
|
||||
pass
|
||||
# for idx in self.plugins
|
||||
|
||||
def edit_plugin(self, row, column):
|
||||
self.has_change = True
|
||||
if column == 0:
|
||||
self.plugin_table.item(row, column).setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
|
||||
elif column == 1:
|
||||
open_file = QFileDialog.getOpenFileName(self, 'Open File', '', 'Python Files (*.py)')
|
||||
if open_file[0]:
|
||||
self.plugin_table.item(row, column).setText(open_file[0])
|
||||
else:
|
||||
pass
|
||||
elif column == 2:
|
||||
if column == 2:
|
||||
self.plugin_table.item(row, column).setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
|
||||
# self.plugin_list.setFixedWidth(200)
|
||||
|
||||
def save_plugin(self):
|
||||
|
||||
plugins = []
|
||||
for idx in range(self.plugin_table.rowCount()):
|
||||
name = self.plugin_table.item(idx, 0).text()
|
||||
path = self.plugin_table.item(idx, 1).text()
|
||||
enabled = self.plugin_table.item(idx, 2).checkState() == Qt.Checked
|
||||
plugins.append({'name': name, 'path': path, 'enabled': enabled})
|
||||
Settings.Plugin().plugins = plugins
|
||||
self.plugins[idx]['enabled'] = enabled
|
||||
Settings.Plugin().plugins = self.plugins
|
||||
self.has_change = False
|
||||
self.close()
|
||||
|
||||
def closeEvent(self, event):
|
||||
|
@ -5,10 +5,10 @@ from PyQt5.QtGui import QFont, QPixmap
|
||||
from PyQt5.QtWidgets import QSplashScreen, QProgressBar, QStyleFactory, QMessageBox
|
||||
from qgis.core import QgsApplication
|
||||
# from qgis.core import
|
||||
from gui.mainwindow import MainWindow
|
||||
from rscder.gui.mainwindow import MainWindow
|
||||
import multiprocessing
|
||||
from gui import license
|
||||
from utils.setting import Settings
|
||||
from rscder.gui import license
|
||||
from rscder.utils.setting import Settings
|
||||
|
||||
class MulStart:
|
||||
|
||||
@ -27,7 +27,7 @@ class MulStart:
|
||||
|
||||
|
||||
# pyrcc5 res.qrc -o rc.py
|
||||
import rc
|
||||
import rscder.rc
|
||||
|
||||
app = QgsApplication([], True)
|
||||
QgsApplication.initQgis()
|
||||
|
@ -1,6 +1,7 @@
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
from rscder.utils.project import PairLayer
|
||||
|
||||
|
||||
class BasicPlugin(QObject):
|
||||
'''
|
||||
插件基类
|
||||
@ -15,11 +16,16 @@ class BasicPlugin(QObject):
|
||||
statusbar: statusbar
|
||||
menu: menu
|
||||
file_menu: file menu
|
||||
|
||||
|
||||
'''
|
||||
@staticmethod
|
||||
def info():
|
||||
'''
|
||||
Plugin info
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def __init__(self, ctx:dict) -> None:
|
||||
super().__init__()
|
||||
super().__init__(ctx['mainwindow'])
|
||||
self.ctx = ctx
|
||||
self.layer_tree = ctx['layer_tree']
|
||||
self.pair_canvas = ctx['pair_canvas']
|
||||
@ -29,7 +35,7 @@ class BasicPlugin(QObject):
|
||||
self.mainwindow = ctx['mainwindow']
|
||||
self.set_action()
|
||||
self.project.layer_load.connect(self.on_data_load)
|
||||
self.project.project_created.connect(self.setup)
|
||||
self.project.project_init.connect(self.setup)
|
||||
|
||||
|
||||
def set_action(self):
|
||||
|
@ -1,4 +1,6 @@
|
||||
import shutil
|
||||
from rscder.utils.setting import Settings
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
from rscder.plugins.basic import BasicPlugin
|
||||
import importlib
|
||||
@ -11,20 +13,54 @@ class PluginLoader(QObject):
|
||||
plugin_loaded = pyqtSignal()
|
||||
|
||||
def __init__(self, ctx):
|
||||
super().__init__()
|
||||
self.ctx = ctx
|
||||
self.plugins = []
|
||||
|
||||
@staticmethod
|
||||
def copy_plugin_to_3rd(dir, random_suffix=True):
|
||||
if not os.path.exists(Settings.Plugin().root):
|
||||
os.makedirs(Settings.Plugin().root)
|
||||
return shutil.copytree(dir,
|
||||
os.path.join(Settings.Plugin().root,
|
||||
os.path.basename(dir)))
|
||||
|
||||
@staticmethod
|
||||
def load_plugin_info(path):
|
||||
|
||||
sys.path.insert(0, os.path.join(path, '..'))
|
||||
info = None
|
||||
try:
|
||||
module = importlib.import_module(os.path.basename(path))
|
||||
mes = inspect.getmembers(module)
|
||||
for name, obj in mes:
|
||||
print(name, obj)
|
||||
if inspect.isclass(obj) and issubclass(obj, BasicPlugin):
|
||||
info = obj.info()
|
||||
break
|
||||
except Exception as e:
|
||||
print(e)
|
||||
QMessageBox.critical(None, 'Error', f'{path} load error: {e}')
|
||||
finally:
|
||||
sys.path.pop(0)
|
||||
return info
|
||||
|
||||
def load_plugin(self):
|
||||
plugins = Settings.Plugin().plugins
|
||||
if Settings.Plugin().root not in sys.path:
|
||||
sys.path.insert(0, Settings.Plugin().root)
|
||||
for plugin in plugins:
|
||||
name = plugin['name']
|
||||
path = plugin['path']
|
||||
# path = plugin['path']
|
||||
if not plugin['enabled']:
|
||||
continue
|
||||
try:
|
||||
module = importlib.import_module(path)
|
||||
module = importlib.import_module(plugin['module'])
|
||||
for oname, obj in inspect.getmembers(module):
|
||||
if inspect.isclass(obj) and issubclass(obj, BasicPlugin) and obj != BasicPlugin and obj != PluginLoader and oname == name:
|
||||
obj(self.ctx)
|
||||
if inspect.isclass(obj) and issubclass(obj, BasicPlugin) and obj != BasicPlugin and obj != PluginLoader:
|
||||
self.plugins.append(obj(self.ctx))
|
||||
break
|
||||
except Exception as e:
|
||||
self.ctx['message_box'].error(f'{name} load error: {e}')
|
||||
# print(e)
|
||||
self.ctx['message_box'].error(f'{plugin["name"]} load error: {e}')
|
||||
|
||||
|
||||
self.plugin_loaded.emit()
|
@ -1,26 +1,26 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>icons/splash.png</file>
|
||||
<file>icons/logo.svg</file>
|
||||
<file>icons/load.svg</file>
|
||||
<file>icons/settings.svg</file>
|
||||
<file>icons/exit.svg</file>
|
||||
<file>icons/vector.svg</file>
|
||||
<file>icons/zoomin.svg</file>
|
||||
<file>icons/zoomout.svg</file>
|
||||
<file>icons/pan.svg</file>
|
||||
<file>icons/full.svg</file>
|
||||
<file>icons/start.svg</file>
|
||||
<file>icons/clear.svg</file>
|
||||
<file>icons/edit.svg</file>
|
||||
<file>icons/export.svg</file>
|
||||
<file>icons/model.svg</file>
|
||||
<file>icons/assessment.svg</file>
|
||||
<file>icons/paint.svg</file>
|
||||
<file>icons/font.svg</file>
|
||||
<file>icons/qt.svg</file>
|
||||
<file>icons/ok.svg</file>
|
||||
<file>icons/cancel.svg</file>
|
||||
<file>icons/outline.svg</file>
|
||||
<file>icons\assessment.svg</file>
|
||||
<file>icons\cancel.svg</file>
|
||||
<file>icons\clear.svg</file>
|
||||
<file>icons\edit.svg</file>
|
||||
<file>icons\exit.svg</file>
|
||||
<file>icons\export.svg</file>
|
||||
<file>icons\font.svg</file>
|
||||
<file>icons\full.svg</file>
|
||||
<file>icons\load.svg</file>
|
||||
<file>icons\logo.svg</file>
|
||||
<file>icons\model.svg</file>
|
||||
<file>icons\ok.svg</file>
|
||||
<file>icons\outline.svg</file>
|
||||
<file>icons\paint.svg</file>
|
||||
<file>icons\pan.svg</file>
|
||||
<file>icons\qt.svg</file>
|
||||
<file>icons\settings.svg</file>
|
||||
<file>icons\splash.png</file>
|
||||
<file>icons\start.svg</file>
|
||||
<file>icons\vector.svg</file>
|
||||
<file>icons\zoomin.svg</file>
|
||||
<file>icons\zoomout.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
16
rscder/utils/generate_rc.py
Normal file
16
rscder/utils/generate_rc.py
Normal file
@ -0,0 +1,16 @@
|
||||
import shutil
|
||||
import subprocess
|
||||
import os
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
icon_path = os.path.join(path, '..', 'icons')
|
||||
with open(os.path.join(path, '..', 'res.qrc'), 'w') as f:
|
||||
f.write(f'<RCC>\n')
|
||||
f.write(f' <qresource prefix="/">\n')
|
||||
for icon in os.listdir(icon_path):
|
||||
f.write(f' <file>{os.path.join("icons", icon)}</file>\n')
|
||||
f.write(f' </qresource>\n')
|
||||
f.write(f'</RCC>\n')
|
||||
|
||||
subprocess.run(['pyrcc5', 'res.qrc', '-o', 'rc.py'], cwd=os.path.join(path, '..'))
|
||||
|
||||
shutil.rmtree(icon_path)
|
@ -3,7 +3,7 @@ from pathlib import Path
|
||||
from typing import Dict, List
|
||||
import uuid
|
||||
from osgeo import gdal, gdal_array
|
||||
from utils.setting import Settings
|
||||
from rscder.utils.setting import Settings
|
||||
from qgis.core import QgsRasterLayer, QgsLineSymbol, QgsSingleSymbolRenderer, QgsSimpleLineSymbolLayer, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsFeature, QgsGeometry, QgsPointXY
|
||||
from PyQt5.QtCore import QObject, pyqtSignal
|
||||
from PyQt5.QtGui import QColor
|
||||
|
@ -27,8 +27,10 @@ class Settings(QSettings):
|
||||
@property
|
||||
def plugins(self):
|
||||
with Settings(Settings.Plugin.PRE) as s:
|
||||
return s.value('plugins', [])
|
||||
|
||||
pl = s.value('plugins', [])
|
||||
if pl is None:
|
||||
return []
|
||||
return pl
|
||||
@plugins.setter
|
||||
def plugins(self, value):
|
||||
with Settings(Settings.Plugin.PRE) as s:
|
||||
|
Loading…
x
Reference in New Issue
Block a user