rename.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. from PyQt5.QtWidgets import QFileDialog, QDialog, QLineEdit, QHBoxLayout, QVBoxLayout, QLabel, QPushButton, QMessageBox
  2. from PyQt5.QtCore import QTimer
  3. from .datasource import change_datasource
  4. from .uri import Uri
  5. from .file_manager import FileManager
  6. from file_management.providers.errors import FileDoesNotExistError, LayerOpenError
  7. from qgis.core import QgsProject
  8. def rename(view, iface, rename_type, settings):
  9. layer = view.currentLayer()
  10. if layer is None:
  11. return
  12. uri = Uri(layer)
  13. renamer = Renamer(view, iface, uri, rename_type)
  14. if not renamer.is_valid():
  15. QMessageBox.critical(iface.mainWindow(), 'Rename Layer',
  16. f'Layer format not supported yet: {uri.ext()[1:].upper()}')
  17. return
  18. target_uri = renamer.file_dialog()
  19. if not target_uri:
  20. return
  21. if renamer.exists(target_uri):
  22. overwrite = renamer.ask_overwrite(target_uri, iface)
  23. if not overwrite:
  24. return
  25. try:
  26. renamer.delete(target_uri)
  27. except LayerOpenError:
  28. if rename_type == 'layer':
  29. QMessageBox.critical(iface.mainWindow(), 'Rename Layer',
  30. f'Target layer {target_uri.layer_name()} is currently open in QGIS. '
  31. 'Remove open layers before overwriting')
  32. elif rename_type == 'database':
  33. QMessageBox.critical(iface.mainWindow(), 'Rename Layer',
  34. f'Target database {target_uri.database()} has layers open in QGIS. '
  35. 'Remove open layers before overwriting')
  36. return
  37. except PermissionError:
  38. QMessageBox.critical(iface.mainWindow(), 'Rename Layer', 'Target layer locked by another process')
  39. return
  40. renamer.rename(target_uri)
  41. class Renamer:
  42. def __init__(self, view=None, iface=None, uri=None, rename_type=None):
  43. self._view = view
  44. self._iface = iface
  45. self._timer = QTimer()
  46. self._timer.setSingleShot(True)
  47. self._timer.setInterval(300)
  48. self._renamed_layers = []
  49. self._src_uri = uri
  50. self._file_manager = FileManager(self._src_uri)
  51. self._rename_type = rename_type
  52. def is_valid(self):
  53. return self._file_manager.is_valid()
  54. def file_dialog(self):
  55. # assumed to be QgsVectorLayer
  56. if self._src_uri.is_memory_layer() or (self._src_uri.is_database() and self._rename_type == 'layer'):
  57. dialog = QDialog(self._iface.mainWindow())
  58. label = QLabel()
  59. line_edit = QLineEdit()
  60. layout = QVBoxLayout()
  61. label.setText(f'Choose new name for: {self._src_uri.layer_name()}')
  62. line_edit.setText(self._src_uri.layer_name())
  63. line_edit.selectAll()
  64. layout.addWidget(label)
  65. layout.addWidget(line_edit)
  66. button_layout = QHBoxLayout()
  67. ok_btn = QPushButton('Ok')
  68. cancel_btn = QPushButton('Cancel')
  69. button_layout.addStretch()
  70. button_layout.addWidget(ok_btn)
  71. button_layout.addWidget(cancel_btn)
  72. layout.addLayout(button_layout)
  73. dialog.setLayout(layout)
  74. ok_btn.clicked.connect(dialog.accept)
  75. cancel_btn.clicked.connect(dialog.reject)
  76. dialog.exec_()
  77. if dialog.result() == QDialog.Rejected:
  78. return
  79. target_file = self._src_uri.database()
  80. target_name = line_edit.text()
  81. if not target_name or target_name == self._src_uri.layer_name():
  82. return
  83. else:
  84. ext = self._src_uri.ext()
  85. filter_ = f'{self._file_manager.provider_name()} (*{ext.lower()} *{ext.upper()})'
  86. target_file = QFileDialog.getSaveFileName(self._iface.mainWindow(), 'Rename',
  87. self._src_uri.database(), filter_)[0]
  88. target_name = None
  89. if self._src_uri.is_database():
  90. target_name = self._src_uri.layer_name()
  91. if not target_file:
  92. return
  93. target_uri = Uri()
  94. target_uri.set_database(target_file)
  95. target_uri.set_layer_name(target_name)
  96. target_uri.build(self._src_uri)
  97. return target_uri
  98. def exists(self, target_uri):
  99. if self._rename_type == 'layer':
  100. return self._layer_exists(target_uri)
  101. elif self._rename_type == 'database':
  102. return self._database_exists(target_uri)
  103. def _layer_exists(self, target_uri):
  104. return self._file_manager.layer_exists(target_uri)
  105. def _database_exists(self, target_uri):
  106. return self._file_manager.database_exists(target_uri)
  107. def ask_overwrite(self, target_uri, iface):
  108. if self._rename_type == 'layer':
  109. return self._ask_overwrite_layer(target_uri, iface)
  110. elif self._rename_type == 'database':
  111. return self._ask_overwrite_database(target_uri, iface)
  112. def _ask_overwrite_layer(self, target_uri, iface):
  113. return self._file_manager.ask_overwrite_layer(target_uri, iface)
  114. def _ask_overwrite_database(self, target_uri, iface):
  115. return self._file_manager.ask_overwrite_database(target_uri, iface)
  116. def delete(self, target_uri):
  117. if self._rename_type == 'layer':
  118. self._delete_layer(target_uri)
  119. elif self._rename_type == 'database':
  120. self._delete_database(target_uri)
  121. def _delete_layer(self, target_uri):
  122. # if existing layer is open in workspace, stop overwrite
  123. for layer_id, layer_ in QgsProject.instance().mapLayers().items():
  124. if Uri(layer_).layer_uri() == target_uri.layer_uri():
  125. raise LayerOpenError
  126. self._file_manager.delete_layer(target_uri)
  127. def _delete_database(self, target_uri):
  128. for layer_id, layer_ in QgsProject.instance().mapLayers().items():
  129. if Uri(layer_).database() == target_uri.database():
  130. raise LayerOpenError
  131. self._file_manager.delete_database(target_uri)
  132. def rename(self, target_uri):
  133. if self._rename_type == 'layer':
  134. self._rename_layer(target_uri)
  135. elif self._rename_type == 'database':
  136. self._rename_database(target_uri)
  137. def _rename_database(self, target_uri):
  138. legint = QgsProject.instance().layerTreeRoot()
  139. for layer_id, layer_ in QgsProject.instance().mapLayers().items():
  140. if Uri(layer_).database() == self._src_uri.database():
  141. tree_layer = legint.findLayer(layer_id)
  142. if tree_layer is not None:
  143. # change datasource
  144. target_uri_ = Uri(layer_)
  145. target_uri_.set_database(target_uri.database())
  146. target_uri_.build()
  147. change_datasource(layer_, target_uri_)
  148. layer_.dataProvider().reloadData()
  149. self._renamed_layers.append((layer_, target_uri_))
  150. self._timer.timeout.connect(lambda: self.rename_file())
  151. self._timer.start()
  152. def _rename_layer(self, target_uri):
  153. # rename all layers in Legend that use this datasource
  154. legint = QgsProject.instance().layerTreeRoot()
  155. for layer_id, layer_ in QgsProject.instance().mapLayers().items():
  156. if Uri(layer_).layer_uri() == self._src_uri.layer_uri():
  157. tree_layer = legint.findLayer(layer_id)
  158. if tree_layer is not None:
  159. tree_layer.setName(target_uri.layer_name())
  160. # change datasource
  161. target_uri_ = Uri(uri=target_uri)
  162. target_uri_.build(Uri(layer_))
  163. change_datasource(layer_, target_uri_)
  164. layer_.dataProvider().reloadData()
  165. self._renamed_layers.append((layer_, target_uri_))
  166. self._timer.timeout.connect(lambda: self.rename_file())
  167. self._timer.start()
  168. def rename_file(self):
  169. for i, (layer, target_uri) in enumerate(self._renamed_layers):
  170. if i == 0:
  171. # rename on disk
  172. try:
  173. if self._rename_type == 'layer':
  174. self._file_manager.rename_layer(target_uri)
  175. elif self._rename_type == 'database':
  176. self._file_manager.rename_database(target_uri)
  177. except FileDoesNotExistError:
  178. print('source file does not exist error')
  179. except FileExistsError:
  180. print('destination file exists error')
  181. except PermissionError:
  182. print('permission error')
  183. # refresh again from source
  184. change_datasource(layer, target_uri)
  185. layer.dataProvider().forceReload()
  186. layer.dataProvider().reloadData()
  187. layer.reload()
  188. self._view.refreshLayerSymbology(layer.id())
  189. self._iface.mapCanvas().refresh()