fix plugin bugs

This commit is contained in:
copper 2022-05-05 20:08:52 +08:00
parent 00ffe0c03f
commit df27d40587
16 changed files with 262 additions and 71 deletions

1
3rd/about/__init__.py Normal file
View File

@ -0,0 +1 @@
from about.main import *

68
3rd/about/main.py Normal file
View 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()

View File

@ -0,0 +1 @@
from about.main import *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File