from PyQt5.QtWidgets import QFileDialog, QDialog, QLineEdit, QHBoxLayout, QVBoxLayout, QLabel, QPushButton, QMessageBox from PyQt5.QtCore import QTimer from .datasource import change_datasource from .uri import Uri from .file_manager import FileManager from file_management.providers.errors import FileDoesNotExistError, LayerOpenError from qgis.core import QgsProject def rename(view, iface, rename_type, settings): layer = view.currentLayer() if layer is None: return uri = Uri(layer) renamer = Renamer(view, iface, uri, rename_type) if not renamer.is_valid(): QMessageBox.critical(iface.mainWindow(), 'Rename Layer', f'Layer format not supported yet: {uri.ext()[1:].upper()}') return target_uri = renamer.file_dialog() if not target_uri: return if renamer.exists(target_uri): overwrite = renamer.ask_overwrite(target_uri, iface) if not overwrite: return try: renamer.delete(target_uri) except LayerOpenError: if rename_type == 'layer': QMessageBox.critical(iface.mainWindow(), 'Rename Layer', f'Target layer {target_uri.layer_name()} is currently open in QGIS. ' 'Remove open layers before overwriting') elif rename_type == 'database': QMessageBox.critical(iface.mainWindow(), 'Rename Layer', f'Target database {target_uri.database()} has layers open in QGIS. ' 'Remove open layers before overwriting') return except PermissionError: QMessageBox.critical(iface.mainWindow(), 'Rename Layer', 'Target layer locked by another process') return renamer.rename(target_uri) class Renamer: def __init__(self, view=None, iface=None, uri=None, rename_type=None): self._view = view self._iface = iface self._timer = QTimer() self._timer.setSingleShot(True) self._timer.setInterval(300) self._renamed_layers = [] self._src_uri = uri self._file_manager = FileManager(self._src_uri) self._rename_type = rename_type def is_valid(self): return self._file_manager.is_valid() def file_dialog(self): # assumed to be QgsVectorLayer if self._src_uri.is_memory_layer() or (self._src_uri.is_database() and self._rename_type == 'layer'): dialog = QDialog(self._iface.mainWindow()) label = QLabel() line_edit = QLineEdit() layout = QVBoxLayout() label.setText(f'Choose new name for: {self._src_uri.layer_name()}') line_edit.setText(self._src_uri.layer_name()) line_edit.selectAll() layout.addWidget(label) layout.addWidget(line_edit) button_layout = QHBoxLayout() ok_btn = QPushButton('Ok') cancel_btn = QPushButton('Cancel') button_layout.addStretch() button_layout.addWidget(ok_btn) button_layout.addWidget(cancel_btn) layout.addLayout(button_layout) dialog.setLayout(layout) ok_btn.clicked.connect(dialog.accept) cancel_btn.clicked.connect(dialog.reject) dialog.exec_() if dialog.result() == QDialog.Rejected: return target_file = self._src_uri.database() target_name = line_edit.text() if not target_name or target_name == self._src_uri.layer_name(): return else: ext = self._src_uri.ext() filter_ = f'{self._file_manager.provider_name()} (*{ext.lower()} *{ext.upper()})' target_file = QFileDialog.getSaveFileName(self._iface.mainWindow(), 'Rename', self._src_uri.database(), filter_)[0] target_name = None if self._src_uri.is_database(): target_name = self._src_uri.layer_name() if not target_file: return target_uri = Uri() target_uri.set_database(target_file) target_uri.set_layer_name(target_name) target_uri.build(self._src_uri) return target_uri def exists(self, target_uri): if self._rename_type == 'layer': return self._layer_exists(target_uri) elif self._rename_type == 'database': return self._database_exists(target_uri) def _layer_exists(self, target_uri): return self._file_manager.layer_exists(target_uri) def _database_exists(self, target_uri): return self._file_manager.database_exists(target_uri) def ask_overwrite(self, target_uri, iface): if self._rename_type == 'layer': return self._ask_overwrite_layer(target_uri, iface) elif self._rename_type == 'database': return self._ask_overwrite_database(target_uri, iface) def _ask_overwrite_layer(self, target_uri, iface): return self._file_manager.ask_overwrite_layer(target_uri, iface) def _ask_overwrite_database(self, target_uri, iface): return self._file_manager.ask_overwrite_database(target_uri, iface) def delete(self, target_uri): if self._rename_type == 'layer': self._delete_layer(target_uri) elif self._rename_type == 'database': self._delete_database(target_uri) def _delete_layer(self, target_uri): # if existing layer is open in workspace, stop overwrite for layer_id, layer_ in QgsProject.instance().mapLayers().items(): if Uri(layer_).layer_uri() == target_uri.layer_uri(): raise LayerOpenError self._file_manager.delete_layer(target_uri) def _delete_database(self, target_uri): for layer_id, layer_ in QgsProject.instance().mapLayers().items(): if Uri(layer_).database() == target_uri.database(): raise LayerOpenError self._file_manager.delete_database(target_uri) def rename(self, target_uri): if self._rename_type == 'layer': self._rename_layer(target_uri) elif self._rename_type == 'database': self._rename_database(target_uri) def _rename_database(self, target_uri): legint = QgsProject.instance().layerTreeRoot() for layer_id, layer_ in QgsProject.instance().mapLayers().items(): if Uri(layer_).database() == self._src_uri.database(): tree_layer = legint.findLayer(layer_id) if tree_layer is not None: # change datasource target_uri_ = Uri(layer_) target_uri_.set_database(target_uri.database()) target_uri_.build() change_datasource(layer_, target_uri_) layer_.dataProvider().reloadData() self._renamed_layers.append((layer_, target_uri_)) self._timer.timeout.connect(lambda: self.rename_file()) self._timer.start() def _rename_layer(self, target_uri): # rename all layers in Legend that use this datasource legint = QgsProject.instance().layerTreeRoot() for layer_id, layer_ in QgsProject.instance().mapLayers().items(): if Uri(layer_).layer_uri() == self._src_uri.layer_uri(): tree_layer = legint.findLayer(layer_id) if tree_layer is not None: tree_layer.setName(target_uri.layer_name()) # change datasource target_uri_ = Uri(uri=target_uri) target_uri_.build(Uri(layer_)) change_datasource(layer_, target_uri_) layer_.dataProvider().reloadData() self._renamed_layers.append((layer_, target_uri_)) self._timer.timeout.connect(lambda: self.rename_file()) self._timer.start() def rename_file(self): for i, (layer, target_uri) in enumerate(self._renamed_layers): if i == 0: # rename on disk try: if self._rename_type == 'layer': self._file_manager.rename_layer(target_uri) elif self._rename_type == 'database': self._file_manager.rename_database(target_uri) except FileDoesNotExistError: print('source file does not exist error') except FileExistsError: print('destination file exists error') except PermissionError: print('permission error') # refresh again from source change_datasource(layer, target_uri) layer.dataProvider().forceReload() layer.dataProvider().reloadData() layer.reload() self._view.refreshLayerSymbology(layer.id()) self._iface.mapCanvas().refresh()