Untitled

 avatar
unknown
plain_text
7 days ago
18 kB
1
No Index
from qgis.PyQt.QtCore import Qt, QSettings, QObject, QItemSelectionModel, QCoreApplication
from qgis.PyQt.QtWidgets import (
    QLineEdit, QPushButton, QVBoxLayout, QWidget,
    QHBoxLayout, QDockWidget, QAbstractScrollArea, QAbstractItemView
)
from qgis.core import (
    QgsProject, QgsLayerTreeGroup, QgsLayerTreeLayer, QgsMessageLog, Qgis
)
from qgis.gui import QgsLayerTreeView

class LayerSearchPlugin(QObject):

    def tr(self, message):
        """Get the translation for a string using Qt translation API."""
        return QCoreApplication.translate('LayerSearchPlugin', message)

    def __init__(self, iface):
        super().__init__()
        self.iface = iface
        QgsMessageLog.logMessage(
            "LayerSearch Plugin: Constructor called",
            'LayerSearch',
            level=Qgis.Info
        )
        self.settings = QSettings("QGIS", "LayerSearchPlugin")
        self._original_expanded = {}
        self.searchWidget = None
        self.searchBox = None
        self.clearButton = None
        self.modified_dock_widget = None
        self.original_dock_widget_content = None

    def initGui(self):
        QgsMessageLog.logMessage(
            "LayerSearch Plugin: Initializing GUI",
            'LayerSearch',
            level=Qgis.Info
        )
        self.searchWidget = QWidget()
        self.searchWidget.setObjectName("LayerSearchPluginWidget")
        layout = QHBoxLayout(self.searchWidget)
        layout.setContentsMargins(2, 2, 2, 2)
        layout.setSpacing(4)

        self.searchBox = QLineEdit()
        self.searchBox.setPlaceholderText(self.tr("Search layers..."))
        self.searchBox.textChanged.connect(self.on_search_text_changed)

        self.clearButton = QPushButton(self.tr("Clear"))
        self.clearButton.clicked.connect(self.clear_search)
        font_metrics = self.iface.mainWindow().fontMetrics()
        clear_text_width = font_metrics.horizontalAdvance(self.tr("Clear") + "  ")
        self.clearButton.setMaximumWidth(clear_text_width * 2) 

        layout.addWidget(self.searchBox)
        layout.addWidget(self.clearButton)

        layer_tree_view = self.iface.layerTreeView()
        if not layer_tree_view:
            QgsMessageLog.logMessage(
                "LayerSearch Plugin: Could not find QgsLayerTreeView (iface.layerTreeView() is None). Cannot add search bar.",
                'LayerSearch',
                level=Qgis.Critical
            )
            return

        target_dock = None
        max_parents_to_check = 10
        
        layers_dock_by_name = self.iface.mainWindow().findChild(QDockWidget, "Layers")
        if layers_dock_by_name:
            is_parent_check_widget = layer_tree_view
            is_found_in_dock_by_name = False
            for _ in range(max_parents_to_check):
                if not is_parent_check_widget: break
                if is_parent_check_widget.parentWidget() == layers_dock_by_name:
                    target_dock = layers_dock_by_name
                    is_found_in_dock_by_name = True
                    break
                is_parent_check_widget = is_parent_check_widget.parentWidget()
            if is_found_in_dock_by_name:
                QgsMessageLog.logMessage(
                    f"LayerSearch Plugin: Target Layers Dock identified by objectName 'Layers': {target_dock.windowTitle()}",
                    'LayerSearch',
                    level=Qgis.Info
                )
            else:
                QgsMessageLog.logMessage(
                    f"LayerSearch Plugin: Dock with objectName 'Layers' found, but it's not the parent of the active LayerTreeView. Fallback search needed.",
                    'LayerSearch',
                    level=Qgis.Warning
                )
                target_dock = None

        if not target_dock:
            QgsMessageLog.logMessage(
                "LayerSearch Plugin: LayersDock not found by objectName 'Layers' or it's not parent. Iterating all docks to find parent of LayerTreeView.",
                'LayerSearch',
                level=Qgis.Info
            )
            for dock_widget_candidate in self.iface.mainWindow().findChildren(QDockWidget):
                is_parent_check_widget = layer_tree_view
                is_found = False
                for _ in range(max_parents_to_check):
                    if not is_parent_check_widget: break
                    if is_parent_check_widget.parentWidget() == dock_widget_candidate:
                        target_dock = dock_widget_candidate
                        is_found = True
                        break
                    is_parent_check_widget = is_parent_check_widget.parentWidget()
                if is_found:
                    QgsMessageLog.logMessage(
                        f"LayerSearch Plugin: Found Layers Dock by iterating and checking parentage: {target_dock.windowTitle()} (ObjectName: {target_dock.objectName()})",
                        'LayerSearch',
                        level=Qgis.Info
                    )
                    break

        if target_dock:
            QgsMessageLog.logMessage(
                f"LayerSearch Plugin: Final Target Layers Dock: {target_dock.windowTitle()} (ObjectName: {target_dock.objectName()})",
                'LayerSearch',
                level=Qgis.Info
            )
            original_dock_content = target_dock.widget()

            if original_dock_content and original_dock_content.objectName() == "LayerSearchContainer":
                QgsMessageLog.logMessage(
                    "LayerSearch Plugin: Layers panel already has LayerSearchContainer. Restoring original content before re-adding.",
                    'LayerSearch',
                    level=Qgis.Warning
                )
                old_container_layout = original_dock_content.layout()
                if old_container_layout and old_container_layout.count() == 2:
                    original_dock_content_restored = old_container_layout.itemAt(1).widget()
                    if original_dock_content_restored:
                        old_search_widget = old_container_layout.itemAt(0).widget()
                        if old_search_widget:
                            old_search_widget.setParent(None)
                            old_search_widget.deleteLater()
                        target_dock.setWidget(original_dock_content_restored)
                        original_dock_content = original_dock_content_restored
                        QgsMessageLog.logMessage(
                            "LayerSearch Plugin: Restored original widget from previous LayerSearchContainer.",
                            'LayerSearch',
                            level=Qgis.Info
                        )
                    else:
                        QgsMessageLog.logMessage(
                            "LayerSearch Plugin: Could not restore original widget from existing container. Aborting UI setup.",
                            'LayerSearch',
                            level=Qgis.Critical
                        )
                        return
                else:
                    QgsMessageLog.logMessage(
                        "LayerSearch Plugin: Existing LayerSearchContainer has unexpected layout. Aborting UI setup.",
                        'LayerSearch',
                        level=Qgis.Critical
                    )
                    return

            self.original_dock_widget_content = original_dock_content
            self.modified_dock_widget = target_dock

            container = QWidget()
            container.setObjectName("LayerSearchContainer")
            vlay = QVBoxLayout(container)
            vlay.setContentsMargins(0, 0, 0, 0)
            vlay.setSpacing(0)
            vlay.addWidget(self.searchWidget)
            vlay.addWidget(self.original_dock_widget_content)
            vlay.setStretchFactor(self.searchWidget, 0)
            vlay.setStretchFactor(self.original_dock_widget_content, 1)
            target_dock.setWidget(container)
            QgsMessageLog.logMessage(
                "LayerSearch Plugin: Search widget added to Layers Dock.",
                'LayerSearch',
                level=Qgis.Info
            )
        else:
            QgsMessageLog.logMessage(
                self.tr("LayerSearch Plugin: Could not find the Layers Panel (QDockWidget) to attach the search bar. The plugin will not be active."),
                self.tr('Layer Search Plugin Error'),
                level=Qgis.Critical
            )
            self.iface.messageBar().pushMessage(
                self.tr("Error"),
                self.tr("Layer Search Plugin could not find the Layers Panel and will not be active."),
                level=Qgis.Critical,
                duration=10
            )

    def find_matching_layers(self, node, search_text):
        matches = []
        if isinstance(node, QgsLayerTreeLayer):
            # Usar node.name() para obtener el nombre que se muestra en el árbol de capas
            layer_display_name = node.name() 
            if layer_display_name and search_text.lower() in layer_display_name.lower():
                matches.append(node)
        # Recursivamente buscar en todos los hijos
        for child in node.children():
            matches.extend(self.find_matching_layers(child, search_text))
        return matches

    def on_search_text_changed(self, text):
        root = QgsProject.instance().layerTreeRoot()
        main_layer_tree_view = self.iface.layerTreeView()

        if not main_layer_tree_view:
            QgsMessageLog.logMessage(self.tr("LayerSearch: Main QgsLayerTreeView (iface.layerTreeView()) not found."), 'LayerSearch', level=Qgis.Warning)
            return
        
        if not self.modified_dock_widget:
            return

        view_to_process = None
        is_descendant = False
        parent_widget = main_layer_tree_view
        max_checks = 10
        count = 0
        while parent_widget and count < max_checks:
            if parent_widget == self.modified_dock_widget:
                is_descendant = True
                break
            parent_widget = parent_widget.parentWidget()
            count += 1
        
        if is_descendant:
            view_to_process = main_layer_tree_view
        else:
            QgsMessageLog.logMessage(self.tr("LayerSearch: Main LayerTreeView is not a descendant of the modified Layers Dock. Skipping search update."), 'LayerSearch', level=Qgis.Warning)
            return

        view = view_to_process
        view.selectionModel().clearSelection()

        if not text:
            if view in self._original_expanded:
                self.restore_expansion_state(view, root)
                del self._original_expanded[view]
            self.collapse_all_recursively(view, root)
            if view in self._original_expanded:
                self.restore_expansion_state(view, root)
            return

        if view not in self._original_expanded:
            self._original_expanded[view] = set()
            self.store_expanded_groups(root, view)

        matches = self.find_matching_layers(root, text)

        groups_to_expand = set()
        for node_match in matches: # Renombrada la variable para evitar confusión con el 'node' de los bucles de expansión
            parent = node_match.parent()
            while parent and isinstance(parent, QgsLayerTreeGroup):
                groups_to_expand.add(parent)
                parent = parent.parent()

        self.adjust_group_expansion(view, root, groups_to_expand, bool(text))

        selection_model = view.selectionModel()
        for node_match in matches: # Renombrada la variable
            idx = view.node2index(node_match)
            if idx.isValid():
                selection_model.select(
                    idx,
                    QItemSelectionModel.Select | QItemSelectionModel.Rows
                )

        if matches:
            first_match_idx = view.node2index(matches[0])
            if first_match_idx.isValid():
                view.scrollTo(first_match_idx, QAbstractItemView.ScrollHint.PositionAtTop)


    def store_expanded_groups(self, node, view):
        if isinstance(node, QgsLayerTreeGroup):
            idx = view.node2index(node)
            if idx.isValid() and view.isExpanded(idx):
                self._original_expanded[view].add(node)
        for child in node.children():
            self.store_expanded_groups(child, view)


    def adjust_group_expansion(self, view, node, groups_to_expand, is_searching):
        if not isinstance(node, QgsLayerTreeGroup):
            return

        root = QgsProject.instance().layerTreeRoot()
        idx = view.node2index(node)

        if not idx.isValid():
            return

        if node is not root:
            if node in groups_to_expand:
                view.expand(idx)
            elif is_searching:
                original_set = self._original_expanded.get(view, set())
                if node not in original_set:
                    view.collapse(idx)
        
        for child in node.children():
            self.adjust_group_expansion(view, child, groups_to_expand, is_searching)

    def restore_expansion_state(self, view, node):
        if not isinstance(node, QgsLayerTreeGroup):
            return

        root = QgsProject.instance().layerTreeRoot()
        idx = view.node2index(node)

        if not idx.isValid():
            return

        if node is not root:
            original_set = self._original_expanded.get(view, set())
            if node in original_set:
                view.expand(idx)
            else:
                view.collapse(idx)
        
        for child in node.children():
            self.restore_expansion_state(view, child)
            
    def collapse_all_recursively(self, view, node):
        if isinstance(node, QgsLayerTreeGroup):
            idx = view.node2index(node)
            if idx.isValid() and node != QgsProject.instance().layerTreeRoot():
                view.collapse(idx)
        for child in node.children():
            self.collapse_all_recursively(view, child)


    def clear_search(self):
        QgsMessageLog.logMessage(
            "LayerSearch Plugin: clear_search called",
            'LayerSearch',
            level=Qgis.Info
        )
        if self.searchBox:
            self.searchBox.clear()
        else:
            root = QgsProject.instance().layerTreeRoot()
            main_layer_tree_view = self.iface.layerTreeView()
            if not main_layer_tree_view or not self.modified_dock_widget:
                return

            is_descendant = False
            parent_widget = main_layer_tree_view
            max_checks = 10
            count = 0
            while parent_widget and count < max_checks:
                if parent_widget == self.modified_dock_widget:
                    is_descendant = True
                    break
                parent_widget = parent_widget.parentWidget()
                count += 1
            
            if is_descendant:
                view = main_layer_tree_view
                view.selectionModel().clearSelection()
                if view in self._original_expanded: # Comprobar si la vista existe como clave antes de acceder
                    original_view_expansion = self._original_expanded.get(view)
                    if original_view_expansion is not None:
                        self.restore_expansion_state(view, root)
                        # Solo eliminar si estamos seguros de que la clave existe y ha sido procesada
                        if view in self._original_expanded:
                             del self._original_expanded[view]


    def unload(self):
        QgsMessageLog.logMessage(
            "LayerSearch Plugin: Unloading",
            'LayerSearch',
            level=Qgis.Info
        )

        if self.modified_dock_widget and self.original_dock_widget_content:
            current_dock_content = self.modified_dock_widget.widget()
            if current_dock_content and current_dock_content.objectName() == "LayerSearchContainer":
                if self.searchWidget:
                    self.searchWidget.setParent(None)
                self.modified_dock_widget.setWidget(self.original_dock_widget_content)
                QgsMessageLog.logMessage(
                    "LayerSearch Plugin: Restored original Layers dock content.",
                    'LayerSearch',
                    level=Qgis.Info
                )
            else:
                 QgsMessageLog.logMessage(
                    "LayerSearch Plugin: Original dock content not found or already restored. Skipping restoration.",
                    'LayerSearch',
                    level=Qgis.Warning
                )
        elif self.modified_dock_widget:
            QgsMessageLog.logMessage(
                "LayerSearch Plugin: modified_dock_widget exists but original_dock_widget_content is missing. Cannot restore.",
                'LayerSearch',
                level=Qgis.Warning
            )

        if self.searchBox:
            try:
                self.searchBox.textChanged.disconnect(self.on_search_text_changed)
            except (TypeError, RuntimeError):
                pass
        
        if self.clearButton:
            try:
                self.clearButton.clicked.disconnect(self.clear_search)
            except (TypeError, RuntimeError):
                pass
        
        if self.searchWidget:
            self.searchWidget.deleteLater()
            QgsMessageLog.logMessage(
                "LayerSearch Plugin: searchWidget scheduled for deletion.",
                'LayerSearch',
                level=Qgis.Info
            )

        self._original_expanded = {}
        self.searchWidget = None
        self.searchBox = None
        self.clearButton = None
        self.modified_dock_widget = None
        self.original_dock_widget_content = None

        QgsMessageLog.logMessage(
            "LayerSearch Plugin: Unload finished.",
            'LayerSearch',
            level=Qgis.Info
        )
Editor is loading...
Leave a Comment