dlg_main.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. """
  2. """
  3. from core import mncheck, checking
  4. import logging
  5. from qgis.core import QgsProject
  6. from PyQt5 import QtWidgets
  7. from PyQt5 import uic
  8. from PyQt5.Qt import Qt
  9. from PyQt5.QtCore import QObject, pyqtSignal
  10. from PyQt5.QtGui import QIcon, QPixmap
  11. from PyQt5.QtWidgets import QApplication, QTreeWidgetItem
  12. from core.checking import TestResult
  13. from core.constants import MAIN, RSCDIR, VERSION
  14. from ui.dlg_contact import DlgContact
  15. logger = logging.getLogger("mncheck")
  16. Ui_Main, _ = uic.loadUiType(MAIN / 'ui'/ 'dlg_main.ui')
  17. class QComlink(QObject):
  18. started_test = pyqtSignal(TestResult)
  19. ended_test = pyqtSignal(TestResult)
  20. def _started_test(self, test):
  21. self.started_test.emit(test)
  22. def _ended_test(self, test):
  23. self.ended_test.emit(test)
  24. class DlgMain(QtWidgets.QDialog):
  25. def __init__(self, iface, parent=None):
  26. super().__init__(parent)
  27. self.available_schemas = mncheck.list_schemas()
  28. self.iface = iface
  29. self._contact_dlg = None
  30. self.schema_lib = None
  31. self.user_conf = {}
  32. self.createWidgets()
  33. def createWidgets(self):
  34. """ set up the interface """
  35. self.ui = Ui_Main()
  36. self.ui.setupUi(self)
  37. self.user_conf = mncheck.get_user_data()
  38. self.setWindowTitle(f"MnCheck v{VERSION}")
  39. self.setWindowIcon(QIcon(MAIN / "icon.png"))
  40. self.ui.lbl_mn_logo.setPixmap(QPixmap(RSCDIR / "mn_logo_mini.png"))
  41. self.ui.btn_run.setIcon(QIcon(RSCDIR / "play.png"))
  42. self.ui.btn_run.clicked.connect(self.run)
  43. self.ui.btn_contact.clicked.connect(self.show_contact)
  44. for i, schema_name in enumerate(self.available_schemas):
  45. s = mncheck.get_schema(schema_name)
  46. try:
  47. self.ui.cbb_schemas.addItem(s.SCHEMA_NAME, i)
  48. except AttributeError:
  49. self.ui.cbb_schemas.addItem(schema_name, i)
  50. if 'current_schema' in self.user_conf and self.user_conf['current_schema'] == schema_name:
  51. self.ui.cbb_schemas.setCurrentIndex(i)
  52. self.ui.cbb_schemas.currentIndexChanged.connect(self.update_layers_list)
  53. self.ui.progress_bar.setVisible(False)
  54. self.ui.tree_report.setColumnWidth(0, 35)
  55. self.ui.tree_report.itemClicked.connect(self.tree_item_clicked)
  56. self.ui.tree_report.expanded.connect(self.tree_item_expanded)
  57. QgsProject.instance().layersAdded.connect(self.update_layers_list)
  58. QgsProject.instance().layersRemoved.connect(self.update_layers_list)
  59. self.update_layers_list()
  60. def tree_item_expanded(self, _):
  61. self.tree_auto_resize()
  62. def tree_auto_resize(self):
  63. self.ui.tree_report.resizeColumnToContents(1)
  64. self.ui.tree_report.resizeColumnToContents(2)
  65. self.ui.tree_report.setColumnWidth(1, self.ui.tree_report.columnWidth(1) + 20)
  66. self.ui.tree_report.setColumnWidth(2, self.ui.tree_report.columnWidth(2) + 20)
  67. def current_schema_name(self):
  68. return self.available_schemas[int(self.ui.cbb_schemas.itemData(self.ui.cbb_schemas.currentIndex()))]
  69. def init_progress_bar(self, value=0, minimum=0, maximum=100):
  70. self.ui.progress_bar.setMinimum(minimum)
  71. self.ui.progress_bar.setValue(value)
  72. self.ui.progress_bar.setMaximum(maximum)
  73. def test_started(self, test):
  74. logger.debug("* %s - start test", test.name)
  75. def test_ended(self, test):
  76. logger.debug("* %s - %s", test.name, test.status_str)
  77. if test.status == checking.ERROR:
  78. try:
  79. logger.debug("%s", "\n".join([e.info['exc_info'] for e in test.errors]))
  80. except KeyError:
  81. pass
  82. self.ui.progress_bar.setValue(self.ui.progress_bar.value() + 1)
  83. self.ui.progress_bar.update()
  84. def update_layers_list(self):
  85. schema_name = self.current_schema_name()
  86. logger.info("Selected schema: %s", schema_name)
  87. schema = mncheck.get_schema(schema_name)
  88. logger.info("Expected layers: %s", str([model.layername for model in schema.models]))
  89. found = [layer.name().lower() for layer in QgsProject.instance().mapLayers().values()]
  90. logger.info("Found layers: %s", str(found))
  91. self.ui.btn_run.setEnabled(True)
  92. self.ui.tree_report.clear()
  93. for model in schema.models:
  94. item = QTreeWidgetItem()
  95. if model.layername.lower() in found:
  96. item.setIcon(0, QIcon(QPixmap(RSCDIR / "ok_16.png")))
  97. item.setText(1, model.layername)
  98. else:
  99. item.setText(1, f"{model.layername}: La couche est introuvable")
  100. if model.required:
  101. item.setIcon(0, QIcon(QPixmap(RSCDIR / "error_16.png")))
  102. self.ui.btn_run.setEnabled(False)
  103. else:
  104. item.setIcon(0, QIcon(QPixmap(RSCDIR / "warning_16.png")))
  105. self.ui.tree_report.addTopLevelItem(item)
  106. self.tree_auto_resize()
  107. def run(self):
  108. QApplication.setOverrideCursor(Qt.WaitCursor)
  109. self.ui.tree_report.clear()
  110. try:
  111. schema_name = self.current_schema_name()
  112. schema = mncheck.get_schema(schema_name)
  113. if not schema:
  114. logger.error("Aucun schéma sélectionné - Opération annulée")
  115. return
  116. if not schema.checkers:
  117. logger.error("Aucun testeur trouvé dans le schéma")
  118. return
  119. for checker_cls in schema.checkers:
  120. logger.info(f"Execution du checker {checker_cls.__name__}")
  121. self._run(checker_cls)
  122. except:
  123. raise
  124. finally:
  125. self.tree_auto_resize()
  126. self.ui.progress_bar.setVisible(False)
  127. QApplication.restoreOverrideCursor()
  128. def _run(self, checker_cls):
  129. checker = checker_cls()
  130. checker.comlink = QComlink()
  131. checker.comlink.started_test.connect(self.test_started)
  132. checker.comlink.ended_test.connect(self.test_ended)
  133. self.ui.progress_bar.setVisible(True)
  134. self.init_progress_bar(maximum=len(checker.tests))
  135. results = checker.run()
  136. for result in results:
  137. topitem = QTreeWidgetItem()
  138. if result.status == checking.SUCCESS:
  139. topitem.setIcon(0, QIcon(QPixmap(RSCDIR / "ok_16.png")))
  140. topitem.setText(1, f"{result.title}")
  141. elif result.status == checking.FAILURE:
  142. topitem.setIcon(0, QIcon(QPixmap(RSCDIR / "error_16.png")))
  143. topitem.setText(1, f"{result.title} ({len(result.errors)})")
  144. elif result.status == checking.ERROR:
  145. topitem.setIcon(0, QIcon(QPixmap(RSCDIR / "warning_16.png")))
  146. topitem.setText(1, f"{result.title} [Erreur]")
  147. else:
  148. topitem.setText(1, f"{result.title} [Erreur: aucun résultat]")
  149. topitem.setToolTip(0, f"{result.status_str}")
  150. topitem.setToolTip(1, f"{result.description}\n[test: {result.name}]")
  151. self.ui.tree_report.addTopLevelItem(topitem)
  152. for err in result.errors:
  153. erritem = QTreeWidgetItem()
  154. msg = err.message
  155. erritem.setText(1, msg)
  156. if "exc_info" in err.info:
  157. erritem.setData(1, Qt.UserRole, err)
  158. elif "model" in err.info:
  159. data = err.info["model"]
  160. erritem.setText(2, f"{data.layername}".upper())
  161. erritem.setData(1, Qt.UserRole, data)
  162. elif "item" in err.info:
  163. data = err.info["item"]
  164. erritem.setText(2, f"{data}")
  165. erritem.setData(1, Qt.UserRole, data)
  166. font = erritem.font(2)
  167. font.setItalic(True)
  168. erritem.setFont(2, font)
  169. topitem.addChild(erritem)
  170. def tree_item_clicked(self, item, _):
  171. data = item.data(1, Qt.UserRole)
  172. if not data:
  173. return
  174. elif hasattr(data, "_feature"):
  175. self.zoom_to_feature(data.layer, data._feature)
  176. elif hasattr(data, "layer"):
  177. self.select_layer(data.layer)
  178. def select_layer(self, layer):
  179. self.iface.setActiveLayer(layer)
  180. def zoom_to_feature(self, layer, feature):
  181. self.iface.setActiveLayer(layer)
  182. self.iface.mapCanvas().zoomToFeatureExtent(feature.geometry().boundingBox())
  183. layer.removeSelection()
  184. layer.select(feature.id())
  185. def show_contact(self):
  186. try:
  187. self._contact_dlg.close()
  188. except:
  189. pass
  190. self._contact_dlg = DlgContact()
  191. self._contact_dlg.show()
  192. self._contact_dlg.exec_()
  193. def store_config(self):
  194. user_conf = mncheck.get_user_data()
  195. user_conf["current_schema"] = self.current_schema_name()
  196. mncheck.dump_user_data(user_conf)
  197. def closeEvent(self, event):
  198. self.store_config()
  199. super().closeEvent(event)