Exemplo n.º 1
0
    def test_group_layer_updates_from_node(self):
        """
        Test that group layer child layers are synced correctly from the group node
        """
        group_node = QgsLayerTreeGroup('my group')
        options = QgsGroupLayer.LayerOptions(QgsCoordinateTransformContext())
        group_layer = group_node.convertToGroupLayer(options)
        self.assertFalse(group_layer.childLayers())

        layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory")
        group_node.addLayer(layer)
        self.assertEqual(group_layer.childLayers(), [layer])

        layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2",
                                "memory")
        group_node.insertLayer(0, layer2)
        self.assertEqual(group_layer.childLayers(), [layer, layer2])

        layer3 = QgsVectorLayer("Point?field=fldtxt:string", "layer3",
                                "memory")
        layer4 = QgsVectorLayer("Point?field=fldtxt:string", "layer4",
                                "memory")
        layer3_node = QgsLayerTreeLayer(layer3)
        layer4_node = QgsLayerTreeLayer(layer4)
        group_node.insertChildNodes(1, [layer3_node, layer4_node])
        self.assertEqual(group_layer.childLayers(),
                         [layer, layer4, layer3, layer2])

        layer5 = QgsVectorLayer("Point?field=fldtxt:string", "layer5",
                                "memory")
        layer5_node = QgsLayerTreeLayer(layer5)
        group_node.addChildNode(layer5_node)
        self.assertEqual(group_layer.childLayers(),
                         [layer5, layer, layer4, layer3, layer2])

        group_node.removeChildNode(layer3_node)
        self.assertEqual(group_layer.childLayers(),
                         [layer5, layer, layer4, layer2])

        group_node.removeLayer(layer)
        self.assertEqual(group_layer.childLayers(), [layer5, layer4, layer2])

        group_node.removeChildren(0, 2)
        self.assertEqual(group_layer.childLayers(), [layer5])

        group_node.removeAllChildren()
        self.assertEqual(group_layer.childLayers(), [])
class AuxiliaryWindow(QMainWindow):
    """Creates a New Canvas."""

    closed = pyqtSignal(int)

    def __init__(self, parent, geometryWin, numWin):
        def populateStatusBar():
            statusBar = self.statusBar()

            w = QCheckBox("Render", self)
            w.setObjectName('renderCheck')
            w.setToolTip("Toggle map rendering")
            w.setChecked(True)
            statusBar.addPermanentWidget(w)

            w = QCheckBox("Marker", self)
            w.setObjectName('markerCheck')
            w.setToolTip("Toggle marker with cursor position from main map")
            w.setChecked(False)
            statusBar.addPermanentWidget(w, 1)

            w = QCheckBox("Extent", self)
            w.setObjectName('extentCheck')
            w.setToolTip("Show extent of main map")
            w.setChecked(False)
            statusBar.addPermanentWidget(w, 1)

            w = QToolButton(self)
            w.setObjectName('highlightBtn')
            w.setToolTip("Highlight extent in main map")
            w.setText("Highlight")
            statusBar.addPermanentWidget(w, 1)

            w = QLabel("Scale factor:", self)
            w.setObjectName('scaleFactorLabel')
            w.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
            statusBar.addPermanentWidget(w, 1)

            w = QDoubleSpinBox(self)
            w.setObjectName('scaleFactorSpin')
            w.setToolTip("Current scale factor of main map")
            w.setMinimum(0.0)
            w.setMaximum(1000.0)
            w.setDecimals(3)
            w.setValue(1)
            w.setSingleStep(.05)
            statusBar.addPermanentWidget(w, 1)

            w = QToolButton(self)
            w.setObjectName('scaleBtn')
            w.setToolTip("Set scale for main map")
            w.setText("Scale: ")
            statusBar.addPermanentWidget(w, 1)

        def setupUi():
            self.setObjectName("AuxiliaryWindow")
            self.setGeometry(geometryWin)
            self.addDockWidget(Qt.LeftDockWidgetArea, self.dockLegend)
            self.actLegend = self.menuBar().addAction("")
            self.actLegend.triggered.connect(self.onActionLegend)
            self.canvas.setMapTool(self.toolPan)
            self.canvas.setCanvasColor(QColor(255, 255, 255))
            self.canvas.enableAntiAliasing(False)
            self.canvas.useImageToRender(False)
            self.canvas.setWheelAction(QgsMapCanvas.WheelZoom)
            self.setCentralWidget(centralWidget)
            self.messageBar.setSizePolicy(QSizePolicy.Minimum,
                                          QSizePolicy.Fixed)
            layout = QGridLayout()
            layout.setContentsMargins(0, 0, 0, 0)
            layout.addWidget(self.canvas, 0, 0, 2, 1)
            layout.addWidget(self.messageBar, 0, 0, 1, 1)
            centralWidget.setLayout(layout)

        super(AuxiliaryWindow, self).__init__(parent)

        centralWidget = QWidget(self)
        self.canvas = QgsMapCanvas(centralWidget)
        self.messageBar = QgsMessageBar(centralWidget)
        self.toolPan = QgsMapToolPan(self.canvas)
        self.qgisCanvas = qgis.utils.iface.mapCanvas()
        self.qgisTView = qgis.utils.iface.layerTreeView()
        self.qgisSyncGroup = None
        self.numWin = numWin

        self.ltg = QgsLayerTreeGroup('', Qt.Unchecked)
        self.dockLegend = AuxiliaryLegend(self, numWin)
        self.root = QgsProject.instance().layerTreeRoot()

        self.extent = self.actLegend = None
        self.marker = MarkerWindow(self.canvas)

        setupUi()
        populateStatusBar()

        self.onCurrentLayerChanged(None)
        self.onDestinationCrsChanged_MapUnitsChanged()
        self.onHasCrsTransformEnabledChanged(
            self.qgisCanvas.hasCrsTransformEnabled())

    def _connect(self, isConnect=True):
        widgets = {
            'scaleBtn': self.findChild(QToolButton, 'scaleBtn'),
            'renderCheck': self.findChild(QCheckBox, 'renderCheck'),
            'markerCheck': self.findChild(QCheckBox, 'markerCheck'),
            'extentCheck': self.findChild(QCheckBox, 'extentCheck'),
            'highlightBtn': self.findChild(QToolButton, 'highlightBtn'),
            'scaleFactorSpin': self.findChild(QDoubleSpinBox,
                                              'scaleFactorSpin')
        }
        signal_slot = ({
            'signal': widgets['scaleBtn'].clicked,
            'slot': self.onClickedScale
        }, {
            'signal': widgets['renderCheck'].toggled,
            'slot': self.onToggledRender
        }, {
            'signal': widgets['markerCheck'].toggled,
            'slot': self.onToggledMarker
        }, {
            'signal': widgets['extentCheck'].toggled,
            'slot': self.onToggledExtent
        }, {
            'signal': widgets['highlightBtn'].clicked,
            'slot': self.onClickedHighlight
        }, {
            'signal': widgets['scaleFactorSpin'].valueChanged,
            'slot': self.onValueChangedScale
        }, {
            'signal': self.dockLegend.currentLayerChanged,
            'slot': self.onCurrentLayerChanged
        }, {
            'signal': self.dockLegend.currentLayerQgis,
            'slot': self.onCurrentLayerQgis
        }, {
            'signal': self.dockLegend.syncGroupLayer,
            'slot': self.onSyncGroupAddLayersQgis
        }, {
            'signal': self.dockLegend.addSelectedLayersQgis,
            'slot': self.onAddSelectedLayersQgis
        }, {
            'signal': self.dockLegend.removeLayer,
            'slot': self.onRemoveLayers
        }, {
            'signal': self.dockLegend.needSelectLayer,
            'slot': self.onNeedSelectLayer
        }, {
            'signal': self.dockLegend.closed,
            'slot': self.onClosedLegend
        }, {
            'signal': self.canvas.extentsChanged,
            'slot': self.onExtentsChangedMirror
        }, {
            'signal': self.qgisCanvas.extentsChanged,
            'slot': self.onExtentsChangedQgisCanvas
        }, {
            'signal': self.qgisCanvas.xyCoordinates,
            'slot': self.marker.onXYCoordinates
        }, {
            'signal': self.qgisCanvas.destinationCrsChanged,
            'slot': self.onDestinationCrsChanged_MapUnitsChanged
        }, {
            'signal': self.qgisCanvas.mapUnitsChanged,
            'slot': self.onDestinationCrsChanged_MapUnitsChanged
        }, {
            'signal': self.qgisCanvas.hasCrsTransformEnabledChanged,
            'slot': self.onHasCrsTransformEnabledChanged
        }, {
            'signal': self.root.removedChildren,
            'slot': self.onRemovedChildrenQgisRoot
        }, {
            'signal': QgsMapLayerRegistry.instance().layersWillBeRemoved,
            'slot': self.onLayersWillBeRemoved
        })
        if isConnect:
            for item in signal_slot:
                item['signal'].connect(item['slot'])
        else:
            for item in signal_slot:
                item['signal'].disconnect(item['slot'])

    def _extentsChanged(self,
                        canvasOrigin,
                        originSlot,
                        canvasDest,
                        scaleFactor=None):
        canvasOrigin.extentsChanged.disconnect(originSlot)

        if scaleFactor is None:
            scale = canvasOrigin.scale()
            canvasOrigin.setExtent(canvasDest.extent())
            canvasOrigin.zoomScale(scale)
        else:
            canvasOrigin.setExtent(canvasDest.extent())
            canvasOrigin.zoomScale(scaleFactor * canvasDest.scale())

        canvasOrigin.extentsChanged.connect(originSlot)

    def _textScaleBtnChanched(self):
        scale = locale.format("%.0f", self.canvas.scale(), True)
        w = self.findChild(QToolButton, 'scaleBtn')
        w.setText("Scale 1:%s" % scale)

    def _extent(self):
        rect = self.qgisCanvas.extent()
        p1 = QgsPoint(rect.xMinimum(), rect.yMinimum())
        p2 = QgsPoint(rect.xMinimum(), rect.yMaximum())
        p3 = QgsPoint(rect.xMaximum(), rect.yMaximum())
        p4 = QgsPoint(rect.xMaximum(), rect.yMinimum())
        p5 = QgsPoint(rect.xMinimum(), rect.yMinimum())
        points = [p1, p2, p3, p4, p5]
        self.extent.setToGeometry(QgsGeometry.fromPolyline(points), None)

    def _execFunction(self, func, arg, signal, slot):
        signal.disconnect(slot)
        func(arg)
        signal.connect(slot)

    def _connectVectorRefresh(self, layer, isConnect=True):
        if isinstance(layer, QgsVectorLayer):
            f = layer.editCommandEnded.connect if isConnect else layer.editCommandEnded.disconnect
            f(self.canvas.refresh)

    def _addLayersQgis(self, layersQgis, needMsg=True):

        self.dockLegend.clearBridge()

        l1 = set(layersQgis)
        l2 = set(map(lambda item: item.layer(), self.ltg.findLayers()))
        layers = list(l1 - l2)
        if len(layers) == 0:
            if needMsg:
                self.messageBar.pushMessage(
                    "Need select new layer(s) in main map",
                    QgsMessageBar.WARNING, 2)
        else:
            # Get order by layersQgis
            for item in layersQgis:
                if item in layers:
                    self.ltg.addLayer(item)
                    self._connectVectorRefresh(item)

        self.dockLegend.setBridge(self.canvas)

    def _syncGroupAddLayersQgis(self, ltg):
        layersQgis = map(lambda item: item.layer(), ltg.findLayers())
        if len(layersQgis) == 0:
            return False

        name = ltg.name()
        if self.qgisSyncGroup == ltg:
            msg = "Already synchronized group (main map) -> '%s'" % name
            self.messageBar.pushMessage(msg, QgsMessageBar.INFO, 4)
            return True

        if not self.qgisSyncGroup is None:
            self.qgisSyncGroup.addedChildren.disconnect(
                self.addedChildrenLayer)

        self.qgisSyncGroup = ltg
        self.qgisSyncGroup.addedChildren.connect(self.addedChildrenLayer)

        self.dockLegend.addNameSyncGroup(name)
        msg = "Changed synchronized group (main map) -> '%s'" % name
        self.messageBar.pushMessage(msg, QgsMessageBar.INFO, 4)

        self._addLayersQgis(layersQgis)
        return True

    def run(self):

        if len(self.qgisTView.selectedLayerNodes()) > 0:
            self.onAddSelectedLayersQgis()
        else:
            ltn = self.qgisTView.currentNode()
            if not isinstance(ltn, QgsLayerTreeGroup):
                return False
            else:
                if ltn == self.root:
                    return False
                else:
                    if not self._syncGroupAddLayersQgis(ltn):
                        return False

        self.dockLegend.setBridge(self.canvas)

        self.canvas.setRenderFlag(False)
        self.show()  # Need show before self._connect()
        self._connect()
        self.canvas.setExtent(self.qgisCanvas.extent())
        w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
        w.setValue(1)
        self.canvas.setRenderFlag(True)

        return True

    def getLayersCanvas(self):
        layerIds = map(lambda x: x.layerId(), self.ltg.findLayers())
        layerChecks = map(lambda x: str(x.isVisible()), self.ltg.findLayers())

        return (layerIds, layerChecks)

    def setLayersCanvas(self, layerIds, layerChecks):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        lyrRegs = QgsMapLayerRegistry.instance()
        for id in range(len(layerIds)):
            layer = lyrRegs.mapLayer(layerIds[id])
            isVisible = int(layerChecks[id])
            if not layer is None:
                self.ltg.addLayer(layer).setVisible(isVisible)

        self.canvas.setRenderFlag(prevFlag)

    def getWindowSetting(self):
        g = self.geometry()
        r = self.canvas.extent()
        nodes = self.ltg.findLayers()
        currentLayer = self.dockLegend.tview.currentLayer()
        currentLayerId = currentLayer.id(
        ) if not currentLayer is None else "None"

        windowSetting = {
            'numWin':
            self.numWin,
            'geometryWin': {
                'x': g.x(),
                'y': g.y(),
                'width': g.width(),
                'height': g.height()
            },
            'extentCanvas': {
                'xmin': r.xMinimum(),
                'ymin': r.yMinimum(),
                'xmax': r.xMaximum(),
                'ymax': r.yMaximum()
            },
            'currentLayerId':
            currentLayerId,
            'layerIds':
            ' '.join(map(lambda item: item.layerId(), nodes)),
            'visibles':
            ' '.join(map(lambda item: str(int(item.isVisible())), nodes))
        }
        for item in ('render', 'marker', 'extent'):
            nameGui = "%sCheck" % item
            windowSetting[item] = int(
                self.findChild(QCheckBox, nameGui).isChecked())

        return windowSetting

    def setWindowSetting(self, windowSetting):
        self.numWin = windowSetting['numWin']

        # Populate with layers and set Bridge for legend
        layerIds = windowSetting['layerIds'].split(' ')
        visibles = map(lambda item: bool(int(item)),
                       windowSetting['visibles'].split(' '))
        ltg = self.qgisTView.layerTreeModel().rootGroup()
        for id in range(len(layerIds)):
            node = ltg.findLayer(layerIds[id])
            if node is None:
                continue
            layer = node.layer()
            visible = Qt.Checked if visibles[id] else Qt.Unchecked
            self._connectVectorRefresh(layer)
            self.ltg.addLayer(layer).setVisible(visible)
        self.dockLegend.setBridge(self.canvas)

        self.show()  # Need show before self._connect()
        self._connect()
        node = ltg.findLayer(windowSetting['currentLayerId'])
        if not node is None:
            layer = node.layer()
            self.dockLegend.tview.setCurrentLayer(layer)
        w = windowSetting['extentCanvas']
        self.canvas.setExtent(
            QgsRectangle(w['xmin'], w['ymin'], w['xmax'], w['ymax']))
        for item in ('render', 'marker', 'extent'):
            value = bool(windowSetting[item])
            nameGui = "%sCheck" % item
            self.findChild(QCheckBox, nameGui).setChecked(value)

    def closeEvent(self, event):
        self._connect(False)
        event.accept()
        self.closed.emit(self.numWin)

    @pyqtSlot(int)
    def onValueChangedScale(self, scaleFactor):
        w = self.findChild(QCheckBox, 'renderCheck')
        if not w.isChecked():
            return
        self._execFunction(self.canvas.zoomScale,
                           scaleFactor * self.qgisCanvas.scale(),
                           self.canvas.extentsChanged,
                           self.onExtentsChangedMirror)
        self._textScaleBtnChanched()

    @pyqtSlot()
    def onClickedScale(self):
        self._execFunction(self.qgisCanvas.zoomScale, self.canvas.scale(),
                           self.qgisCanvas.extentsChanged,
                           self.onExtentsChangedQgisCanvas)
        w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
        self._execFunction(w.setValue, 1.0, w.valueChanged,
                           self.onValueChangedScale)

    @pyqtSlot()
    def onClickedHighlight(self):
        def removeRB():
            rb.reset(True)
            self.qgisCanvas.scene().removeItem(rb)

        rb = QgsRubberBand(self.qgisCanvas, QGis.Polygon)
        rb.setBorderColor(QColor(255, 0, 0))
        rb.setWidth(2)
        rb.setToGeometry(QgsGeometry.fromRect(self.canvas.extent()), None)
        QTimer.singleShot(2000, removeRB)

    @pyqtSlot(bool)
    def onToggledRender(self, enabled):
        if enabled:
            self.canvas.setMapTool(self.toolPan)
            w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
            self._extentsChanged(self.canvas, self.onExtentsChangedMirror,
                                 self.qgisCanvas, w.value())
            self._textScaleBtnChanched()
            self.canvas.setWheelAction(QgsMapCanvas.WheelZoom)
        else:
            self.canvas.unsetMapTool(self.toolPan)
            self.canvas.setWheelAction(QgsMapCanvas.WheelNothing)
        self.canvas.setRenderFlag(enabled)

    @pyqtSlot(bool)
    def onToggledMarker(self, enabled):
        self.marker.add() if enabled else self.marker.remove()

    @pyqtSlot(bool)
    def onToggledExtent(self, enabled):
        def setExtent():
            if not self.extent is None:
                self.canvas.scene().removeItem(self.extent)
            self.extent = QgsRubberBand(self.canvas, QGis.Polygon)
            self.extent.setBorderColor(QColor(255, 0, 0))
            self.extent.setWidth(2)
            self._extent()

        if enabled:
            setExtent()
        else:
            if not self.extent is None:
                self.canvas.scene().removeItem(self.extent)
                self.extent = None

    @pyqtSlot()
    def onExtentsChangedMirror(self):
        w = self.findChild(QCheckBox, 'renderCheck')
        if not w.isChecked():
            return
        self._extentsChanged(self.qgisCanvas, self.onExtentsChangedQgisCanvas,
                             self.canvas)
        self._textScaleBtnChanched()
        w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
        self._execFunction(w.setValue,
                           self.canvas.scale() / self.qgisCanvas.scale(),
                           w.valueChanged, self.onValueChangedScale)
        if not self.extent is None:
            self._extent()

    @pyqtSlot()
    def onExtentsChangedQgisCanvas(self):
        w = self.findChild(QCheckBox, 'renderCheck')
        if not w.isChecked():
            return
        w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
        self._extentsChanged(self.canvas, self.onExtentsChangedMirror,
                             self.qgisCanvas, w.value())
        self._textScaleBtnChanched()
        if not self.extent is None:
            self._extent()

    @pyqtSlot()
    def onDestinationCrsChanged_MapUnitsChanged(self):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        self.canvas.setDestinationCrs(
            self.qgisCanvas.mapRenderer().destinationCrs())
        self.canvas.setMapUnits(self.qgisCanvas.mapUnits())

        self.canvas.setRenderFlag(prevFlag)

    @pyqtSlot(bool)
    def onHasCrsTransformEnabledChanged(self, enabled):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)
        self.canvas.mapRenderer().setProjectionsEnabled(enabled)
        self.canvas.setRenderFlag(prevFlag)

    @pyqtSlot(list)
    def onLayersWillBeRemoved(self, theLayerIds):
        ids = list(set(self.ltg.findLayerIds())
                   & set(theLayerIds))  # intersection
        nodes = map(lambda item: self.ltg.findLayer(item), ids)
        for item in nodes:
            self._connectVectorRefresh(item.layer(), False)
            self.ltg.removeChildNode(item)

    @pyqtSlot()
    def onAddSelectedLayersQgis(self):
        layersQgis = map(lambda item: item.layer(),
                         self.qgisTView.selectedLayerNodes())
        self._addLayersQgis(layersQgis)

    @pyqtSlot('QgsLayerTreeNode', int, int)
    def addedChildrenLayer(self, ltg, indexFrom, indexTo):
        layersQgis = map(lambda item: item.layer(), ltg.findLayers())
        self._addLayersQgis(layersQgis, False)

    @pyqtSlot('QgsLayerTreeNode', int, int)
    def onRemovedChildrenQgisRoot(self, ltg, indexFrom, indexTo):
        if not self.qgisSyncGroup is None and not self.qgisSyncGroup in self.root.children(
        ):
            self.qgisSyncGroup = None
            self.dockLegend.addNameSyncGroup("None")
            msg = "Removed synchronized group (main map)"
            self.messageBar.pushMessage(msg, QgsMessageBar.INFO, 4)

    @pyqtSlot()
    def onSyncGroupAddLayersQgis(self):

        msg = "Need active a group in main map with new layers"
        ltn = self.qgisTView.currentNode()
        if not isinstance(ltn, QgsLayerTreeGroup) or ltn == self.root:
            self.messageBar.pushMessage(msg, QgsMessageBar.WARNING, 3)
            return

        if not self._syncGroupAddLayersQgis(ltn):
            self.messageBar.pushMessage(msg, QgsMessageBar.WARNING, 3)

    @pyqtSlot('QgsMapLayer')
    def onRemoveLayers(self, layer):
        self._connectVectorRefresh(layer, False)

    @pyqtSlot()
    def onNeedSelectLayer(self):
        self.messageBar.pushMessage("Need select layer(s)",
                                    QgsMessageBar.WARNING, 2)

    @pyqtSlot('QgsMapLayer')
    def onCurrentLayerQgis(self, layer):
        if layer is None:
            self.messageBar.pushMessage("Need active layer",
                                        QgsMessageBar.WARNING, 2)
        else:
            self.qgisTView.setCurrentLayer(layer)

    @pyqtSlot('QgsMapLayer')
    def onCurrentLayerChanged(self, layer):
        hasLayer = True if not layer is None else False
        selectName = "Select layer '%s'" % layer.name(
        ) if hasLayer else "None select layer"
        title = "#%d - %s" % (self.numWin, selectName)
        self.setWindowTitle(title)

    @pyqtSlot()
    def onClosedLegend(self):
        self.actLegend.setText("Show layers")

    @pyqtSlot()
    def onActionLegend(self):
        self.actLegend.setText("")
        self.dockLegend.show()
class AuxiliaryWindow(QMainWindow):
  
  closed = pyqtSignal( int )
  
  def __init__(self, parent, geometryWin, numWin):
    
    def populateStatusBar():
      statusBar = self.statusBar()

      w = QCheckBox( "Render", self )
      w.setObjectName( 'renderCheck')
      w.setToolTip( "Toggle map rendering" )
      w.setChecked( True )
      statusBar.addPermanentWidget( w )

      w = QCheckBox( "Marker", self )
      w.setObjectName( 'markerCheck')
      w.setToolTip( "Toggle marker with cursor position from main map" )
      w.setChecked( False )
      statusBar.addPermanentWidget( w, 1 )

      w = QCheckBox( "Extent", self )
      w.setObjectName( 'extentCheck')
      w.setToolTip( "Show extent of main map" )
      w.setChecked( False )
      statusBar.addPermanentWidget( w, 1 )

      w = QToolButton(self)
      w.setObjectName( 'highlightBtn')
      w.setToolTip( "Highlight extent in main map" )
      w.setText("Highlight")
      statusBar.addPermanentWidget( w, 1 )

      w = QLabel( "Scale factor:", self )
      w.setObjectName( 'scaleFactorLabel')
      w.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
      statusBar.addPermanentWidget( w, 1 )

      w = QDoubleSpinBox(self)
      w.setObjectName( 'scaleFactorSpin')
      w.setToolTip( "Current scale factor of main map" )
      w.setMinimum(0.0)
      w.setMaximum(1000.0)
      w.setDecimals(3)
      w.setValue(1)
      w.setSingleStep(.05)
      statusBar.addPermanentWidget( w, 1 )

      w = QToolButton(self)
      w.setObjectName( 'scaleBtn')
      w.setToolTip( "Set scale for main map" )
      w.setText("Scale: ")
      statusBar.addPermanentWidget( w, 1 )

    def setupUi():
      self.setObjectName( "AuxiliaryWindow" )
      self.setGeometry( geometryWin )
      self.addDockWidget ( Qt.LeftDockWidgetArea, self.dockLegend )
      self.actLegend = self.menuBar().addAction("")
      self.actLegend.triggered.connect( self.onActionLegend )
      self.canvas.setMapTool( self.toolPan )
      self.canvas.setCanvasColor( QColor(255,255,255) )
      self.canvas.enableAntiAliasing( False )
      self.canvas.useImageToRender( False )
      self.canvas.setWheelAction( QgsMapCanvas.WheelZoom )
      self.setCentralWidget( centralWidget )
      self.messageBar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed )
      layout = QGridLayout()
      layout.setContentsMargins( 0, 0, 0, 0 )
      layout.addWidget( self.canvas, 0, 0, 2, 1 )
      layout.addWidget( self.messageBar, 0, 0, 1, 1 )
      centralWidget.setLayout( layout )

    super( AuxiliaryWindow, self ).__init__( parent )

    centralWidget = QWidget( self )
    self.canvas = QgsMapCanvas( centralWidget )
    self.messageBar = QgsMessageBar( centralWidget )
    self.toolPan = QgsMapToolPan( self.canvas )
    self.qgisCanvas = qgis.utils.iface.mapCanvas()
    self.qgisTView = qgis.utils.iface.layerTreeView()
    self.qgisSyncGroup = None
    self.numWin = numWin

    self.ltg = QgsLayerTreeGroup('', Qt.Unchecked)
    self.dockLegend = AuxiliaryLegend( self, numWin )
    self.root = QgsProject.instance().layerTreeRoot()
    

    self.extent = self.actLegend = None
    self.marker = MarkerWindow( self.canvas )

    setupUi()
    populateStatusBar()

    self.onCurrentLayerChanged( None )
    self.onDestinationCrsChanged_MapUnitsChanged()
    self.onHasCrsTransformEnabledChanged( self.qgisCanvas.hasCrsTransformEnabled() )
    
  def _connect(self, isConnect = True):
    widgets = {
     'scaleBtn': self.findChild( QToolButton, 'scaleBtn'),
     'renderCheck': self.findChild( QCheckBox, 'renderCheck'),
     'markerCheck': self.findChild( QCheckBox, 'markerCheck'),
     'extentCheck': self.findChild( QCheckBox, 'extentCheck'),
     'highlightBtn': self.findChild( QToolButton, 'highlightBtn'),
     'scaleFactorSpin': self.findChild( QDoubleSpinBox, 'scaleFactorSpin')
    }
    signal_slot = (
      { 'signal': widgets['scaleBtn'].clicked, 'slot': self.onClickedScale },
      { 'signal': widgets['renderCheck'].toggled, 'slot': self.onToggledRender },
      { 'signal': widgets['markerCheck'].toggled, 'slot': self.onToggledMarker },
      { 'signal': widgets['extentCheck'].toggled, 'slot': self.onToggledExtent },
      { 'signal': widgets['highlightBtn'].clicked, 'slot': self.onClickedHighlight },
      { 'signal': widgets['scaleFactorSpin'].valueChanged, 'slot': self.onValueChangedScale },
      { 'signal': self.dockLegend.currentLayerChanged, 'slot': self.onCurrentLayerChanged },
      { 'signal': self.dockLegend.currentLayerQgis, 'slot': self.onCurrentLayerQgis },
      { 'signal': self.dockLegend.syncGroupLayer, 'slot': self.onSyncGroupAddLayersQgis },
      { 'signal': self.dockLegend.addSelectedLayersQgis, 'slot': self.onAddSelectedLayersQgis },
      { 'signal': self.dockLegend.removeLayer, 'slot': self.onRemoveLayers },
      { 'signal': self.dockLegend.needSelectLayer, 'slot': self.onNeedSelectLayer },
      { 'signal': self.dockLegend.closed, 'slot': self.onClosedLegend },
      { 'signal': self.canvas.extentsChanged, 'slot': self.onExtentsChangedMirror },
      { 'signal': self.qgisCanvas.extentsChanged, 'slot': self.onExtentsChangedQgisCanvas },
      { 'signal': self.qgisCanvas.xyCoordinates, 'slot': self.marker.onXYCoordinates },
      { 'signal': self.qgisCanvas.destinationCrsChanged, 'slot': self.onDestinationCrsChanged_MapUnitsChanged },
      { 'signal': self.qgisCanvas.mapUnitsChanged, 'slot': self.onDestinationCrsChanged_MapUnitsChanged },
      { 'signal': self.qgisCanvas.hasCrsTransformEnabledChanged, 'slot': self.onHasCrsTransformEnabledChanged },
      { 'signal': self.root.removedChildren, 'slot': self.onRemovedChildrenQgisRoot },
      { 'signal': QgsMapLayerRegistry.instance().layersWillBeRemoved, 'slot': self.onLayersWillBeRemoved }
    )
    if isConnect:
      for item in signal_slot:
        item['signal'].connect( item['slot'] )
    else:
      for item in signal_slot:
        item['signal'].disconnect( item['slot'] )

  def _extentsChanged(self, canvasOrigin, originSlot, canvasDest, scaleFactor=None):
    canvasOrigin.extentsChanged.disconnect( originSlot )

    if scaleFactor is None:
      scale = canvasOrigin.scale()
      canvasOrigin.setExtent( canvasDest.extent() )
      canvasOrigin.zoomScale( scale )
    else:
      canvasOrigin.setExtent( canvasDest.extent() )
      canvasOrigin.zoomScale( scaleFactor * canvasDest.scale() )

    canvasOrigin.extentsChanged.connect( originSlot )

  def _textScaleBtnChanched(self):
    scale = locale.format( "%.0f", self.canvas.scale(), True ) 
    w = self.findChild( QToolButton, 'scaleBtn' )
    w.setText("Scale 1:%s" % scale )

  def _extent(self):
   rect = self.qgisCanvas.extent()
   p1 = QgsPoint( rect.xMinimum() , rect.yMinimum() )
   p2 = QgsPoint( rect.xMinimum() , rect.yMaximum() )
   p3 = QgsPoint( rect.xMaximum() , rect.yMaximum() )
   p4 = QgsPoint( rect.xMaximum() , rect.yMinimum() )
   p5 = QgsPoint( rect.xMinimum() , rect.yMinimum() )
   points = [ p1, p2, p3, p4, p5 ]
   self.extent.setToGeometry(QgsGeometry.fromPolyline (points), None)

  def _execFunction( self, func, arg, signal, slot):
   signal.disconnect( slot )
   func( arg )
   signal.connect( slot )

  def _connectVectorRefresh(self, layer, isConnect=True):
    if isinstance( layer, QgsVectorLayer ):
      f = layer.editCommandEnded.connect if isConnect else layer.editCommandEnded.disconnect
      f( self.canvas.refresh )

  def _addLayersQgis( self, layersQgis, needMsg=True ):
    
    self.dockLegend.clearBridge()

    l1 = set( layersQgis )
    l2 = set( map( lambda item: item.layer(), self.ltg.findLayers() ) )
    layers = list( l1 - l2 )
    if len( layers ) == 0:
      if needMsg:
        self.messageBar.pushMessage("Need select new layer(s) in main map", QgsMessageBar.WARNING, 2 )
    else:
      # Get order by layersQgis
      for item in layersQgis:
        if item in layers:
          self.ltg.addLayer( item )
          self._connectVectorRefresh( item )

    self.dockLegend.setBridge( self.canvas )

  def _syncGroupAddLayersQgis( self, ltg ):
    layersQgis = map( lambda item: item.layer(), ltg.findLayers() )
    if len( layersQgis ) == 0:
      return False

    name = ltg.name()
    if self.qgisSyncGroup == ltg:
      msg = "Already synchronized group (main map) -> '%s'" % name
      self.messageBar.pushMessage( msg, QgsMessageBar.INFO, 4 )
      return True
    
    if not self.qgisSyncGroup is None:
      self.qgisSyncGroup.addedChildren.disconnect( self.addedChildrenLayer )
      
    self.qgisSyncGroup = ltg
    self.qgisSyncGroup.addedChildren.connect( self.addedChildrenLayer )
    
    self.dockLegend.addNameSyncGroup( name )
    msg = "Changed synchronized group (main map) -> '%s'" % name
    self.messageBar.pushMessage( msg, QgsMessageBar.INFO, 4 )
    
    self._addLayersQgis( layersQgis )
    return True

  def run(self):
    
    if len( self.qgisTView.selectedLayerNodes() ) > 0:
      self.onAddSelectedLayersQgis()
    else:
      ltn = self.qgisTView.currentNode()
      if not isinstance( ltn, QgsLayerTreeGroup ):
        return False
      else:
        if ltn == self.root:
          return False
        else:
          if not self._syncGroupAddLayersQgis( ltn ):
            return False

    self.dockLegend.setBridge( self.canvas)

    self.canvas.setRenderFlag( False )
    self.show() # Need show before self._connect()
    self._connect()
    self.canvas.setExtent( self.qgisCanvas.extent() )
    w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin')
    w.setValue( 1 )
    self.canvas.setRenderFlag( True )

    return True

  def getLayersCanvas(self):
    layerIds = map(lambda x: x.layerId(), self.ltg.findLayers() )
    layerChecks = map(lambda x: str( x.isVisible() ), self.ltg.findLayers() )

    return ( layerIds, layerChecks )

  def setLayersCanvas(self, layerIds, layerChecks ):
    prevFlag = self.canvas.renderFlag()
    self.canvas.setRenderFlag( False )

    lyrRegs = QgsMapLayerRegistry.instance()
    for id in range( len( layerIds ) ):
      layer = lyrRegs.mapLayer(  layerIds[id] )
      isVisible = int( layerChecks[id] )
      if not layer is None:
        self.ltg.addLayer( layer ).setVisible( isVisible )

    self.canvas.setRenderFlag( prevFlag )

  def getWindowSetting(self):
    g = self.geometry()
    r = self.canvas.extent()
    nodes = self.ltg.findLayers()
    currentLayer = self.dockLegend.tview.currentLayer()
    currentLayerId = currentLayer.id() if not currentLayer is None else "None"
    
    windowSetting =  {
      'numWin': self.numWin,
      'geometryWin': { 'x': g.x(), 'y': g.y(), 'width': g.width(), 'height': g.height() },
      'extentCanvas': { 'xmin': r.xMinimum(), 'ymin': r.yMinimum(), 'xmax': r.xMaximum(), 'ymax': r.yMaximum() },
      'currentLayerId': currentLayerId,
      'layerIds' : ' '.join( map(lambda item: item.layerId(), nodes ) ),
      'visibles': ' '.join( map(lambda item: str( int( item.isVisible() ) ), nodes ) )
    }
    for item in ( 'render', 'marker', 'extent' ):
      nameGui = "%sCheck" % item
      windowSetting[ item ] = int( self.findChild( QCheckBox, nameGui).isChecked() )

    return windowSetting

  def setWindowSetting(self, windowSetting):
    self.numWin = windowSetting['numWin']

    # Populate with layers and set Bridge for legend
    layerIds = windowSetting['layerIds'].split(' ')
    visibles = map( lambda item: bool( int( item ) ), windowSetting['visibles'].split(' ') )
    ltg = self.qgisTView.layerTreeModel().rootGroup()
    for id in range( len( layerIds ) ):
      node = ltg.findLayer( layerIds[ id ] )
      if node is None:
        continue
      layer = node.layer()
      visible = Qt.Checked if visibles[ id ] else Qt.Unchecked
      self._connectVectorRefresh( layer )
      self.ltg.addLayer( layer ).setVisible( visible )
    self.dockLegend.setBridge( self.canvas)
    
    self.show() # Need show before self._connect()
    self._connect()
    node = ltg.findLayer( windowSetting['currentLayerId'] )
    if not node is None:
      layer = node.layer()
      self.dockLegend.tview.setCurrentLayer( layer )
    w = windowSetting['extentCanvas']
    self.canvas.setExtent( QgsRectangle( w['xmin'], w['ymin'], w['xmax'], w['ymax'] ) )
    for item in ( 'render', 'marker', 'extent' ):
      value = bool( windowSetting[ item ] )
      nameGui = "%sCheck" % item
      self.findChild( QCheckBox, nameGui ).setChecked( value )

  def closeEvent(self, event):
    self._connect( False )
    event.accept()
    self.closed.emit( self.numWin )

  @pyqtSlot(int)
  def onValueChangedScale(self, scaleFactor):
    w = self.findChild( QCheckBox, 'renderCheck')
    if not w.isChecked():
      return
    self._execFunction(
      self.canvas.zoomScale, scaleFactor * self.qgisCanvas.scale(),
      self.canvas.extentsChanged, self.onExtentsChangedMirror
    )
    self._textScaleBtnChanched()

  @pyqtSlot()
  def onClickedScale(self):
    self._execFunction( 
        self.qgisCanvas.zoomScale, self.canvas.scale(),
        self.qgisCanvas.extentsChanged, self.onExtentsChangedQgisCanvas
    )
    w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin' )
    self._execFunction( w.setValue, 1.0, w.valueChanged, self.onValueChangedScale )

  @pyqtSlot()
  def onClickedHighlight(self):
    def removeRB():
      rb.reset( True )
      self.qgisCanvas.scene().removeItem( rb )
    
    rb = QgsRubberBand( self.qgisCanvas, QGis.Polygon)
    rb.setBorderColor( QColor( 255,  0, 0 ) )
    rb.setWidth( 2 )
    rb.setToGeometry( QgsGeometry.fromRect( self.canvas.extent() ), None )
    QTimer.singleShot( 2000, removeRB )

  @pyqtSlot(bool)
  def onToggledRender(self, enabled):
    if enabled:
      self.canvas.setMapTool(self.toolPan)
      w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin' )
      self._extentsChanged( self.canvas, self.onExtentsChangedMirror, self.qgisCanvas, w.value() )
      self._textScaleBtnChanched()
      self.canvas.setWheelAction( QgsMapCanvas.WheelZoom )
    else:
      self.canvas.unsetMapTool(self.toolPan)
      self.canvas.setWheelAction( QgsMapCanvas.WheelNothing )
    self.canvas.setRenderFlag( enabled )

  @pyqtSlot(bool)
  def onToggledMarker(self, enabled):
    self.marker.add() if enabled else self.marker.remove() 

  @pyqtSlot(bool)
  def onToggledExtent(self, enabled):
    def setExtent():
      if not self.extent is None:
        self.canvas.scene().removeItem( self.extent )
      self.extent = QgsRubberBand( self.canvas, QGis.Polygon )
      self.extent.setBorderColor( QColor( 255, 0 , 0 ) )
      self.extent.setWidth( 2 )
      self._extent()

    if enabled:
      setExtent()
    else:
      if not self.extent is None:
        self.canvas.scene().removeItem( self.extent )
        self.extent = None

  @pyqtSlot()
  def onExtentsChangedMirror(self):
    w = self.findChild( QCheckBox, 'renderCheck')
    if not w.isChecked():
      return
    self._extentsChanged( self.qgisCanvas, self.onExtentsChangedQgisCanvas, self.canvas )
    self._textScaleBtnChanched()
    w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin' )
    self._execFunction(
        w.setValue, self.canvas.scale() / self.qgisCanvas.scale(),
        w.valueChanged, self.onValueChangedScale
    )
    if not self.extent is None:
      self._extent()

  @pyqtSlot()
  def onExtentsChangedQgisCanvas(self):
    w = self.findChild( QCheckBox, 'renderCheck')
    if not w.isChecked():
      return
    w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin' )
    self._extentsChanged( self.canvas, self.onExtentsChangedMirror, self.qgisCanvas, w.value() )
    self._textScaleBtnChanched()
    if not self.extent is None:
      self._extent()

  @pyqtSlot()
  def onDestinationCrsChanged_MapUnitsChanged(self):
    prevFlag = self.canvas.renderFlag()
    self.canvas.setRenderFlag( False )

    self.canvas.setDestinationCrs( self.qgisCanvas.mapRenderer().destinationCrs()  )
    self.canvas.setMapUnits( self.qgisCanvas.mapUnits() )

    self.canvas.setRenderFlag( prevFlag )

  @pyqtSlot(bool)
  def onHasCrsTransformEnabledChanged(self, enabled):
    prevFlag = self.canvas.renderFlag()
    self.canvas.setRenderFlag( False )
    self.canvas.mapRenderer().setProjectionsEnabled( enabled )
    self.canvas.setRenderFlag( prevFlag )

  @pyqtSlot(list)
  def onLayersWillBeRemoved( self, theLayerIds ):
    ids = list( set( self.ltg.findLayerIds() ) & set( theLayerIds ) ) # intersection
    nodes = map( lambda item: self.ltg.findLayer( item ), ids )
    for item in nodes:
      self._connectVectorRefresh( item.layer(), False )
      self.ltg.removeChildNode( item )

  @pyqtSlot()
  def onAddSelectedLayersQgis( self ):
    layersQgis = map( lambda item: item.layer(), self.qgisTView.selectedLayerNodes() )
    self._addLayersQgis( layersQgis )

  @pyqtSlot('QgsLayerTreeNode', int, int)
  def addedChildrenLayer(self, ltg, indexFrom, indexTo):
    layersQgis = map( lambda item: item.layer(), ltg.findLayers() )
    self._addLayersQgis( layersQgis, False )

  @pyqtSlot('QgsLayerTreeNode', int, int)
  def onRemovedChildrenQgisRoot(self, ltg, indexFrom, indexTo):
    if not self.qgisSyncGroup is None and not self.qgisSyncGroup in self.root.children():
      self.qgisSyncGroup = None
      self.dockLegend.addNameSyncGroup( "None" )
      msg = "Removed synchronized group (main map)"
      self.messageBar.pushMessage( msg, QgsMessageBar.INFO, 4 )
      
      
  @pyqtSlot()
  def onSyncGroupAddLayersQgis( self):
    
    msg = "Need active a group in main map with new layers"
    ltn = self.qgisTView.currentNode()
    if not isinstance( ltn, QgsLayerTreeGroup ) or ltn == self.root:
      self.messageBar.pushMessage( msg, QgsMessageBar.WARNING, 3 )
      return

    if not self._syncGroupAddLayersQgis( ltn ):
      self.messageBar.pushMessage( msg, QgsMessageBar.WARNING, 3 )

  @pyqtSlot( 'QgsMapLayer' )
  def onRemoveLayers( self, layer ):
    self._connectVectorRefresh(layer, False)

  @pyqtSlot()
  def onNeedSelectLayer(self):
    self.messageBar.pushMessage("Need select layer(s)", QgsMessageBar.WARNING, 2 )    

  @pyqtSlot('QgsMapLayer')
  def onCurrentLayerQgis(self, layer ):
    if layer is None:
      self.messageBar.pushMessage("Need active layer", QgsMessageBar.WARNING, 2 )
    else:
      self.qgisTView.setCurrentLayer( layer )

  @pyqtSlot('QgsMapLayer')
  def onCurrentLayerChanged(self, layer ):
    hasLayer = True if not layer is None else False
    selectName = "Select layer '%s'" % layer.name() if hasLayer else "None select layer"
    title = "#%d - %s" % ( self.numWin, selectName )
    self.setWindowTitle( title )

  @pyqtSlot()
  def onClosedLegend(self):
    self.actLegend.setText( "Show layers" )

  @pyqtSlot()
  def onActionLegend(self):
    self.actLegend.setText( "" )
    self.dockLegend.show()