Ejemplo n.º 1
0
    def changingObjects(self):
        changingObjects = QList()
        
        for movingObject in self.mMovingObjects:
            changingObjects.append(movingObject.item.mapObject())

        return changingObjects
Ejemplo n.º 2
0
    def raise_(self):
        if (not self.initContext()):
            return
        # Iterate backwards over the ranges in order to keep the indexes valid
        size = len(self.mSelectionRanges)
        if size <= 0:  # no range
            return
        firstRange = self.mSelectionRanges.begin()
        it = self.mSelectionRanges.end()
        if (it == firstRange):  # no range
            return
        # For each range of objects, only the first will move
        commands = QList()

        lastIndex = len(self.mRelatedObjects) - 1
        for i in range(size - 1, -1, -1):
            it = self.mSelectionRanges.item(i)
            value = it[1]
            # The last range may be already at the top of the related items
            if value == lastIndex:
                continue
            movingItem = self.mRelatedObjects.at(value)
            targetItem = self.mRelatedObjects.at(value + 1)
            _from = int(movingItem.zValue())
            to = int(targetItem.zValue() + 1)
            commands.append(
                ChangeMapObjectsOrder(self.mMapDocument, self.mObjectGroup,
                                      _from, to, 1))
        self.push(commands,
                  QCoreApplication.translate("Undo Commands", "Raise Object"))
Ejemplo n.º 3
0
    def addTiles(self):
        tileset = self.currentTileset()
        if (not tileset):
            return
        prefs = preferences.Preferences.instance()
        startLocation = QFileInfo(
            prefs.lastPath(preferences.Preferences.ImageFile)).absolutePath()
        filter = Utils.readableImageFormatsFilter()
        files = QFileDialog.getOpenFileNames(self.window(),
                                             self.tr("Add Tiles"),
                                             startLocation, filter)
        tiles = QList()
        id = tileset.tileCount()
        for file in files:
            image = QPixmap(file)
            if (not image.isNull()):
                tiles.append(Tile(image, file, id, tileset))
                id += 1
            else:
                warning = QMessageBox(QMessageBox.Warning,
                                      self.tr("Add Tiles"),
                                      self.tr("Could not load \"%s\"!" % file),
                                      QMessageBox.Ignore | QMessageBox.Cancel,
                                      self.window())
                warning.setDefaultButton(QMessageBox.Ignore)
                if (warning.exec() != QMessageBox.Ignore):
                    tiles.clear()
                    return

        if (tiles.isEmpty()):
            return
        prefs.setLastPath(preferences.Preferences.ImageFile, files.last())
        self.mMapDocument.undoStack().push(
            AddTiles(self.mMapDocument, tileset, tiles))
Ejemplo n.º 4
0
    def selectionChanged(self, selected, deselected):
        super().selectionChanged(selected, deselected)
        
        if (not self.mMapDocument or self.mSynching):
            return
        selectedRows = self.selectionModel().selectedRows()
        currentLayerIndex = -1
        selectedObjects = QList()
        for index in selectedRows:
            og = self.model().toLayer(index)
            if og:
                i = self.mMapDocument.map().layers().indexOf(og)
                if (currentLayerIndex == -1):
                    currentLayerIndex = i
                elif (currentLayerIndex != i):
                    currentLayerIndex = -2

            o = self.model().toMapObject(index)
            if o:
                selectedObjects.append(o)
        
        # Switch the current object layer if only one object layer (and/or its objects)
        # are included in the current selection.
        if (currentLayerIndex >= 0 and currentLayerIndex != self.mMapDocument.currentLayerIndex()):
            self.mMapDocument.setCurrentLayerIndex(currentLayerIndex)
        if (selectedObjects != self.mMapDocument.selectedObjects()):
            self.mSynching = True
            if (selectedObjects.count() == 1):
                o = selectedObjects.first()
                center = o.bounds().center()
                DocumentManager.instance().centerViewOn(center)

            self.mMapDocument.setSelectedObjects(selectedObjects)
            self.mSynching = False
Ejemplo n.º 5
0
 def objects(_type=None):
     results = QList()
     for object in PluginManager.instance().mObjects:
         if _type!=None and type(object)!=_type:
             continue
         results.append(object)
     return results
Ejemplo n.º 6
0
 def objectGroups(self):
     layers = QList()
     for layer in self.mLayers:
         og = layer.asObjectGroup()
         if og:
             layers.append(og)
     return layers
Ejemplo n.º 7
0
    def lowerToBottom(self):
        selectedItems = self.mMapScene.selectedObjectItems()
        objectGroup = RaiseLowerHelper.sameObjectGroup(selectedItems)
        if (not objectGroup):
            return
        if (objectGroup.drawOrder() != ObjectGroup.DrawOrder.IndexOrder):
            return
        ranges = RangeSet()
        for item in selectedItems:
            ranges.insert(int(item.zValue()))

        commands = QList()
        to = 0
        for it in ranges:
            first = it[0]
            _from = first
            count = it[1] - first + 1
            if (_from == to):
                to += count
                continue

            commands.append(
                ChangeMapObjectsOrder(self.mMapDocument, objectGroup, _from,
                                      to, count))
            to += count

        self.push(
            commands,
            QCoreApplication.translate("Undo Commands",
                                       "Lower Object To Bottom"))
Ejemplo n.º 8
0
 def tileLayers(self):
     layers = QList()
     for layer in self.mLayers:
         tl = layer.asTileLayer()
         if tl:
             layers.append(tl)
     return layers
Ejemplo n.º 9
0
 def tileLayers(self):
     layers = QList()
     for layer in self.mLayers:
         tl = layer.asTileLayer()
         if tl:
             layers.append(tl)
     return layers
Ejemplo n.º 10
0
 def setSelectedObjectItems(self, items):
     # Inform the map document about the newly selected objects
     selectedObjects = QList()
     #selectedObjects.reserve(items.size())
     for item in items:
         selectedObjects.append(item.mapObject())
     self.mMapDocument.setSelectedObjects(selectedObjects)
Ejemplo n.º 11
0
 def objects(_type=None):
     results = QList()
     for object in PluginManager.instance().mObjects:
         if _type != None and type(object) != _type:
             continue
         results.append(object)
     return results
Ejemplo n.º 12
0
    def init(self, parent):
        layout = QHBoxLayout(parent)
        layout.setContentsMargins(0, 0, 0, 0)
        self.m_treeWidget = QtPropertyEditorView(parent)
        self.m_treeWidget.setEditorPrivate(self)
        self.m_treeWidget.setIconSize(QSize(18, 18))
        layout.addWidget(self.m_treeWidget)
        parent.setFocusProxy(self.m_treeWidget)

        self.m_treeWidget.setColumnCount(2)
        labels = QList()
        labels.append(
            QCoreApplication.translate("QtTreePropertyBrowser", "Property"))
        labels.append(
            QCoreApplication.translate("QtTreePropertyBrowser", "Value"))
        self.m_treeWidget.setHeaderLabels(labels)
        self.m_treeWidget.setAlternatingRowColors(True)
        self.m_treeWidget.setEditTriggers(QAbstractItemView.EditKeyPressed)
        self.m_delegate = QtPropertyEditorDelegate(parent)
        self.m_delegate.setEditorPrivate(self)
        self.m_treeWidget.setItemDelegate(self.m_delegate)
        self.m_treeWidget.header().setSectionsMovable(False)
        self.m_treeWidget.header().setSectionResizeMode(QHeaderView.Stretch)

        self.m_expandIcon = drawIndicatorIcon(self.q_ptr.palette(),
                                              self.q_ptr.style())

        self.m_treeWidget.collapsed.connect(self.slotCollapsed)
        self.m_treeWidget.expanded.connect(self.slotExpanded)
        self.m_treeWidget.currentItemChanged.connect(
            self.slotCurrentTreeItemChanged)
Ejemplo n.º 13
0
 def objectGroups(self):
     layers = QList()
     for layer in self.mLayers:
         og = layer.asObjectGroup()
         if og:
             layers.append(og)
     return layers
Ejemplo n.º 14
0
 def interfaces(self):
     results = QList()
     for plugin in self.mPlugins:
         result = plugin.instance
         if result:
             results.append(result)
     return results
Ejemplo n.º 15
0
 def setSelectedObjectItems(self, items):
     # Inform the map document about the newly selected objects
     selectedObjects = QList()
     #selectedObjects.reserve(items.size())
     for item in items:
         selectedObjects.append(item.mapObject())
     self.mMapDocument.setSelectedObjects(selectedObjects)
Ejemplo n.º 16
0
class FormatHelper():
    
    def __init__(self, capabilities, initialFilter):       
        self.mFilter = initialFilter
        self.mFormats = QList()
        self.mFormatByNameFilter = QMap()
        
        def t(self, format):
            if (format.hasCapabilities(capabilities)):
                nameFilter = format.nameFilter()
                self.mFilter += ";;"
                self.mFilter += nameFilter
                self.mFormats.append(format)
                self.mFormatByNameFilter.insert(nameFilter, format)
        
        PluginManager.each(self, t)
    
    def filter(self):
        return self.mFilter
        
    def formats(self):
        return self.mFormats
        
    def formatByNameFilter(self, nameFilter):
        return self.mFormatByNameFilter.value(nameFilter)
Ejemplo n.º 17
0
    def setCurrentTiles(self, tiles):
        if (self.mCurrentTiles == tiles):
            return
        del self.mCurrentTiles
        self.mCurrentTiles = tiles
        # Set the selected tiles on the map document
        if (tiles):
            selectedTiles = QList()
            for y in range(tiles.height()):
                for x in range(tiles.width()):
                    cell = tiles.cellAt(x, y)
                    if (not cell.isEmpty()):
                        selectedTiles.append(cell.tile)

            self.mMapDocument.setSelectedTiles(selectedTiles)

            # Create a tile stamp with these tiles
            map = self.mMapDocument.map()
            stamp = Map(map.orientation(), tiles.width(), tiles.height(),
                        map.tileWidth(), map.tileHeight())
            stamp.addLayer(tiles.clone())
            stamp.addTilesets(tiles.usedTilesets())

            self.mEmittingStampCaptured = True
            self.stampCaptured.emit(TileStamp(stamp))
            self.mEmittingStampCaptured = False
Ejemplo n.º 18
0
    def changingObjects(self):
        changingObjects = QList()

        for movingObject in self.mMovingObjects:
            changingObjects.append(movingObject.item.mapObject())

        return changingObjects
    def init(self, parent):
        layout = QHBoxLayout(parent)
        layout.setContentsMargins(0, 0, 0, 0)
        self.m_treeWidget = QtPropertyEditorView(parent)
        self.m_treeWidget.setEditorPrivate(self)
        self.m_treeWidget.setIconSize(QSize(18, 18))
        layout.addWidget(self.m_treeWidget)
        parent.setFocusProxy(self.m_treeWidget)

        self.m_treeWidget.setColumnCount(2)
        labels = QList()
        labels.append(QCoreApplication.translate("QtTreePropertyBrowser", "Property"))
        labels.append(QCoreApplication.translate("QtTreePropertyBrowser", "Value"))
        self.m_treeWidget.setHeaderLabels(labels)
        self.m_treeWidget.setAlternatingRowColors(True)
        self.m_treeWidget.setEditTriggers(QAbstractItemView.EditKeyPressed)
        self.m_delegate = QtPropertyEditorDelegate(parent)
        self.m_delegate.setEditorPrivate(self)
        self.m_treeWidget.setItemDelegate(self.m_delegate)
        self.m_treeWidget.header().setSectionsMovable(False)
        self.m_treeWidget.header().setSectionResizeMode(QHeaderView.Stretch)

        self.m_expandIcon = drawIndicatorIcon(self.q_ptr.palette(), self.q_ptr.style())

        self.m_treeWidget.collapsed.connect(self.slotCollapsed)
        self.m_treeWidget.expanded.connect(self.slotExpanded)
        self.m_treeWidget.currentItemChanged.connect(self.slotCurrentTreeItemChanged)
Ejemplo n.º 20
0
 def interfaces(self):
     results = QList()
     for plugin in self.mPlugins:
         result = plugin.instance
         if result:
             results.append(result)
     return results
Ejemplo n.º 21
0
 def commit(self):
     # Save saveBeforeExecute option
     self.mSettings.setValue("saveBeforeExecute", self.mSaveBeforeExecute)
     # Save command list
     commands = QList()
     for command in self.mCommands:
         commands.append(command.toQVariant())
     self.mSettings.setValue("commandList", commands)
Ejemplo n.º 22
0
 def commit(self):
     # Save saveBeforeExecute option
     self.mSettings.setValue("saveBeforeExecute", self.mSaveBeforeExecute)
     # Save command list
     commands = QList()
     for command in self.mCommands:
         commands.append(command.toQVariant())
     self.mSettings.setValue("commandList", commands)
Ejemplo n.º 23
0
    def removeTiles(self):
        view = self.currentTilesetView()
        if (not view):
            return
        if (not view.selectionModel().hasSelection()):
            return
        indexes = view.selectionModel().selectedIndexes()
        model = view.tilesetModel()
        tileIds = RangeSet()
        tiles = QList()
        for index in indexes:
            tile = model.tileAt(index)
            if tile:
                tileIds.insert(tile.id())
                tiles.append(tile)

        def matchesAnyTile(cell):
            tile = cell.tile
            if tile:
                return tiles.contains(tile)
            return False

        inUse = self.hasTileReferences(self.mMapDocument, matchesAnyTile)
        # If the tileset is in use, warn the user and confirm removal
        if (inUse):
            warning = QMessageBox(
                QMessageBox.Warning, self.tr("Remove Tiles"),
                self.tr("One or more of the tiles to be removed are "
                        "still in use by the map!"),
                QMessageBox.Yes | QMessageBox.No, self)
            warning.setDefaultButton(QMessageBox.Yes)
            warning.setInformativeText(
                self.tr("Remove all references to these tiles?"))
            if (warning.exec() != QMessageBox.Yes):
                return

        undoStack = self.mMapDocument.undoStack()
        undoStack.beginMacro(self.tr("Remove Tiles"))
        removeTileReferences(self.mMapDocument, matchesAnyTile)
        # Iterate backwards over the ranges in order to keep the indexes valid
        firstRange = tileIds.begin()
        it = tileIds.end()
        if (it == firstRange):  # no range
            return
        tileset = view.tilesetModel().tileset()
        while (it != firstRange):
            it -= 1
            item = tileIds.item(it)
            length = item[1] - item[0] + 1
            undoStack.push(
                RemoveTiles(self.mMapDocument, tileset, item[0], length))

        undoStack.endMacro()
        # Clear the current tiles, will be referencing the removed tiles
        self.setCurrentTiles(None)
        self.setCurrentTile(None)
Ejemplo n.º 24
0
 def removeObject(self, og, o):
     objects = QList()
     objects.append(o)
     row = og.objects().indexOf(o)
     self.beginRemoveRows(self.index(og), row, row)
     og.removeObjectAt(row)
     self.mObjects.remove(o)
     self.endRemoveRows()
     self.objectsRemoved.emit(objects)
     return row
Ejemplo n.º 25
0
 def removeObject(self, og, o):
     objects = QList()
     objects.append(o)
     row = og.objects().indexOf(o)
     self.beginRemoveRows(self.index(og), row, row)
     og.removeObjectAt(row)
     self.mObjects.remove(o)
     self.endRemoveRows()
     self.objectsRemoved.emit(objects)
     return row
Ejemplo n.º 26
0
 def layers(self, *args):
     l = len(args)
     if l==0:
         return QList(self.mLayers)
     elif l==1:
         tp = args[0]
         layers = QList()
         for layer in self.mLayers:
             if (layer.layerType() == tp):
                 layers.append(layer)
         return layers
Ejemplo n.º 27
0
 def layers(self, *args):
     l = len(args)
     if l == 0:
         return QList(self.mLayers)
     elif l == 1:
         tp = args[0]
         layers = QList()
         for layer in self.mLayers:
             if (layer.layerType() == tp):
                 layers.append(layer)
         return layers
Ejemplo n.º 28
0
    def redo(self):
        i = self.mChanges.constBegin()
        changedTiles = QList()
        changedTiles.reserve(self.mChanges.size())
        while (i != self.mChanges.constEnd()):
            tile = i.key()
            change = i.value()
            tile.setTerrain(change.to)
            changedTiles.append(tile)
            i += 1

        self.mMapDocument.emitTileTerrainChanged(changedTiles)
    def intToEnum(self, metaEnum, intValue):
        valueMap = QMap() # dont show multiple enum values which have the same values
        values = QList()
        for i in range(metaEnum.keyCount()):
            value = metaEnum.value(i)
            if (not valueMap.contains(value)):
                valueMap[value] = True
                values.append(value)

        if (intValue >= values.count()):
            return -1
        return values.at(intValue)
Ejemplo n.º 30
0
    def intToEnum(self, metaEnum, intValue):
        valueMap = QMap(
        )  # dont show multiple enum values which have the same values
        values = QList()
        for i in range(metaEnum.keyCount()):
            value = metaEnum.value(i)
            if (not valueMap.contains(value)):
                valueMap[value] = True
                values.append(value)

        if (intValue >= values.count()):
            return -1
        return values.at(intValue)
Ejemplo n.º 31
0
 def copyObjectRegion(self, srcLayer, srcX, srcY, width, height, dstLayer, dstX, dstY):
     undo = self.mMapDocument.undoStack()
     rect = QRectF(srcX, srcY, width, height)
     pixelRect = self.mMapDocument.renderer().tileToPixelCoords_(rect)
     objects = objectsInRegion(srcLayer, pixelRect.toAlignedRect())
     pixelOffset = self.mMapDocument.renderer().tileToPixelCoords(dstX, dstY)
     pixelOffset -= pixelRect.topLeft()
     clones = QList()
     for obj in objects:
         clone = obj.clone()
         clones.append(clone)
         clone.setX(clone.x() + pixelOffset.x())
         clone.setY(clone.y() + pixelOffset.y())
         undo.push(AddMapObject(self.mMapDocument, dstLayer, clone))
Ejemplo n.º 32
0
    def changeSelectedNode(self):
        if not self.directSelection_:
            return

        nodeIDs = QList()
        for item in self.selectedItems():
            node = item.data(1, Qt.UserRole)
            nodeID = node.getID()
            nodeIDs.append(nodeID)

        if nodeIDs != self.selectedNodeIDs_:
            #print('NOH Tree, Selected Nodes:', nodeIDs, self.selectedNodeIDs_)
            self.selectedNodeIDs_ = nodeIDs
            self.itemsSelected.emit(nodeIDs)
Ejemplo n.º 33
0
    def duplicateObjects(self, objects):
        if (objects.isEmpty()):
            return
        self.mUndoStack.beginMacro(
            self.tr("Duplicate %n Object(s)", "", objects.size()))
        clones = QList()
        for mapObject in objects:
            clone = mapObject.clone()
            clones.append(clone)
            self.mUndoStack.push(
                AddMapObject(self, mapObject.objectGroup(), clone))

        self.mUndoStack.endMacro()
        self.setSelectedObjects(clones)
Ejemplo n.º 34
0
    def duplicateObjects(self, objects):
        if (objects.isEmpty()):
            return
        self.mUndoStack.beginMacro(self.tr("Duplicate %n Object(s)", "", objects.size()))
        clones = QList()
        for mapObject in objects:
            clone = mapObject.clone()
            clones.append(clone)
            self.mUndoStack.push(AddMapObject(self,
                                              mapObject.objectGroup(),
                                              clone))

        self.mUndoStack.endMacro()
        self.setSelectedObjects(clones)
Ejemplo n.º 35
0
 def copyObjectRegion(self, srcLayer, srcX, srcY, width, height, dstLayer,
                      dstX, dstY):
     undo = self.mMapDocument.undoStack()
     rect = QRectF(srcX, srcY, width, height)
     pixelRect = self.mMapDocument.renderer().tileToPixelCoords_(rect)
     objects = objectsInRegion(srcLayer, pixelRect.toAlignedRect())
     pixelOffset = self.mMapDocument.renderer().tileToPixelCoords(
         dstX, dstY)
     pixelOffset -= pixelRect.topLeft()
     clones = QList()
     for obj in objects:
         clone = obj.clone()
         clones.append(clone)
         clone.setX(clone.x() + pixelOffset.x())
         clone.setY(clone.y() + pixelOffset.y())
         undo.push(AddMapObject(self.mMapDocument, dstLayer, clone))
    def intToFlag(self, metaEnum, intValue):
        valueMap = QMap() # dont show multiple enum values which have the same values
        values = QList()
        for i in range(metaEnum.keyCount()):
            value = metaEnum.value(i)
            if (not valueMap.contains(value) and self.isPowerOf2(value)):
                valueMap[value] = True
                values.append(value)

        flagValue = 0
        temp = intValue
        i = 0
        while (temp):
            if (i >= values.count()):
                return -1
            if (temp & 1):
                flagValue |= values.at(i)
            i += 1
            temp = temp >> 1

        return flagValue
Ejemplo n.º 37
0
    def lower(self):
        if (not self.initContext()):
            return

        # For each range of objects, only the first will move
        commands = QList()
        for it in self.mSelectionRanges:
            value = it[0]
            # The first range may be already at the bottom of the related items
            if (value == 0):
                continue
            movingItem = self.mRelatedObjects.at(value)
            targetItem = self.mRelatedObjects.at(value - 1)
            _from = int(movingItem.zValue())
            to = int(targetItem.zValue())
            commands.append(
                ChangeMapObjectsOrder(self.mMapDocument, self.mObjectGroup,
                                      _from, to, 1))

        self.push(commands,
                  QCoreApplication.translate("Undo Commands", "Lower Object"))
Ejemplo n.º 38
0
    def pasteObjectGroup(self,
                         objectGroup,
                         mapDocument,
                         view,
                         mode=PasteMode.Standard):
        currentLayer = mapDocument.currentLayer()
        if (not currentLayer):
            return
        currentObjectGroup = currentLayer.asObjectGroup()
        if (not currentObjectGroup):
            return
        # Determine where to insert the objects
        renderer = mapDocument.renderer()
        center = objectGroup.objectsBoundingRect().center()
        # Take the mouse position if the mouse is on the view, otherwise
        # take the center of the view.
        viewPos = QPoint()
        if (view.underMouse()):
            viewPos = view.mapFromGlobal(QCursor.pos())
        else:
            viewPos = QPoint(view.width() / 2, view.height() / 2)
        scenePos = view.mapToScene(viewPos)
        insertPos = renderer.screenToPixelCoords_(scenePos) - center
        SnapHelper(renderer).snap(insertPos)
        undoStack = mapDocument.undoStack()
        pastedObjects = QList()
        pastedObjects.reserve(objectGroup.objectCount())
        undoStack.beginMacro(self.tr("Paste Objects"))
        for mapObject in objectGroup.objects():
            if (mode == PasteMode.NoTileObjects
                    and not mapObject.cell().isEmpty()):
                continue
            objectClone = mapObject.clone()
            objectClone.setPosition(objectClone.position() + insertPos)
            pastedObjects.append(objectClone)
            undoStack.push(
                AddMapObject(mapDocument, currentObjectGroup, objectClone))

        undoStack.endMacro()
        mapDocument.setSelectedObjects(pastedObjects)
Ejemplo n.º 39
0
    def intToFlag(self, metaEnum, intValue):
        valueMap = QMap(
        )  # dont show multiple enum values which have the same values
        values = QList()
        for i in range(metaEnum.keyCount()):
            value = metaEnum.value(i)
            if (not valueMap.contains(value) and self.isPowerOf2(value)):
                valueMap[value] = True
                values.append(value)

        flagValue = 0
        temp = intValue
        i = 0
        while (temp):
            if (i >= values.count()):
                return -1
            if (temp & 1):
                flagValue |= values.at(i)
            i += 1
            temp = temp >> 1

        return flagValue
Ejemplo n.º 40
0
    def raiseToTop(self):
        selectedItems = self.mMapScene.selectedObjectItems()
        objectGroup = RaiseLowerHelper.sameObjectGroup(selectedItems)
        if (not objectGroup):
            return
        if (objectGroup.drawOrder() != ObjectGroup.DrawOrder.IndexOrder):
            return
        ranges = RangeSet()
        for item in selectedItems:
            ranges.insert(int(item.zValue()))

        # Iterate backwards over the ranges in order to keep the indexes valid
        size = len(ranges)
        if size <= 0:  # no range
            return

        commands = QList()
        to = objectGroup.objectCount()
        for i in range(size - 1, -1, -1):
            it = ranges.item(i)
            first = it[0]
            last = it[1]
            count = last - first + 1
            if (last + 1 == to):
                to -= count
                continue

            _from = first
            commands.append(
                ChangeMapObjectsOrder(self.mMapDocument, objectGroup, _from,
                                      to, count))
            to -= count

        self.push(
            commands,
            QCoreApplication.translate("Undo Commands", "Raise Object To Top"))
Ejemplo n.º 41
0
class FormatHelper():
    def __init__(self, capabilities, initialFilter):
        self.mFilter = initialFilter
        self.mFormats = QList()
        self.mFormatByNameFilter = QMap()

        def t(self, format):
            if (format.hasCapabilities(capabilities)):
                nameFilter = format.nameFilter()
                self.mFilter += ";;"
                self.mFilter += nameFilter
                self.mFormats.append(format)
                self.mFormatByNameFilter.insert(nameFilter, format)

        PluginManager.each(self, t)

    def filter(self):
        return self.mFilter

    def formats(self):
        return self.mFormats

    def formatByNameFilter(self, nameFilter):
        return self.mFormatByNameFilter.value(nameFilter)
Ejemplo n.º 42
0
    def pasteObjectGroup(self, objectGroup, mapDocument, view, mode = PasteMode.Standard):
        currentLayer = mapDocument.currentLayer()
        if (not currentLayer):
            return
        currentObjectGroup = currentLayer.asObjectGroup()
        if (not currentObjectGroup):
            return
        # Determine where to insert the objects
        renderer = mapDocument.renderer()
        center = objectGroup.objectsBoundingRect().center()
        # Take the mouse position if the mouse is on the view, otherwise
        # take the center of the view.
        viewPos = QPoint()
        if (view.underMouse()):
            viewPos = view.mapFromGlobal(QCursor.pos())
        else:
            viewPos = QPoint(view.width() / 2, view.height() / 2)
        scenePos = view.mapToScene(viewPos)
        insertPos = renderer.screenToPixelCoords_(scenePos) - center
        SnapHelper(renderer).snap(insertPos)
        undoStack = mapDocument.undoStack()
        pastedObjects = QList()
        pastedObjects.reserve(objectGroup.objectCount())
        undoStack.beginMacro(self.tr("Paste Objects"))
        for mapObject in objectGroup.objects():
            if (mode == PasteMode.NoTileObjects and not mapObject.cell().isEmpty()):
                continue
            objectClone = mapObject.clone()
            objectClone.setPosition(objectClone.position() + insertPos)
            pastedObjects.append(objectClone)
            undoStack.push(AddMapObject(mapDocument,
                                             currentObjectGroup,
                                             objectClone))

        undoStack.endMacro()
        mapDocument.setSelectedObjects(pastedObjects)
Ejemplo n.º 43
0
    def currentObjects(self):
        objects = QList()
        if (self.mCurrentObject):
            if (self.mCurrentObject.typeId() == Object.MapObjectType and not self.mSelectedObjects.isEmpty()):
                for mapObj in self.mSelectedObjects:
                    objects.append(mapObj)
            elif (self.mCurrentObject.typeId() == Object.TileType and not self.mSelectedTiles.isEmpty()):
                for tile in self.mSelectedTiles:
                    objects.append(tile)

            else:
                objects.append(self.mCurrentObject)

        return objects
Ejemplo n.º 44
0
    def affectedLayerIndexes(self):
        layerIndexes = QList()
        map = self.mMapDocument.map()
        x = self.layerSelection()
        if x==OffsetMapDialog.AllVisibleLayers:
            for i in range(map.layerCount()):
                if (map.layerAt(i).isVisible()):
                    layerIndexes.append(i)
        elif x==OffsetMapDialog.AllLayers:
            for i in range(map.layerCount()):
                layerIndexes.append(i)
        elif x==OffsetMapDialog.SelectedLayer:
            layerIndexes.append(self.mMapDocument.currentLayerIndex())

        return layerIndexes
Ejemplo n.º 45
0
    def affectedLayerIndexes(self):
        layerIndexes = QList()
        map = self.mMapDocument.map()
        x = self.layerSelection()
        if x == OffsetMapDialog.AllVisibleLayers:
            for i in range(map.layerCount()):
                if (map.layerAt(i).isVisible()):
                    layerIndexes.append(i)
        elif x == OffsetMapDialog.AllLayers:
            for i in range(map.layerCount()):
                layerIndexes.append(i)
        elif x == OffsetMapDialog.SelectedLayer:
            layerIndexes.append(self.mMapDocument.currentLayerIndex())

        return layerIndexes
Ejemplo n.º 46
0
    def currentObjects(self):
        objects = QList()
        if (self.mCurrentObject):
            if (self.mCurrentObject.typeId() == Object.MapObjectType
                    and not self.mSelectedObjects.isEmpty()):
                for mapObj in self.mSelectedObjects:
                    objects.append(mapObj)
            elif (self.mCurrentObject.typeId() == Object.TileType
                  and not self.mSelectedTiles.isEmpty()):
                for tile in self.mSelectedTiles:
                    objects.append(tile)

            else:
                objects.append(self.mCurrentObject)

        return objects
Ejemplo n.º 47
0
class Tileset(Object):
    ##
    # Constructor.
    #
    # @param name        the name of the tileset
    # @param tileWidth   the width of the tiles in the tileset
    # @param tileHeight  the height of the tiles in the tileset
    # @param tileSpacing the spacing between the tiles in the tileset image
    # @param margin      the margin around the tiles in the tileset image
    ##
    def __init__(self, name, tileWidth, tileHeight, tileSpacing = 0, margin = 0):
        super().__init__(Object.TilesetType)

        self.mName = name
        self.mTileWidth = tileWidth
        self.mTileHeight = tileHeight
        self.mTileSpacing = tileSpacing
        self.mMargin = margin
        self.mImageWidth = 0
        self.mImageHeight = 0
        self.mColumnCount = 0
        self.mTerrainDistancesDirty = False

        self.mTileOffset = QPoint()
        self.mFileName = QString()
        self.mTiles = QList()
        self.mTransparentColor = QColor()
        self.mImageSource = QString()
        self.mTerrainTypes = QList()
        self.mWeakPointer = None

    ##
    # Destructor.
    ##
    def __del__(self):
        self.mTiles.clear()
        self.mTerrainTypes.clear()

    def create(name, tileWidth, tileHeight, tileSpacing = 0, margin = 0):
        tileset = Tileset(name, tileWidth, tileHeight, tileSpacing, margin)
        tileset.mWeakPointer = tileset
        return tileset
    
    def __iter__(self):
        return self.mTiles.__iter__()
        
    ##
    # Returns the name of this tileset.
    ##
    def name(self):
        return self.mName

    ##
    # Sets the name of this tileset.
    ##
    def setName(self, name):
        self.mName = name

    ##
    # Returns the file name of this tileset. When the tileset isn't an
    # external tileset, the file name is empty.
    ##
    def fileName(self):
        return self.mFileName

    ##
    # Sets the filename of this tileset.
    ##
    def setFileName(self, fileName):
        self.mFileName = fileName

    ##
    # Returns whether this tileset is external.
    ##
    def isExternal(self):
        return self.mFileName!=''

    ##
    # Returns the maximum width of the tiles in this tileset.
    ##
    def tileWidth(self):
        return self.mTileWidth

    ##
    # Returns the maximum height of the tiles in this tileset.
    ##
    def tileHeight(self):
        return self.mTileHeight

    ##
    # Returns the maximum size of the tiles in this tileset.
    ##
    def tileSize(self):
        return QSize(self.mTileWidth, self.mTileHeight)

    ##
    # Returns the spacing between the tiles in the tileset image.
    ##
    def tileSpacing(self):
        return self.mTileSpacing

    ##
    # Returns the margin around the tiles in the tileset image.
    ##
    def margin(self):
        return self.mMargin

    ##
    # Returns the offset that is applied when drawing the tiles in this
    # tileset.
    ##
    def tileOffset(self):
        return self.mTileOffset

    ##
    # @see tileOffset
    ##
    def setTileOffset(self, offset):
        self.mTileOffset = offset

    ##
    # Returns a const reference to the list of tiles in this tileset.
    ##
    def tiles(self):
        return QList(self.mTiles)

    ##
    # Returns the tile for the given tile ID.
    # The tile ID is local to this tileset, which means the IDs are in range
    # [0, tileCount() - 1].
    ##
    def tileAt(self, id):
        if id < self.mTiles.size():
            return self.mTiles.at(id)
        return None

    ##
    # Returns the number of tiles in this tileset.
    ##
    def tileCount(self):
        return self.mTiles.size()

    ##
    # Returns the number of tile columns in the tileset image.
    ##
    def columnCount(self):
        return self.mColumnCount

    ##
    # Returns the width of the tileset image.
    ##
    def imageWidth(self):
        return self.mImageWidth

    ##
    # Returns the height of the tileset image.
    ##
    def imageHeight(self):
        return self.mImageHeight

    ##
    # Returns the transparent color, or an invalid color if no transparent
    # color is used.
    ##
    def transparentColor(self):
        return QColor(self.mTransparentColor)

    ##
    # Sets the transparent color. Pixels with this color will be masked out
    # when loadFromImage() is called.
    ##
    def setTransparentColor(self, c):
        self.mTransparentColor = c

    ##
    # Load this tileset from the given tileset \a image. This will replace
    # existing tile images in this tileset with new ones. If the new image
    # contains more tiles than exist in the tileset new tiles will be
    # appended, if there are fewer tiles the excess images will be blanked.
    #
    # The tile width and height of this tileset must be higher than 0.
    #
    # @param image    the image to load the tiles from
    # @param fileName the file name of the image, which will be remembered
    #                 as the image source of this tileset.
    # @return <code>true</code> if loading was successful, otherwise
    #         returns <code>false</code>
    ##
    def loadFromImage(self, *args):
        l = len(args)
        if l==2:
            image, fileName = args

            tileSize = self.tileSize()
            margin = self.margin()
            spacing = self.tileSpacing()
    
            if (image.isNull()):
                return False
            stopWidth = image.width() - tileSize.width()
            stopHeight = image.height() - tileSize.height()
            oldTilesetSize = self.tileCount()
            tileNum = 0
            for y in range(margin, stopHeight+1, tileSize.height() + spacing):
                for x in range(margin, stopWidth+1, tileSize.width() + spacing):
                    tileImage = image.copy(x, y, tileSize.width(), tileSize.height())
                    tilePixmap = QPixmap.fromImage(tileImage)
                    if (self.mTransparentColor.isValid()):
                        mask = tileImage.createMaskFromColor(self.mTransparentColor.rgb())
                        tilePixmap.setMask(QBitmap.fromImage(mask))

                    if (tileNum < oldTilesetSize):
                        self.mTiles.at(tileNum).setImage(tilePixmap)
                    else:
                        self.mTiles.append(Tile(tilePixmap, tileNum, self))

                    tileNum += 1

            # Blank out any remaining tiles to avoid confusion
            while (tileNum < oldTilesetSize):
                tilePixmap = QPixmap(tileSize)
                tilePixmap.fill()
                self.mTiles.at(tileNum).setImage(tilePixmap)
                tileNum += 1

            self.mImageWidth = image.width()
            self.mImageHeight = image.height()
            self.mColumnCount = self.columnCountForWidth(self.mImageWidth)
            self.mImageSource = fileName
            return True
        elif l==1:
            ##
            # Convenience override that loads the image using the QImage constructor.
            ##
            fileName = args[0]
            return self.loadFromImage(QImage(fileName), fileName)

    ##
    # This checks if there is a similar tileset in the given list.
    # It is needed for replacing this tileset by its similar copy.
    ##
    def findSimilarTileset(self, tilesets):
        for candidate in tilesets:
            if (candidate.tileCount() != self.tileCount()):
                continue
            if (candidate.imageSource() != self.imageSource()):
                continue
            if (candidate.tileSize() != self.tileSize()):
                continue
            if (candidate.tileSpacing() != self.tileSpacing()):
                continue
            if (candidate.margin() != self.margin()):
                continue
            if (candidate.tileOffset() != self.tileOffset()):
                continue

            # For an image collection tileset, check the image sources
            if (self.imageSource()==''):
                if (not sameTileImages(self, candidate)):
                    continue

            return candidate
            
        return None

    ##
    # Returns the file name of the external image that contains the tiles in
    # this tileset. Is an empty string when this tileset doesn't have a
    # tileset image.
    ##
    def imageSource(self):
        return self.mImageSource

    ##
    # Returns the column count that this tileset would have if the tileset
    # image would have the given \a width. This takes into account the tile
    # size, margin and spacing.
    ##
    def columnCountForWidth(self, width):
        return int((width - self.mMargin + self.mTileSpacing) / (self.mTileWidth + self.mTileSpacing))

    ##
    # Returns a const reference to the list of terrains in this tileset.
    ##
    def terrains(self):
        return QList(self.mTerrainTypes)

    ##
    # Returns the number of terrain types in this tileset.
    ##
    def terrainCount(self):
        return self.mTerrainTypes.size()

    ##
    # Returns the terrain type at the given \a index.
    ##
    def terrain(self, index):
        if index >= 0:
            _x = self.mTerrainTypes[index]
        else:
            _x = None
        return _x

    ##
    # Adds a new terrain type.
    #
    # @param name      the name of the terrain
    # @param imageTile the id of the tile that represents the terrain visually
    # @return the created Terrain instance
    ##
    def addTerrain(self, name, imageTileId):
        terrain = Terrain(self.terrainCount(), self, name, imageTileId)
        self.insertTerrain(self.terrainCount(), terrain)
        return terrain

    ##
    # Adds the \a terrain type at the given \a index.
    #
    # The terrain should already have this tileset associated with it.
    ##
    def insertTerrain(self, index, terrain):
        self.mTerrainTypes.insert(index, terrain)
        # Reassign terrain IDs
        for terrainId in range(index, self.mTerrainTypes.size()):
            self.mTerrainTypes.at(terrainId).mId = terrainId
        # Adjust tile terrain references
        for tile in self.mTiles:
            for corner in range(4):
                terrainId = tile.cornerTerrainId(corner)
                if (terrainId >= index):
                    tile.setCornerTerrainId(corner, terrainId + 1)

        self.mTerrainDistancesDirty = True

    ##
    # Removes the terrain type at the given \a index and returns it. The
    # caller becomes responsible for the lifetime of the terrain type.
    #
    # This will cause the terrain ids of subsequent terrains to shift up to
    # fill the space and the terrain information of all tiles in this tileset
    # will be updated accordingly.
    ##
    def takeTerrainAt(self, index):
        terrain = self.mTerrainTypes.takeAt(index)
        # Reassign terrain IDs
        for terrainId in range(index, self.mTerrainTypes.size()):
            self.mTerrainTypes.at(terrainId).mId = terrainId

        # Clear and adjust tile terrain references
        for tile in self.mTiles:
            for corner in range(4):
                terrainId = tile.cornerTerrainId(corner)
                if (terrainId == index):
                    tile.setCornerTerrainId(corner, 0xFF)
                elif (terrainId > index):
                    tile.setCornerTerrainId(corner, terrainId - 1)

        self.mTerrainDistancesDirty = True
        return terrain

    ##
    # Returns the transition penalty(/distance) between 2 terrains. -1 if no
    # transition is possible.
    ##
    def terrainTransitionPenalty(self, terrainType0, terrainType1):
        if (self.mTerrainDistancesDirty):
            self.recalculateTerrainDistances()
            self.mTerrainDistancesDirty = False

        if terrainType0 == 255:
            terrainType0 = -1
        if terrainType1 == 255:
            terrainType1 = -1

        # Do some magic, since we don't have a transition array for no-terrain
        if (terrainType0 == -1 and terrainType1 == -1):
            return 0
        if (terrainType0 == -1):
            return self.mTerrainTypes.at(terrainType1).transitionDistance(terrainType0)
        return self.mTerrainTypes.at(terrainType0).transitionDistance(terrainType1)

    ##
    # Adds a new tile to the end of the tileset.
    ##
    def addTile(self, image, source=QString()):
        newTile = Tile(image, source, self.tileCount(), self)
        self.mTiles.append(newTile)
        if (self.mTileHeight < image.height()):
            self.mTileHeight = image.height()
        if (self.mTileWidth < image.width()):
            self.mTileWidth = image.width()
        return newTile

    def insertTiles(self, index, tiles):
        count = tiles.count()
        for i in range(count):
            self.mTiles.insert(index + i, tiles.at(i))
        # Adjust the tile IDs of the remaining tiles
        for i in range(index + count, self.mTiles.size()):
            self.mTiles.at(i).mId += count
        self.updateTileSize()

    def removeTiles(self, index, count):
        first = self.mTiles.begin() + index
        last = first + count
        last = self.mTiles.erase(first, last)
        # Adjust the tile IDs of the remaining tiles
        for last in self.mTiles:
            last.mId -= count
        self.updateTileSize()

    ##
    # Sets the \a image to be used for the tile with the given \a id.
    ##
    def setTileImage(self, id, image, source = QString()):
        # This operation is not supposed to be used on tilesets that are based
        # on a single image
        tile = self.tileAt(id)
        if (not tile):
            return
        previousImageSize = tile.image().size()
        newImageSize = image.size()
        tile.setImage(image)
        tile.setImageSource(source)
        if (previousImageSize != newImageSize):
            # Update our max. tile size
            if (previousImageSize.height() == self.mTileHeight or
                    previousImageSize.width() == self.mTileWidth):
                # This used to be the max image; we have to recompute
                self.updateTileSize()
            else:
                # Check if we have a new maximum
                if (self.mTileHeight < newImageSize.height()):
                    self.mTileHeight = newImageSize.height()
                if (self.mTileWidth < newImageSize.width()):
                    self.mTileWidth = newImageSize.width()

    ##
    # Used by the Tile class when its terrain information changes.
    ##
    def markTerrainDistancesDirty(self):
        self.mTerrainDistancesDirty = True

    ##
    # Sets tile size to the maximum size.
    ##
    def updateTileSize(self):
        maxWidth = 0
        maxHeight = 0
        for tile in self.mTiles:
            size = tile.size()
            if (maxWidth < size.width()):
                maxWidth = size.width()
            if (maxHeight < size.height()):
                maxHeight = size.height()

        self.mTileWidth = maxWidth
        self.mTileHeight = maxHeight

    ##
    # Calculates the transition distance matrix for all terrain types.
    ##
    def recalculateTerrainDistances(self):
        # some fancy macros which can search for a value in each byte of a word simultaneously
        def hasZeroByte(dword):
            return (dword - 0x01010101) & ~dword & 0x80808080

        def hasByteEqualTo(dword, value):
            return hasZeroByte(dword ^ int(~0/255 * value))

        # Terrain distances are the number of transitions required before one terrain may meet another
        # Terrains that have no transition path have a distance of -1
        for i in range(self.terrainCount()):
            type = self.terrain(i)
            distance = QVector()
            for _x in range(self.terrainCount() + 1):
                distance.append(-1)
            # Check all tiles for transitions to other terrain types
            for j in range(self.tileCount()):
                t = self.tileAt(j)
                if (not hasByteEqualTo(t.terrain(), i)):
                    continue
                # This tile has transitions, add the transitions as neightbours (distance 1)
                tl = t.cornerTerrainId(0)
                tr = t.cornerTerrainId(1)
                bl = t.cornerTerrainId(2)
                br = t.cornerTerrainId(3)
                # Terrain on diagonally opposite corners are not actually a neighbour
                if (tl == i or br == i):
                    distance[tr + 1] = 1
                    distance[bl + 1] = 1

                if (tr == i or bl == i):
                    distance[tl + 1] = 1
                    distance[br + 1] = 1

                # terrain has at least one tile of its own type
                distance[i + 1] = 0

            type.setTransitionDistances(distance)

        # Calculate indirect transition distances
        bNewConnections = False
        # Repeat while we are still making new connections (could take a
        # number of iterations for distant terrain types to connect)
        while bNewConnections:
            bNewConnections = False
            # For each combination of terrain types
            for i in range(self.terrainCount()):
                t0 = self.terrain(i)
                for j in range(self.terrainCount()):
                    if (i == j):
                        continue
                    t1 = self.terrain(j)
                    # Scan through each terrain type, and see if we have any in common
                    for t in range(-1, self.terrainCount()):
                        d0 = t0.transitionDistance(t)
                        d1 = t1.transitionDistance(t)
                        if (d0 == -1 or d1 == -1):
                            continue
                        # We have cound a common connection
                        d = t0.transitionDistance(j)
                        # If the new path is shorter, record the new distance
                        if (d == -1 or d0 + d1 < d):
                            d = d0 + d1
                            t0.setTransitionDistance(j, d)
                            t1.setTransitionDistance(i, d)
                            # We're making progress, flag for another iteration...
                            bNewConnections = True

    def sharedPointer(self):
        return self.mWeakPointer
Ejemplo n.º 48
0
def fillRegion(layer, fillOrigin):
    # Create that region that will hold the fill
    fillRegion = QRegion()
    # Silently quit if parameters are unsatisfactory
    if (not layer.contains(fillOrigin)):
        return fillRegion
    # Cache cell that we will match other cells against
    matchCell = layer.cellAt(fillOrigin)
    # Grab map dimensions for later use.
    layerWidth = layer.width()
    layerHeight = layer.height()
    layerSize = layerWidth * layerHeight
    # Create a queue to hold cells that need filling
    fillPositions = QList()
    fillPositions.append(fillOrigin)
    # Create an array that will store which cells have been processed
    # This is faster than checking if a given cell is in the region/list
    processedCellsVec = QVector()
    for i in range(layerSize):
        processedCellsVec.append(0xff)
    processedCells = processedCellsVec
    # Loop through queued positions and fill them, while at the same time
    # checking adjacent positions to see if they should be added
    while (not fillPositions.empty()):
        currentPoint = fillPositions.takeFirst()
        startOfLine = currentPoint.y() * layerWidth
        # Seek as far left as we can
        left = currentPoint.x()
        while (left > 0 and layer.cellAt(left - 1, currentPoint.y()) == matchCell):
            left -= 1
        # Seek as far right as we can
        right = currentPoint.x()
        while (right + 1 < layerWidth and layer.cellAt(right + 1, currentPoint.y()) == matchCell):
            right += 1
        # Add cells between left and right to the region
        fillRegion += QRegion(left, currentPoint.y(), right - left + 1, 1)
        # Add cell strip to processed cells
        for i in range(startOfLine + left, right + startOfLine, 1):
            processedCells[i] = 1
        # These variables cache whether the last cell was added to the queue
        # or not as an optimization, since adjacent cells on the x axis
        # do not need to be added to the queue.
        lastAboveCell = False
        lastBelowCell = False
        # Loop between left and right and check if cells above or
        # below need to be added to the queue
        for x in range(left, right+1):
            fillPoint = QPoint(x, currentPoint.y())
            # Check cell above
            if (fillPoint.y() > 0):
                aboveCell = QPoint(fillPoint.x(), fillPoint.y() - 1)
                if (not processedCells[aboveCell.y() * layerWidth + aboveCell.x()] and layer.cellAt(aboveCell) == matchCell):

                    # Do not add the above cell to the queue if its
                    # x-adjacent cell was added.
                    if (not lastAboveCell):
                        fillPositions.append(aboveCell)
                    lastAboveCell = True
                else:
                    lastAboveCell = False

                processedCells[aboveCell.y() * layerWidth + aboveCell.x()] = 1

            # Check cell below
            if (fillPoint.y() + 1 < layerHeight):
                belowCell = QPoint(fillPoint.x(), fillPoint.y() + 1)
                if (not processedCells[belowCell.y() * layerWidth + belowCell.x()] and layer.cellAt(belowCell) == matchCell):

                    # Do not add the below cell to the queue if its
                    # x-adjacent cell was added.
                    if (not lastBelowCell):
                        fillPositions.append(belowCell)
                    lastBelowCell = True
                else:
                    lastBelowCell = False

                processedCells[belowCell.y() * layerWidth + belowCell.x()] = 1

    return fillRegion
Ejemplo n.º 49
0
    def unifyTilesets(self, *args):
        l = len(args)
        if l==1:
            ##
            # Makes sure the all tilesets which are used at the given \a map will be
            # present in the map document.
            #
            # To reach the aim, all similar tilesets will be replaced by the version
            # in the current map document and all missing tilesets will be added to
            # the current map document.
            #
            # \warning This method assumes that the tilesets in \a map are managed by
            #          the TilesetManager!
            ##
            map = args[0]
            undoCommands = QList()
            existingTilesets = self.mMap.tilesets()
            tilesetManager = TilesetManager.instance()
            # Add tilesets that are not yet part of this map
            for tileset in map.tilesets():
                if (existingTilesets.contains(tileset)):
                    continue
                replacement = tileset.findSimilarTileset(existingTilesets)
                if (not replacement):
                    undoCommands.append(AddTileset(self, tileset))
                    continue

                # Merge the tile properties
                sharedTileCount = min(tileset.tileCount(), replacement.tileCount())
                for i in range(sharedTileCount):
                    replacementTile = replacement.tileAt(i)
                    properties = replacementTile.properties()
                    properties.merge(tileset.tileAt(i).properties())
                    undoCommands.append(ChangeProperties(self,
                                                             self.tr("Tile"),
                                                             replacementTile,
                                                             properties))

                map.replaceTileset(tileset, replacement)
                tilesetManager.addReference(replacement)
                tilesetManager.removeReference(tileset)

            if (not undoCommands.isEmpty()):
                self.mUndoStack.beginMacro(self.tr("Tileset Changes"))
                for command in undoCommands:
                    self.mUndoStack.push(command)
                self.mUndoStack.endMacro()
        elif l==2:
            map, missingTilesets = args
            
            existingTilesets = self.mMap.tilesets()
            tilesetManager = TilesetManager.instance()

            for tileset in map.tilesets():
                # tileset already added
                if existingTilesets.contains(tileset):
                    continue

                replacement = tileset.findSimilarTileset(existingTilesets)

                # tileset not present and no replacement tileset found
                if not replacement:
                    if not missingTilesets.contains(tileset):
                        missingTilesets.append(tileset)
                    continue

                # replacement tileset found, change given map
                map.replaceTileset(tileset, replacement)

                tilesetManager.addReference(replacement)
                tilesetManager.removeReference(tileset)
Ejemplo n.º 50
0
class CommandDataModel(QAbstractTableModel):
    NameColumn, CommandColumn, EnabledColumn = range(3)

    ##
    # Constructs the object and parses the users settings to allow easy
    # programmatic access to the command list.
    ##
    def __init__(self, parent):
        super().__init__(parent)
        
        self.mSettings = QSettings()
        self.mSaveBeforeExecute = False
        self.mCommands = QList()
        
        # Load saveBeforeExecute option
        s = self.mSettings.value("saveBeforeExecute", True)
        self.mSaveBeforeExecute = bool(s)
        # Load command list
        variant = self.mSettings.value("commandList")
        commands = variant
        if commands is None:
            commands = []
        for commandVariant in commands:
            self.mCommands.append(Command.fromQVariant(commandVariant))
        # Add default commands the first time the app has booted up.
        # This is useful on it's own and helps demonstrate how to use the commands.
        addPrefStr = "addedDefaultCommands"
        addedCommands = self.mSettings.value(addPrefStr, False)
        if (not addedCommands):
            # Disable default commands by default so user gets an informative
            # warning when clicking the command button for the first time
            command = Command(False)
            if sys.platform == 'linux':
                command.command = "gedit %mapfile"
            elif sys.platform == 'darwin':
                command.command = "open -t %mapfile"
            if (not command.command.isEmpty()):
                command.name = self.tr("Open in text editor")
                self.mCommands.push_back(command)

            self.commit()
            self.mSettings.setValue(addPrefStr, True)
            
    ##
    # Saves the data to the users preferences.
    ##
    def commit(self):
        # Save saveBeforeExecute option
        self.mSettings.setValue("saveBeforeExecute", self.mSaveBeforeExecute)
        # Save command list
        commands = QList()
        for command in self.mCommands:
            commands.append(command.toQVariant())
        self.mSettings.setValue("commandList", commands)

    ##
    # Returns whether saving before executing commands is enabled.
    ##
    def saveBeforeExecute(self):
        return self.mSaveBeforeExecute

    ##
    # Enables or disables saving before executing commands.
    ##
    def setSaveBeforeExecute(self, enabled):
        self.mSaveBeforeExecute = enabled

    ##
    # Returns the first enabled command in the list, or an empty
    # disabled command if there are no enabled commands.
    ##
    def firstEnabledCommand(self):
        for command in self.mCommands:
            if (command.isEnabled):
                return command
        return Command(False)

    ##
    # Returns a list of all the commands.
    ##
    def allCommands(self):
        return QList(self.mCommands)

    ##
    # Remove the given row or rows from the model.
    ##
    def removeRows(self, *args):
        l = len(args)
        if l>1 and l<4:
            row = args[0]
            count = args[1]
            if l==2:
                parent = QModelIndex()
            elif l==3:
                parent = args[2]

            if (row < 0 or row + count > self.mCommands.size()):
                return False
            self.beginRemoveRows(parent, row, row + count)
            self.mCommands.erase(self.mCommands.begin() + row, self.mCommands.begin() + row + count)
            self.endRemoveRows()
            return True
        elif l==1:
            indices = args[0]
            ##
             # Deletes the commands associated with the given row <i>indices</i>.
            ##
            while (not indices.empty()):
                row = indices.takeFirst().row()
                if (row >= self.mCommands.size()):
                    continue
                self.beginRemoveRows(QModelIndex(), row, row)
                self.mCommands.removeAt(row)
                # Decrement later indices since we removed a row
                for i in indices:
                    if (i.row() > row):
                       i = i.sibling(i.row() - 1, i.column())
                self.endRemoveRows()

    ##
    # Returns the number of rows (this includes the <New Command> row).
    ##
    def rowCount(self, parent):
        if parent.isValid():
            return 0
        else:
            return self.mCommands.size() + 1

    ##
    # Returns the number of columns.
    ##
    def columnCount(self, parent):
        if parent.isValid():
            return 0
        else:
            return 3

    ##
    # Returns the data at <i>index</i> for the given <i>role</i>.
    ##
    def data(self, index, role = Qt.DisplayRole):
        isNormalRow = index.row() < self.mCommands.size()
        command = Command()
        if (isNormalRow):
            command = self.mCommands[index.row()]
        x = role
        if x==Qt.DisplayRole or x==Qt.EditRole:
            if (isNormalRow):
                if (index.column() == CommandDataModel.NameColumn):
                    return command.name
                if (index.column() == CommandDataModel.CommandColumn):
                    return command.command
            else:
                if (index.column() == CommandDataModel.NameColumn):
                    if (role == Qt.EditRole):
                        return QString()
                    else:
                        return self.tr("<new command>")
        elif x==Qt.ToolTipRole:
            if (isNormalRow):
                if (index.column() == CommandDataModel.NameColumn):
                    return self.tr("Set a name for this command")
                if (index.column() == CommandDataModel.CommandColumn):
                    return self.tr("Set the shell command to execute")
                if (index.column() == CommandDataModel.EnabledColumn):
                    return self.tr("Show or hide this command in the command list")
            else:
                if (index.column() == CommandDataModel.NameColumn):
                    return self.tr("Add a new command")
        elif x==Qt.CheckStateRole:
            if (isNormalRow and index.column() == CommandDataModel.EnabledColumn):
                if command.isEnabled:
                    _x = 2
                else:
                    _x = 0
                return _x

        return QVariant()

    ##
    # Sets the data at <i>index</i> to the given <i>value</i>.
    # for the given <i>role</i>
    ##
    def setData(self, index, value, role):
        isNormalRow = index.row() < self.mCommands.size()
        isModified = False
        shouldAppend = False
        command = Command()
        if (isNormalRow):
            # Get the command as it exists already
            command = self.mCommands[index.row()]
            # Modify the command based on the passed date
            x = role
            if x==Qt.EditRole:
                text = value
                if text != '':
                    if (index.column() == CommandDataModel.NameColumn):
                        command.name = value
                        isModified = True
                    elif (index.column() == CommandDataModel.CommandColumn):
                        command.command = value
                        isModified = True
            elif x==Qt.CheckStateRole:
                if (index.column() == CommandDataModel.EnabledColumn):
                    command.isEnabled = value > 0
                    isModified = True

        else:
            # If final row was edited, insert the new command
            if (role == Qt.EditRole and index.column() == CommandDataModel.NameColumn):
                command.name = value
                if (command.name!='' and command.name!=self.tr("<new command>")):
                    isModified = True
                    shouldAppend = True

        if (isModified):
            # Write the modified command to our cache
            if (shouldAppend):
                self.mCommands.append(command)
            else:
                self.mCommands[index.row()] = command
            # Reset if there could be new rows or reordering, else emit dataChanged
            if (shouldAppend or index.column() == CommandDataModel.NameColumn):
                self.beginResetModel()
                self.endResetModel()
            else:
                self.dataChanged.emit(index, index)

        return isModified

    ##
    # Returns flags for the item at <i>index</i>.
    ##
    def flags(self, index):
        isNormalRow = index.row() < self.mCommands.size()
        f = super().flags(index)
        if (isNormalRow):
            f |= Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
            if (index.column() == CommandDataModel.EnabledColumn):
                f |= Qt.ItemIsUserCheckable
            else:
                f |= Qt.ItemIsEditable
        else:
            f |= Qt.ItemIsDropEnabled
            if (index.column() == CommandDataModel.NameColumn):
                f |= Qt.ItemIsEditable

        return f
    ##
    # Returns the header data for the given <i>section</i> and <i>role</i>.
    # <i>orientation</i> should be Qt.Horizontal.
    ##
    def headerData(self, section, orientation, role = Qt.EditRole):
        if (role != Qt.DisplayRole or orientation != Qt.Horizontal):
            return QVariant()
        sectionLabels = ["Name", "Command", "Enable"]
        return self.tr(sectionLabels[section])

    ##
    # Returns a menu containing a list of appropriate actions for the item at
    # <i>index</i>, or 0 if there are no actions for the index.
    ##
    def contextMenu(self, parent, index):
        menu = None
        row = index.row()
        if (row >= 0 and row < self.mCommands.size()):
            menu = QMenu(parent)
            if (row > 0):
                action = menu.addAction(self.tr("Move Up"))
                mapper = QSignalMapper(action)
                mapper.setMapping(action, row)
                action.triggered.connect(mapper.map)
                mapper.mapped.connect(self.moveUp)

            if (row+1 < self.mCommands.size()):
                action = menu.addAction(self.tr("Move Down"))
                mapper = QSignalMapper(action)
                mapper.setMapping(action, row + 1)
                action.triggered.connect(mapper.map)
                mapper.mapped.connect(self.moveUp)

            menu.addSeparator()

            action = menu.addAction(self.tr("Execute"))
            mapper = QSignalMapper(action)
            mapper.setMapping(action, row)
            action.triggered.connect(mapper.map)
            mapper.mapped.connect(self.execute)

            if sys.platform in ['linux', 'darwin']:
                action = menu.addAction(self.tr("Execute in Terminal"))
                mapper = QSignalMapper(action)
                mapper.setMapping(action, row)
                action.triggered.connect(mapper.map)
                mapper.mapped.connect(self.executeInTerminal)

            menu.addSeparator()

            action = menu.addAction(self.tr("Delete"))
            mapper = QSignalMapper(action)
            mapper.setMapping(action, row)
            action.triggered.connect(mapper.map)
            mapper.mapped.connect(self.remove)

        return menu
    ##
    # Returns mime data for the first index in <i>indexes</i>.
    ##
    def mimeData(self, indices):
        row = -1
        for index in indices:
            # Only generate mime data on command rows
            if (index.row() < 0 or index.row() >= self.mCommands.size()):
                return None
            # Currently only one row at a time is supported for drags
            # Note: we can get multiple indexes in the same row (different columns)
            if (row != -1 and index.row() != row):
                return None
            row = index.row()

        command = self.mCommands[row]
        mimeData = QMimeData()
        # Text data is used if command is dragged to a text editor or terminal
        mimeData.setText(command.finalCommand())
        # Ptr is used if command is dragged onto another command
        # We could store the index instead, the only difference would be that if
        # the item is moved or deleted shomehow during the drag, the ptr approach
        # will result in a no-op instead of moving the wrong thing.
        addr = command
        mimeData.setData(commandMimeType, QByteArray(addr, 4))
        return mimeData

    ##
    # Returns a list of mime types that can represent a command.
    ##
    def mimeTypes(self):
        result = QStringList("text/plain")
        result.append(commandMimeType)
        return result

    ##
    # Returns the drop actions that can be performed.
    ##
    def supportedDropActions(self):
        return Qt.CopyAction | Qt.MoveAction

    ##
    # Handles dropping of mime data onto <i>parent</i>.
    ##
    def dropMimeData(self, data, action, row, column, parent):
        if (not parent.isValid()):
            return False
        dstRow = parent.row()
        if (data.hasFormat(commandMimeType)):
            # Get the ptr to the command that was being dragged
            byteData = data.data(commandMimeType)
            addr = byteData.data()
            # Find the command in the command list so we can move/copy it
            for srcRow in range(self.mCommands.size()):
                if (addr == self.mCommands[srcRow]):
                    # If a command is dropped on another command,
                    # move the src command into the positon of the dst command.
                    if (dstRow < self.mCommands.size()):
                        return self.move(srcRow, dstRow)
                    # If a command is dropped elsewhere, create a copy of it
                    if (dstRow == self.mCommands.size()):
                        self.append(Command(addr.isEnabled,
                                       self.tr("%s (copy)"%addr.name),
                                       addr.command))
                        return True

        if (data.hasText()):
            # If text is dropped on a valid command, just replace the data
            if (dstRow < self.mCommands.size()):
                return self.setData(parent, data.text(), Qt.EditRole)
            # If text is dropped elsewhere, create a new command
            # Assume the dropped text is the command, not the name
            if (dstRow == self.mCommands.size()):
                self.append(Command(True, self.tr("New command"), data.text()))
                return True

        return False

    ##
    # Moves the command at <i>commandIndex</i> to <i>newIndex></i>.
    ##
    def move(self, commandIndex, newIndex):
        commandIndex = self.mCommands.size()
        newIndex = self.mCommands.size()
        if (commandIndex or newIndex or newIndex == commandIndex):
            return False
        tmp = newIndex
        if newIndex > commandIndex:
            tmp += 1

        if (not self.beginMoveRows(QModelIndex(), commandIndex, commandIndex, QModelIndex(), tmp)):
            return False
        if (commandIndex - newIndex == 1 or newIndex - commandIndex == 1):
            # Swapping is probably more efficient than removing/inserting
            self.mCommands.swap(commandIndex, newIndex)
        else:
            command = self.mCommands.at(commandIndex)
            self.mCommands.removeAt(commandIndex)
            self.mCommands.insert(newIndex, command)

        self.endMoveRows()
        return True

    ##
    # Appends <i>command</i> to the command list.
    ##
    def append(self, command):
        self.beginInsertRows(QModelIndex(), self.mCommands.size(), self.mCommands.size())
        self.mCommands.append(command)
        self.endInsertRows()

    ##
    # Moves the command at <i>commandIndex</i> up one index, if possible.
    ##
    def moveUp(self, commandIndex):
        self.move(commandIndex, commandIndex - 1)

    ##
    # Executes the command at<i>commandIndex</i>.
    ##
    def execute(self, commandIndex):
        self.mCommands.at(commandIndex).execute()

    ##
    # Executes the command at <i>commandIndex</i> within the systems native
    # terminal if available.
    ##
    def executeInTerminal(self, commandIndex):
        self.mCommands.at(commandIndex).execute(True)

    ##
    # Deletes the command at <i>commandIndex</i>.
    ##
    def remove(self, commandIndex):
        self.removeRow(commandIndex)
Ejemplo n.º 51
0
class Map(Object):

    ##
    # The orientation of the map determines how it should be rendered. An
    # Orthogonal map is using rectangular tiles that are aligned on a
    # straight grid. An Isometric map uses diamond shaped tiles that are
    # aligned on an isometric projected grid. A Hexagonal map uses hexagon
    # shaped tiles that fit into each other by shifting every other row.
    ##
    class Orientation(Enum):
        Unknown, Orthogonal, Isometric, Staggered, Hexagonal = range(5)

    ##
    # The different formats in which the tile layer data can be stored.
    ##
    class LayerDataFormat(Enum):
        XML        = 0
        Base64     = 1
        Base64Gzip = 2
        Base64Zlib = 3
        CSV        = 4

    ##
    # The order in which tiles are rendered on screen.
    ##
    class RenderOrder(Enum):
        RightDown  = 0
        RightUp    = 1
        LeftDown   = 2
        LeftUp     = 3

    ##
    # Which axis is staggered. Only used by the isometric staggered and
    # hexagonal map renderers.
    ##
    class StaggerAxis(Enum):
        StaggerX, StaggerY = range(2)

    ##
    # When staggering, specifies whether the odd or the even rows/columns are
    # shifted half a tile right/down. Only used by the isometric staggered and
    # hexagonal map renderers.
    ##
    class StaggerIndex(Enum):
        StaggerOdd  = 0
        StaggerEven = 1

    def __init__(self, *args):
        self.mOrientation = 0
        self.mRenderOrder = 0
        self.mWidth = 0
        self.mHeight = 0
        self.mTileWidth = 0
        self.mTileHeight = 0
        self.mHexSideLength = 0
        self.mStaggerAxis = 0
        self.mStaggerIndex = 0
        self.mBackgroundColor = QColor()
        self.mDrawMargins = QMargins()
        self.mLayers = QList()
        self.mTilesets = QVector()
        self.mLayerDataFormat = None
        self.mNextObjectId = 0

        l = len(args)
        if l==1:
            ##
            # Copy constructor. Makes sure that a deep-copy of the layers is created.
            ##
            map = args[0]
            super().__init__(map)

            self.mLayers = QList()
            self.mOrientation = map.mOrientation
            self.mRenderOrder = map.mRenderOrder
            self.mWidth = map.mWidth
            self.mHeight = map.mHeight
            self.mTileWidth = map.mTileWidth
            self.mTileHeight = map.mTileHeight
            self.mHexSideLength = map.mHexSideLength
            self.mStaggerAxis = map.mStaggerAxis
            self.mStaggerIndex = map.mStaggerIndex
            self.mBackgroundColor = map.mBackgroundColor
            self.mDrawMargins = map.mDrawMargins
            self.mTilesets = map.mTilesets
            self.mLayerDataFormat = map.mLayerDataFormat
            self.mNextObjectId = 1
            for layer in map.mLayers:
                clone = layer.clone()
                clone.setMap(self)
                self.mLayers.append(clone)
        elif l==5:
            ##
            # Constructor, taking map orientation, size and tile size as parameters.
            ##
            orientation, width, height, tileWidth, tileHeight = args
            super().__init__(Object.MapType)

            self.mLayers = QList()
            self.mTilesets = QList()
            self.mOrientation = orientation
            self.mRenderOrder = Map.RenderOrder.RightDown
            self.mWidth = width
            self.mHeight = height
            self.mTileWidth = tileWidth
            self.mTileHeight = tileHeight
            self.mHexSideLength = 0
            self.mStaggerAxis = Map.StaggerAxis.StaggerY
            self.mStaggerIndex = Map.StaggerIndex.StaggerOdd
            self.mLayerDataFormat = Map.LayerDataFormat.Base64Zlib
            self.mNextObjectId = 1

    ##
    # Destructor.
    ##
    def __del__(self):
        self.mLayers.clear()

    ##
    # Returns the orientation of the map.
    ##
    def orientation(self):
        return self.mOrientation

    ##
    # Sets the orientation of the map.
    ##
    def setOrientation(self, orientation):
        self.mOrientation = orientation

    ##
    # Returns the render order of the map.
    ##
    def renderOrder(self):
        return self.mRenderOrder

    ##
    # Sets the render order of the map.
    ##
    def setRenderOrder(self, renderOrder):
        self.mRenderOrder = renderOrder

    ##
    # Returns the width of this map in tiles.
    ##
    def width(self):
        return self.mWidth

    ##
    # Sets the width of this map in tiles.
    ##
    def setWidth(self, width):
        self.mWidth = width

    ##
    # Returns the height of this map in tiles.
    ##
    def height(self):
        return self.mHeight

    ##
    # Sets the height of this map in tiles.
    ##
    def setHeight(self, height):
        self.mHeight = height

    ##
    # Returns the size of this map. Provided for convenience.
    ##
    def size(self):
        return QSize(self.mWidth, self.mHeight)

    ##
    # Returns the tile width of this map.
    ##
    def tileWidth(self):
        return self.mTileWidth

    ##
    # Sets the width of one tile.
    ##
    def setTileWidth(self, width):
        self.mTileWidth = width

    ##
    # Returns the tile height used by this map.
    ##
    def tileHeight(self):
        return self.mTileHeight

    ##
    # Sets the height of one tile.
    ##
    def setTileHeight(self, height):
        self.mTileHeight = height

    ##
    # Returns the size of one tile. Provided for convenience.
    ##
    def tileSize(self):
        return QSize(self.mTileWidth, self.mTileHeight)

    def hexSideLength(self):
        return self.mHexSideLength

    def setHexSideLength(self, hexSideLength):
        self.mHexSideLength = hexSideLength

    def staggerAxis(self):
        return self.mStaggerAxis

    def setStaggerAxis(self, staggerAxis):
        self.mStaggerAxis = staggerAxis

    def staggerIndex(self):
        return self.mStaggerIndex

    def setStaggerIndex(self, staggerIndex):
        self.mStaggerIndex = staggerIndex

    ##
    # Adjusts the draw margins to be at least as big as the given margins.
    # Called from tile layers when their tiles change.
    ##
    def adjustDrawMargins(self, margins):
        # The TileLayer includes the maximum tile size in its draw margins. So
        # we need to subtract the tile size of the map, since that part does not
        # contribute to additional margin.
        self.mDrawMargins = maxMargins(QMargins(margins.left(),
                                           margins.top() - self.mTileHeight,
                                           margins.right() - self.mTileWidth,
                                           margins.bottom()),
                                  self.mDrawMargins)

    ##
    # Computes the extra margins due to layer offsets. These need to be taken into
    # account when determining the bounding rect of the map for example.
    ##
    def computeLayerOffsetMargins(self):
        offsetMargins = QMargins()

        for layer in self.mLayers:
            offset = layer.offset()
            offsetMargins = maxMargins(QMargins(math.ceil(-offset.x()),
                                                math.ceil(-offset.y()),
                                                math.ceil(offset.x()),
                                                math.ceil(offset.y())),
                                       offsetMargins)

        return offsetMargins

    ##
    # Returns the margins that have to be taken into account when figuring
    # out which part of the map to repaint after changing some tiles.
    #
    # @see TileLayer.drawMargins
    ##
    def drawMargins(self):
        return self.mDrawMargins

    ##
    # Recomputes the draw margins for this map and each of its tile layers. Needed
    # after the tile offset of a tileset has changed for example.
    #
    # \sa TileLayer.recomputeDrawMargins
    ##
    def recomputeDrawMargins(self):
        self.mDrawMargins = QMargins()
        for layer in self.mLayers:
            tileLayer = layer.asTileLayer()
            if tileLayer:
                tileLayer.recomputeDrawMargins()

    ##
    # Returns the number of layers of this map.
    ##
    def layerCount(self, *args):
        l = len(args)
        if l==0:
            return self.mLayers.size()
        elif l==1:
            ##
            # Convenience function that returns the number of layers of this map that
            # match the given \a type.
            ##
            tp = args[0]

            count = 0
            for layer in self.mLayers:
               if (layer.layerType() == tp):
                   count += 1
            return count

    def tileLayerCount(self):
        return self.layerCount(Layer.TileLayerType)

    def objectGroupCount(self):
        return self.layerCount(Layer.ObjectGroupType)

    def imageLayerCount(self):
        return self.layerCount(Layer.ImageLayerType)

    ##
    # Returns the layer at the specified index.
    ##
    def layerAt(self, index):
        return self.mLayers.at(index)

    ##
    # Returns the list of layers of this map. This is useful when you want to
    # use foreach.
    ##
    def layers(self, *args):
        l = len(args)
        if l==0:
            return QList(self.mLayers)
        elif l==1:
            tp = args[0]
            layers = QList()
            for layer in self.mLayers:
                if (layer.layerType() == tp):
                    layers.append(layer)
            return layers

    def objectGroups(self):
        layers = QList()
        for layer in self.mLayers:
            og = layer.asObjectGroup()
            if og:
                layers.append(og)
        return layers

    def tileLayers(self):
        layers = QList()
        for layer in self.mLayers:
            tl = layer.asTileLayer()
            if tl:
                layers.append(tl)
        return layers

    ##
    # Adds a layer to this map.
    ##
    def addLayer(self, layer):
        self.adoptLayer(layer)
        self.mLayers.append(layer)

    ##
    # Returns the index of the layer given by \a layerName, or -1 if no
    # layer with that name is found.
    #
    # The second optional parameter specifies the layer types which are
    # searched.
    ##
    def indexOfLayer(self, layerName, layertypes = Layer.AnyLayerType):
        for index in range(self.mLayers.size()):
            if (self.layerAt(index).name() == layerName and (layertypes & self.layerAt(index).layerType())):
                return index
        return -1

    ##
    # Adds a layer to this map, inserting it at the given index.
    ##
    def insertLayer(self, index, layer):
        self.adoptLayer(layer)
        self.mLayers.insert(index, layer)

    ##
    # Removes the layer at the given index from this map and returns it.
    # The caller becomes responsible for the lifetime of this layer.
    ##
    def takeLayerAt(self, index):
        layer = self.mLayers.takeAt(index)
        layer.setMap(None)
        return layer

    ##
    # Adds a tileset to this map. The map does not take ownership over its
    # tilesets, this is merely for keeping track of which tilesets are used by
    # the map, and their saving order.
    #
    # @param tileset the tileset to add
    ##
    def addTileset(self, tileset):
        self.mTilesets.append(tileset)

    ##
    # Convenience function to be used together with Layer.usedTilesets()
    ##
    def addTilesets(self, tilesets):
        for tileset in tilesets:
            self.addTileset(tileset)
            
    ##
    # Inserts \a tileset at \a index in the list of tilesets used by this map.
    ##
    def insertTileset(self, index, tileset):
        self.mTilesets.insert(index, tileset)

    ##
    # Returns the index of the given \a tileset, or -1 if it is not used in
    # this map.
    ##
    def indexOfTileset(self, tileset):
        return self.mTilesets.indexOf(tileset)

    ##
    # Removes the tileset at \a index from this map.
    #
    # \warning Does not make sure that this map no longer refers to tiles from
    #          the removed tileset!
    #
    # \sa addTileset
    ##
    def removeTilesetAt(self, index):
        self.mTilesets.removeAt(index)

    ##
    # Replaces all tiles from \a oldTileset with tiles from \a newTileset.
    # Also replaces the old tileset with the new tileset in the list of
    # tilesets.
    ##
    def replaceTileset(self, oldTileset, newTileset):
        index = self.mTilesets.indexOf(oldTileset)
        for layer in self.mLayers:
            layer.replaceReferencesToTileset(oldTileset, newTileset)
        self.mTilesets[index] = newTileset

    ##
    # Returns the number of tilesets of this map.
    ##
    def tilesetCount(self):
        return self.mTilesets.size()

    ##
    # Returns the tileset at the given index.
    ##
    def tilesetAt(self, index):
        return self.mTilesets.at(index)

    ##
    # Returns the tilesets that the tiles on this map are using.
    ##
    def tilesets(self):
        return QList(self.mTilesets)

    ##
    # Returns the background color of this map.
    ##
    def backgroundColor(self):
        return QColor(self.mBackgroundColor)

    ##
    # Sets the background color of this map.
    ##
    def setBackgroundColor(self, color):
        self.mBackgroundColor = color

    ##
    # Returns whether the given \a tileset is used by any tile layer of this
    # map.
    ##
    def isTilesetUsed(self, tileset):
        for layer in self.mLayers:
            if (layer.referencesTileset(tileset)):
                return True
        return False

    ##
    # Creates a new map that contains the given \a layer. The map size will be
    # determined by the size of the layer.
    #
    # The orientation defaults to Unknown and the tile width and height will
    # default to 0. In case this map needs to be rendered, these properties
    # will need to be properly set.
    ##
    def fromLayer(layer):
        result = Map(Map.Orientation.Unknown, layer.width(), layer.height(), 0, 0)
        result.addLayer(layer)
        return result

    def layerDataFormat(self):
        return self.mLayerDataFormat

    def setLayerDataFormat(self, format):
        self.mLayerDataFormat = format

    ##
    # Sets the next id to be used for objects on this map.
    ##
    def setNextObjectId(self, nextId):
        self.mNextObjectId = nextId

    ##
    # Returns the next object id for this map.
    ##
    def nextObjectId(self):
        return self.mNextObjectId

    ##
    # Returns the next object id for this map and allocates a new one.
    ##
    def takeNextObjectId(self):
        return self.mNextObjectId+1

    def adoptLayer(self, layer):
        layer.setMap(self)
        tileLayer = layer.asTileLayer()
        if tileLayer:
            self.adjustDrawMargins(tileLayer.drawMargins())
        group = layer.asObjectGroup()
        if group:
            for o in group.objects():
                if (o.id() == 0):
                    o.setId(self.takeNextObjectId())
Ejemplo n.º 52
0
    def updateBrush(self, cursorPos, _list = None):
        # get the current tile layer
        currentLayer = self.currentTileLayer()
        layerWidth = currentLayer.width()
        layerHeight = currentLayer.height()
        numTiles = layerWidth * layerHeight
        paintCorner = 0
        # if we are in vertex paint mode, the bottom right corner on the map will appear as an invalid tile offset...
        if (self.mBrushMode == BrushMode.PaintVertex):
            if (cursorPos.x() == layerWidth):
                cursorPos.setX(cursorPos.x() - 1)
                paintCorner |= 1

            if (cursorPos.y() == layerHeight):
                cursorPos.setY(cursorPos.y() - 1)
                paintCorner |= 2

        # if the cursor is outside of the map, bail out
        if (not currentLayer.bounds().contains(cursorPos)):
            return
        terrainTileset = None
        terrainId = -1
        if (self.mTerrain):
            terrainTileset = self.mTerrain.tileset()
            terrainId = self.mTerrain.id()

        # allocate a buffer to build the terrain tilemap (TODO: this could be retained per layer to save regular allocation)
        newTerrain = []
        for i in range(numTiles):
            newTerrain.append(0)
            
        # allocate a buffer of flags for each tile that may be considered (TODO: this could be retained per layer to save regular allocation)
        checked = array('B')
        for i in range(numTiles):
            checked.append(0)
        # create a consideration list, and push the start points
        transitionList = QList()
        initialTiles = 0
        if (_list):
            # if we were supplied a list of start points
            for p in _list:
                transitionList.append(p)
                initialTiles += 1

        else:
            transitionList.append(cursorPos)
            initialTiles = 1

        brushRect = QRect(cursorPos, cursorPos)
        # produce terrain with transitions using a simple, relative naive approach (considers each tile once, and doesn't allow re-consideration if selection was bad)
        while (not transitionList.isEmpty()):
            # get the next point in the consideration list
            p = transitionList.takeFirst()
            x = p.x()
            y = p.y()
            i = y*layerWidth + x
            # if we have already considered this point, skip to the next
            # TODO: we might want to allow re-consideration if prior tiles... but not for now, this would risk infinite loops
            if (checked[i]):
                continue
            tile = currentLayer.cellAt(p).tile
            currentTerrain = terrain(tile)
            # get the tileset for this tile
            tileset = None
            if (terrainTileset):
                # if we are painting a terrain, then we'll use the terrains tileset
                tileset = terrainTileset
            elif (tile):
                # if we're erasing terrain, use the individual tiles tileset (to search for transitions)
                tileset = tile.tileset()
            else:
                # no tile here and we're erasing terrain, not much we can do
                continue

            # calculate the ideal tile for this position
            preferredTerrain = 0xFFFFFFFF
            mask = 0
            if (initialTiles):
                # for the initial tiles, we will insert the selected terrain and add the surroundings for consideration
                if (self.mBrushMode == BrushMode.PaintTile):
                    # set the whole tile to the selected terrain
                    preferredTerrain = makeTerrain(terrainId)
                    mask = 0xFFFFFFFF
                else:
                    # Bail out if encountering a tile from a different tileset
                    if (tile and tile.tileset() != tileset):
                        continue
                    # calculate the corner mask
                    mask = 0xFF << (3 - paintCorner)*8
                    # mask in the selected terrain
                    preferredTerrain = (currentTerrain & ~mask) | (terrainId << (3 - paintCorner)*8)

                initialTiles -= 1
                # if there's nothing to paint... skip this tile
                if (preferredTerrain == currentTerrain and (not tile or tile.tileset() == tileset)):
                    continue
            else:
                # Bail out if encountering a tile from a different tileset
                if (tile and tile.tileset() != tileset):
                    continue
                # following tiles each need consideration against their surroundings
                preferredTerrain = currentTerrain
                mask = 0
                # depending which connections have been set, we update the preferred terrain of the tile accordingly
                if (y > 0 and checked[i - layerWidth]):
                    preferredTerrain = ((terrain(newTerrain[i - layerWidth]) << 16) | (preferredTerrain & 0x0000FFFF))&0xFFFFFFFF
                    mask |= 0xFFFF0000

                if (y < layerHeight - 1 and checked[i + layerWidth]):
                    preferredTerrain = ((terrain(newTerrain[i + layerWidth]) >> 16) | (preferredTerrain & 0xFFFF0000))&0xFFFFFFFF
                    mask |= 0x0000FFFF

                if (x > 0 and checked[i - 1]):
                    preferredTerrain = (((terrain(newTerrain[i - 1]) << 8) & 0xFF00FF00) | (preferredTerrain & 0x00FF00FF))&0xFFFFFFFF
                    mask |= 0xFF00FF00

                if (x < layerWidth - 1 and checked[i + 1]):
                    preferredTerrain = (((terrain(newTerrain[i + 1]) >> 8) & 0x00FF00FF) | (preferredTerrain & 0xFF00FF00))&0xFFFFFFFF
                    mask |= 0x00FF00FF

            # find the most appropriate tile in the tileset
            paste = None
            if (preferredTerrain != 0xFFFFFFFF):
                paste = findBestTile(tileset, preferredTerrain, mask)
                if (not paste):
                    continue

            # add tile to the brush
            newTerrain[i] = paste
            checked[i] = True
            # expand the brush rect to fit the edit set
            brushRect |= QRect(p, p)
            # consider surrounding tiles if terrain constraints were not satisfied
            if (y > 0 and not checked[i - layerWidth]):
                above = currentLayer.cellAt(x, y - 1).tile
                if (topEdge(paste) != bottomEdge(above)):
                    transitionList.append(QPoint(x, y - 1))

            if (y < layerHeight - 1 and not checked[i + layerWidth]):
                below = currentLayer.cellAt(x, y + 1).tile
                if (bottomEdge(paste) != topEdge(below)):
                    transitionList.append(QPoint(x, y + 1))

            if (x > 0 and not checked[i - 1]):
                left = currentLayer.cellAt(x - 1, y).tile
                if (leftEdge(paste) != rightEdge(left)):
                    transitionList.append(QPoint(x - 1, y))

            if (x < layerWidth - 1 and not checked[i + 1]):
                right = currentLayer.cellAt(x + 1, y).tile
                if (rightEdge(paste) != leftEdge(right)):
                    transitionList.append(QPoint(x + 1, y))

        # create a stamp for the terrain block
        stamp = TileLayer(QString(), brushRect.left(), brushRect.top(), brushRect.width(), brushRect.height())
        for y in range(brushRect.top(), brushRect.bottom()+1):
            for x in range(brushRect.left(), brushRect.right()+1):
                i = y*layerWidth + x
                if (not checked[i]):
                    continue
                tile = newTerrain[i]
                if (tile):
                    stamp.setCell(x - brushRect.left(), y - brushRect.top(), Cell(tile))
                else:
                    # TODO: we need to do something to erase tiles where checked[i] is True, and newTerrain[i] is NULL
                    # is there an eraser stamp? investigate how the eraser works...
                    pass

        # set the new tile layer as the brush
        self.brushItem().setTileLayer(stamp)
        del checked
        del newTerrain
        self.mPaintX = cursorPos.x()
        self.mPaintY = cursorPos.y()
        self.mOffsetX = cursorPos.x() - brushRect.left()
        self.mOffsetY = cursorPos.y() - brushRect.top()
Ejemplo n.º 53
0
    item3 = rectManager.addProperty("geometry")
    item0.addSubProperty(item3)

    item4 = sizePolicyManager.addProperty("sizePolicy")
    item0.addSubProperty(item4)

    item5 = sizeManager.addProperty("sizeIncrement")
    item0.addSubProperty(item5)

    item7 = boolManager.addProperty("mouseTracking")
    item0.addSubProperty(item7)

    item8 = enumManager.addProperty("direction")
    enumNames = QList()
    enumNames.append("Up")
    enumNames.append("Right")
    enumNames.append("Down")
    enumNames.append("Left")

    enumManager.setEnumNames(item8, enumNames)
    enumIcons = QMap()
    enumIcons[0] = QIcon(":/demo/images/up.png")
    enumIcons[1] = QIcon(":/demo/images/right.png")
    enumIcons[2] = QIcon(":/demo/images/down.png")
    enumIcons[3] = QIcon(":/demo/images/left.png")
    enumManager.setEnumIcons(item8, enumIcons)
    item0.addSubProperty(item8)

    item9 = intManager.addProperty("value")
    intManager.setRange(item9, -100, 100)
Ejemplo n.º 54
0
    def unifyTilesets(self, *args):
        l = len(args)
        if l == 1:
            ##
            # Makes sure the all tilesets which are used at the given \a map will be
            # present in the map document.
            #
            # To reach the aim, all similar tilesets will be replaced by the version
            # in the current map document and all missing tilesets will be added to
            # the current map document.
            #
            # \warning This method assumes that the tilesets in \a map are managed by
            #          the TilesetManager!
            ##
            map = args[0]
            undoCommands = QList()
            existingTilesets = self.mMap.tilesets()
            tilesetManager = TilesetManager.instance()
            # Add tilesets that are not yet part of this map
            for tileset in map.tilesets():
                if (existingTilesets.contains(tileset)):
                    continue
                replacement = tileset.findSimilarTileset(existingTilesets)
                if (not replacement):
                    undoCommands.append(AddTileset(self, tileset))
                    continue

                # Merge the tile properties
                sharedTileCount = min(tileset.tileCount(),
                                      replacement.tileCount())
                for i in range(sharedTileCount):
                    replacementTile = replacement.tileAt(i)
                    properties = replacementTile.properties()
                    properties.merge(tileset.tileAt(i).properties())
                    undoCommands.append(
                        ChangeProperties(self, self.tr("Tile"),
                                         replacementTile, properties))

                map.replaceTileset(tileset, replacement)
                tilesetManager.addReference(replacement)
                tilesetManager.removeReference(tileset)

            if (not undoCommands.isEmpty()):
                self.mUndoStack.beginMacro(self.tr("Tileset Changes"))
                for command in undoCommands:
                    self.mUndoStack.push(command)
                self.mUndoStack.endMacro()
        elif l == 2:
            map, missingTilesets = args

            existingTilesets = self.mMap.tilesets()
            tilesetManager = TilesetManager.instance()

            for tileset in map.tilesets():
                # tileset already added
                if existingTilesets.contains(tileset):
                    continue

                replacement = tileset.findSimilarTileset(existingTilesets)

                # tileset not present and no replacement tileset found
                if not replacement:
                    if not missingTilesets.contains(tileset):
                        missingTilesets.append(tileset)
                    continue

                # replacement tileset found, change given map
                map.replaceTileset(tileset, replacement)

                tilesetManager.addReference(replacement)
                tilesetManager.removeReference(tileset)
Ejemplo n.º 55
0
class PluginManager(QObject):
    mInstance = None
    objectAdded = pyqtSignal(list)
    objectAboutToBeRemoved = pyqtSignal(list)
    
    def __init__(self):
        super().__init__()
        
        self.mObjects = QList()
        self.mPlugins = QList()
        self.fileName = ''
        
    def __del__(self):
        pass

    ##
    # Returns the plugin manager instance.
    ##
    def instance():
        if (not PluginManager.mInstance):
            PluginManager.mInstance = PluginManager()
        return PluginManager.mInstance

    def deleteInstance():
        del PluginManager.mInstance
        PluginManager.mInstance = None

    ##
    # Adds the given \a object. This allows the object to be found later based
    # on the interfaces it implements.
    ##
    def addObject(object):
        PluginManager.instance().mObjects.append(object)
        PluginManager.instance().objectAdded.emit([object])
       
    ##
    # Removes the given \a object.
    ##
    def removeObject(self, object):
        PluginManager.instance().objectAboutToBeRemoved.emit([object])
        PluginManager.instance().mObjects.removeOne(object)

    ##
    # Returns the list of objects that implement a given interface.
    ##
    def objects(_type=None):
        results = QList()
        for object in PluginManager.instance().mObjects:
            if _type!=None and type(object)!=_type:
                continue
            results.append(object)
        return results

    ##
    # Scans the plugin directory for plugins and attempts to load them.
    ##
    def loadPlugins(self):
        from plugins.csv.csvplugin import CsvPlugin
        from plugins.droidcraft.droidcraftplugin import DroidcraftPlugin
        from plugins.flare.flareplugin import FlarePlugin
        from plugins.json.jsonplugin import JsonPlugin
        from plugins.lua.luaplugin import LuaPlugin
        from plugins.replicaisland.replicaislandplugin import ReplicaIslandPlugin
        from plugins.tengine.tengineplugin import TenginePlugin
        from plugins.tmw.tmwplugin import TmwPlugin
        
        static_plugins = [
            {'name':'CsvPlugin', 'instance':CsvPlugin},
            {'name':'DroidcraftPlugin', 'instance':DroidcraftPlugin},
            {'name':'FlarePlugin', 'instance':FlarePlugin},
            {'name':'JsonPlugin', 'instance':JsonPlugin},
            {'name':'LuaPlugin', 'instance':LuaPlugin},
            {'name':'ReplicaIslandPlugin', 'instance':ReplicaIslandPlugin},
            {'name':'TenginePlugin', 'instance':TenginePlugin},
            {'name':'TmwPlugin', 'instance':TmwPlugin}
        ]
        
        # Load static plugins
        for plugin in static_plugins:
            name = plugin['name']
            instance = plugin['instance']
            self.mPlugins.append(LoadedPlugin(name, instance))
            if hasattr(instance, 'initialize'):
                instance().initialize()
            else:
                PluginManager.addObject(instance())

    ##
    # Returns the list of plugins found by the plugin manager.
    ##
    def plugins(self):
        return QList(self.fileName)

    ##
    # Returns the list of plugins that implement a given interface.
    ##
    def interfaces(self):
        results = QList()
        for plugin in self.mPlugins:
            result = plugin.instance
            if result:
                results.append(result)
        return results

    ##
    # Calls the given function for each object implementing a given interface.
    ##
    def each(helper, function):
        for object in PluginManager.instance().mObjects:
            if object:
                function(helper, object)
    
    def pluginByFileName(self, pluginFileName):
        for plugin in self.mPlugins:
            if (pluginFileName == plugin.fileName):
                return plugin
        return None

    ##
    # Returns the plugin, which implements the given interface.
    # This must be done via searching the plugins for the right plugins,
    # since casting doesn't work, an interface is not a plugin.
    ##
    def plugin(self, interface):
        for plugin in self.mPlugins:
            result = plugin.instance
            if result:
                if (result == interface):
                    return plugin
        return None
class QtGroupBoxPropertyBrowserPrivate():
    def __init__(self):
        self.q_ptr = None
        self.m_indexToItem = QMap()
        self.m_itemToIndex = QMap()
        self.m_widgetToItem = QMap()
        self.m_mainLayout = 0
        self.m_children = QList()
        self.m_recreateQueue = QList()

    def createEditor(self, property, parent):
        return self.q_ptr.createEditor(property, parent)

    def init(self, parent):
        self.m_mainLayout = QGridLayout()
        parent.setLayout(self.m_mainLayout)
        item = QSpacerItem(0, 0, QSizePolicy.Fixed, QSizePolicy.Expanding)
        self.m_mainLayout.addItem(item, 0, 0)

    def slotEditorDestroyed(self):
        editor = self.q_ptr.sender()
        if (not editor):
            return
        if (not editor in self.m_widgetToItem.keys()):
            return
        self.m_widgetToItem[editor].widget = 0
        self.m_widgetToItem.remove(editor)

    def slotUpdate(self):
        for item in self.m_recreateQueue:
            par = item.parent
            w = 0
            l = 0
            oldRow = -1
            if (not par):
                w = self.q_ptr
                l = self.m_mainLayout
                oldRow = self.m_children.indexOf(item)
            else:
                w = par.groupBox
                l = par.layout
                oldRow = par.children.indexOf(item)
                if (self.hasHeader(par)):
                    oldRow += 2

            if (item.widget):
                item.widget.setParent(w)
            elif (item.widgetLabel):
                item.widgetLabel.setParent(w)
            else:
                item.widgetLabel = QLabel(w)
                item.widgetLabel.setSizePolicy(QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed))
                item.widgetLabel.setTextFormat(Qt.PlainText)

            span = 1
            if (item.widget):
                l.addWidget(item.widget, oldRow, 1, 1, 1)
            elif (item.widgetLabel):
                l.addWidget(item.widgetLabel, oldRow, 1, 1, 1)
            else:
                span = 2
            item.label = QLabel(w)
            item.label.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            l.addWidget(item.label, oldRow, 0, 1, span)

            self.updateItem(item)

        self.m_recreateQueue.clear()

    def updateLater(self):
        QTimer.singleShot(0, self.q_ptr, self.slotUpdate())

    def propertyInserted(self, index, afterIndex):
        afterItem = self.m_indexToItem[afterIndex]
        parentItem = self.m_indexToItem.value(index.parent())

        newItem = WidgetItem()
        newItem.parent = parentItem

        layout = 0
        parentWidget = 0
        row = -1
        if (not afterItem):
            row = 0
            if (parentItem):
                parentItem.children.insert(0, newItem)
            else:
                self.m_children.insert(0, newItem)
        else:
            if (parentItem):
                row = parentItem.children.indexOf(afterItem) + 1
                parentItem.children.insert(row, newItem)
            else:
                row = self.m_children.indexOf(afterItem) + 1
                self.m_children.insert(row, newItem)

        if (parentItem and self.hasHeader(parentItem)):
            row += 2

        if (not parentItem):
            layout = self.m_mainLayout
            parentWidget = self.q_ptr
        else:
            if not parentItem.groupBox:
                self.m_recreateQueue.removeAll(parentItem)
                par = parentItem.parent
                w = 0
                l = 0
                oldRow = -1
                if (not par):
                    w = self.q_ptr
                    l = self.m_mainLayout
                    oldRow = self.m_children.indexOf(parentItem)
                else:
                    w = par.groupBox
                    l = par.layout
                    oldRow = par.children.indexOf(parentItem)
                    if (self.hasHeader(par)):
                        oldRow += 2

                parentItem.groupBox = QGroupBox(w)
                parentItem.layout = QGridLayout()
                parentItem.groupBox.setLayout(parentItem.layout)
                if (parentItem.label):
                    l.removeWidget(parentItem.label)
                    parentItem.label.close()
                    parentItem.label = 0

                if (parentItem.widget):
                    l.removeWidget(parentItem.widget)
                    parentItem.widget.setParent(parentItem.groupBox)
                    parentItem.layout.addWidget(parentItem.widget, 0, 0, 1, 2)
                    parentItem.line = QFrame(parentItem.groupBox)
                elif (parentItem.widgetLabel):
                    l.removeWidget(parentItem.widgetLabel)
                    parentItem.widgetLabel.close()
                    parentItem.widgetLabel = 0

                if (parentItem.line):
                    parentItem.line.setFrameShape(QFrame.HLine)
                    parentItem.line.setFrameShadow(QFrame.Sunken)
                    parentItem.layout.addWidget(parentItem.line, 1, 0, 1, 2)

                l.addWidget(parentItem.groupBox, oldRow, 0, 1, 2)
                self.updateItem(parentItem)

            layout = parentItem.layout
            parentWidget = parentItem.groupBox

        newItem.label = QLabel(parentWidget)
        newItem.label.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        newItem.widget = self.createEditor(index.property(), parentWidget)
        if (not newItem.widget):
            newItem.widgetLabel = QLabel(parentWidget)
            newItem.widgetLabel.setSizePolicy(QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed))
            newItem.widgetLabel.setTextFormat(Qt.PlainText)
        else:
            newItem.widget.destroyed.connect(self.slotEditorDestroyed)
            self.m_widgetToItem[newItem.widget] = newItem

        self.insertRow(layout, row)
        span = 1
        if (newItem.widget):
            layout.addWidget(newItem.widget, row, 1)
        elif (newItem.widgetLabel):
            layout.addWidget(newItem.widgetLabel, row, 1)
        else:
            span = 2
        layout.addWidget(newItem.label, row, 0, 1, span)

        self.m_itemToIndex[newItem] = index
        self.m_indexToItem[index] = newItem

        self.updateItem(newItem)

    def propertyRemoved(self, index):
        item = self.m_indexToItem[index]

        self.m_indexToItem.remove(index)
        self.m_itemToIndex.remove(item)

        parentItem = item.parent

        row = -1

        if (parentItem):
            row = parentItem.children.indexOf(item)
            parentItem.children.removeAt(row)
            if (self.hasHeader(parentItem)):
                row += 2
        else:
            row = self.m_children.indexOf(item)
            self.m_children.removeAt(row)

        if (item.widget):
            item.widget.close()
            del item.widget
        if (item.label):
            item.label.close()
            del item.label
        if (item.widgetLabel):
            item.widgetLabel.close()
            del item.widgetLabel
        if (item.groupBox):
            item.groupBox.close()
            del item.groupBox

        if (not parentItem):
            self.removeRow(self.m_mainLayout, row)
        elif len(parentItem.children) > 0:
            self.removeRow(parentItem.layout, row)
        else:
            par = parentItem.parent
            l = 0
            oldRow = -1
            if (not par):
                l = self.m_mainLayout
                oldRow = self.m_children.indexOf(parentItem)
            else:
                l = par.layout
                oldRow = par.children.indexOf(parentItem)
                if (self.hasHeader(par)):
                    oldRow += 2

            if (parentItem.widget):
                parentItem.widget.hide()
                parentItem.widget.setParent(0)
            elif (parentItem.widgetLabel):
                parentItem.widgetLabel.hide()
                parentItem.widgetLabel.setParent(0)
            else:
                #parentItem.widgetLabel = QLabel(w)
                pass

            l.removeWidget(parentItem.groupBox)
            parentItem.groupBox.close()
            parentItem.groupBox = 0
            parentItem.line = 0
            parentItem.layout = 0
            if (not parentItem in self.m_recreateQueue):
                 self.m_recreateQueue.append(parentItem)
            self.updateLater()

        self.m_recreateQueue.removeAll(item)

        del item

    def insertRow(self, layout, row):
        itemToPos = QMap()
        idx = 0
        while (idx < layout.count()):
            r, c, rs, cs = layout.getItemPosition(idx)
            if (r >= row):
                itemToPos[layout.takeAt(idx)] = QRect(r + 1, c, rs, cs)
            else:
                idx += 1

        for it in itemToPos.keys():
            r = itemToPos[it]
            layout.addItem(it, r.x(), r.y(), r.width(), r.height())

    def removeRow(self, layout, row):
        itemToPos = QMap()
        idx = 0
        while (idx < layout.count()):
            r, c, rs, cs = layout.getItemPosition(idx)
            if (r > row):
                itemToPos[layout.takeAt(idx)] = QRect(r - 1, c, rs, cs)
            else:
                idx += 1

        for it in itemToPos.keys():
            r = itemToPos[it]
            layout.addItem(it, r.x(), r.y(), r.width(), r.height())

    def hasHeader(self, item):
        if (item.widget):
            return True
        return False

    def propertyChanged(self, index):
        item = self.m_indexToItem[index]

        self.updateItem(item)

    def updateItem(self, item):
        property = self.m_itemToIndex[item].property()
        if (item.groupBox):
            font = item.groupBox.font()
            font.setUnderline(property.isModified())
            item.groupBox.setFont(font)
            item.groupBox.setTitle(property.propertyName())
            item.groupBox.setToolTip(property.toolTip())
            item.groupBox.setStatusTip(property.statusTip())
            item.groupBox.setWhatsThis(property.whatsThis())
            item.groupBox.setEnabled(property.isEnabled())

        if (item.label):
            font = item.label.font()
            font.setUnderline(property.isModified())
            item.label.setFont(font)
            item.label.setText(property.propertyName())
            item.label.setToolTip(property.toolTip())
            item.label.setStatusTip(property.statusTip())
            item.label.setWhatsThis(property.whatsThis())
            item.label.setEnabled(property.isEnabled())

        if (item.widgetLabel):
            font = item.widgetLabel.font()
            font.setUnderline(False)
            item.widgetLabel.setFont(font)
            item.widgetLabel.setText(property.valueText())
            item.widgetLabel.setToolTip(property.valueText())
            item.widgetLabel.setEnabled(property.isEnabled())

        if (item.widget):
            font = item.widget.font()
            font.setUnderline(False)
            item.widget.setFont(font)
            item.widget.setEnabled(property.isEnabled())
            item.widget.setToolTip(property.valueText())
Ejemplo n.º 57
0
class QtButtonPropertyBrowserPrivate():
    def __init__(self):
        self.q_ptr = None
        self.WidgetItem = WidgetItem()
        self.m_indexToItem = QMap()
        self.m_itemToIndex = QMap()
        self.m_widgetToItem = QMap()
        self.m_buttonToItem = QMap()
        self.m_mainLayout = None
        self.m_children = QList()
        self.m_recreateQueue = QList()

    def createEditor(self, property, parent):
        return self.q_ptr.createEditor(property, parent)

    def createButton(self, parent=None):
        button = QToolButton(parent)
        button.setCheckable(True)
        button.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
        button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        button.setArrowType(Qt.DownArrow)
        button.setIconSize(QSize(3, 16))
        ###
        #QIcon icon
        #icon.addPixmap(self.style().standardPixmap(QStyle.SP_ArrowDown), QIcon.Normal, QIcon.Off)
        #icon.addPixmap(self.style().standardPixmap(QStyle.SP_ArrowUp), QIcon.Normal, QIcon.On)
        #button.setIcon(icon)
        ###
        return button

    def gridRow(self, item):
        siblings = QList()
        if (item.parent):
            siblings = item.parent.children
        else:
            siblings = self.m_children

        row = 0
        for sibling in siblings:
            if (sibling == item):
                return row
            row += self.gridSpan(sibling)

        return -1

    def gridSpan(self, item):
        if (item.container and item.expanded):
            return 2
        return 1

    def init(self, parent):
        self.m_mainLayout = QGridLayout()
        parent.setLayout(self.m_mainLayout)
        item = QSpacerItem(0, 0, QSizePolicy.Fixed, QSizePolicy.Expanding)
        self.m_mainLayout.addItem(item, 0, 0)

    def slotEditorDestroyed(self):
        editor = self.q_ptr.sender()
        if (not editor):
            return
        if not self.m_widgetToItem.get(editor):
            return
        self.m_widgetToItem[editor].widget = 0
        self.m_widgetToItem.remove(editor)

    def slotUpdate(self):
        for item in self.m_recreateQueue:
            parent = item.parent
            w = 0
            l = 0
            oldRow = self.gridRow(item)
            if (parent):
                w = parent.container
                l = parent.layout
            else:
                w = self.q_ptr
                l = self.m_mainLayout

            span = 1
            if (not item.widget and not item.widgetLabel):
                span = 2
            item.label = QLabel(w)
            item.label.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            l.addWidget(item.label, oldRow, 0, 1, span)

            self.updateItem(item)

        self.m_recreateQueue.clear()

    def setExpanded(self, item, expanded):
        if (item.expanded == expanded):
            return

        if (not item.container):
            return

        item.expanded = expanded
        row = self.gridRow(item)
        parent = item.parent
        l = 0
        if (parent):
            l = parent.layout
        else:
            l = self.m_mainLayout

        if (expanded):
            self.insertRow(l, row + 1)
            l.addWidget(item.container, row + 1, 0, 1, 2)
            item.container.show()
        else:
            l.removeWidget(item.container)
            item.container.hide()
            self.removeRow(l, row + 1)

        item.button.setChecked(expanded)
        if expanded:
            item.button.setArrowType(Qt.UpArrow)
        else:
            item.button.setArrowType(Qt.DownArrow)

    def slotToggled(self, checked):
        item = self.m_buttonToItem[self.q_ptr.sender()]
        if (not item):
            return

        self.setExpanded(item, checked)

        if (checked):
            self.q_ptr.expandedSignal.emit(self.m_itemToIndex[item])
        else:
            self.q_ptr.collapsedSignal.emit(self.m_itemToIndex[item])

    def updateLater(self):
        QTimer.singleShot(0, self.slotUpdate)

    def propertyInserted(self, index, afterIndex):
        afterItem = self.m_indexToItem[afterIndex]
        parentItem = self.m_indexToItem.value(index.parent())

        newItem = WidgetItem()
        newItem.parent = parentItem

        layout = 0
        parentWidget = 0
        row = -1
        if (not afterItem):
            row = 0
            if (parentItem):
                parentItem.children.insert(0, newItem)
            else:
                self.m_children.insert(0, newItem)
        else:
            row = self.gridRow(afterItem) + self.gridSpan(afterItem)
            if (parentItem):
                parentItem.children.insert(parentItem.children.indexOf(afterItem) + 1, newItem)
            else:
                self.m_children.insert(self.m_children.indexOf(afterItem) + 1, newItem)

        if (not parentItem):
            layout = self.m_mainLayout
            parentWidget = self.q_ptr
        else:
            if (not parentItem.container):
                self.m_recreateQueue.removeAll(parentItem)
                grandParent = parentItem.parent
                l = 0
                oldRow = self.gridRow(parentItem)
                if (grandParent):
                    l = grandParent.layout
                else:
                    l = self.m_mainLayout

                container = QFrame()
                container.setFrameShape(QFrame.Panel)
                container.setFrameShadow(QFrame.Raised)
                parentItem.container = container
                parentItem.button = self.createButton()
                self.m_buttonToItem[parentItem.button] = parentItem
                parentItem.button.toggled.connect(self.slotToggled)
                parentItem.layout = QGridLayout()
                container.setLayout(parentItem.layout)
                if (parentItem.label):
                    l.removeWidget(parentItem.label)
                    parentItem.label.close()
                    parentItem.label = 0

                span = 1
                if (not parentItem.widget and not parentItem.widgetLabel):
                    span = 2
                l.addWidget(parentItem.button, oldRow, 0, 1, span)
                self.updateItem(parentItem)

            layout = parentItem.layout
            parentWidget = parentItem.container

        newItem.label = QLabel(parentWidget)
        newItem.label.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        newItem.widget = self.createEditor(index.property(), parentWidget)
        if (newItem.widget):
            newItem.widget.destroyed.connect(self.slotEditorDestroyed)
            self.m_widgetToItem[newItem.widget] = newItem
        elif (index.property().hasValue()):
            newItem.widgetLabel = QLabel(parentWidget)
            newItem.widgetLabel.setSizePolicy(QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed))

        self.insertRow(layout, row)
        span = 1
        if (newItem.widget):
            layout.addWidget(newItem.widget, row, 1)
        elif (newItem.widgetLabel):
            layout.addWidget(newItem.widgetLabel, row, 1)
        else:
            span = 2
        layout.addWidget(newItem.label, row, 0, span, 1)

        self.m_itemToIndex[newItem] = index
        self.m_indexToItem[index] = newItem

        self.updateItem(newItem)

    def propertyRemoved(self, index):
        item = self.m_indexToItem[index]

        self.m_indexToItem.remove(index)
        self.m_itemToIndex.remove(item)

        parentItem = item.parent

        row = self.gridRow(item)

        if (parentItem):
            parentItem.children.removeAt(parentItem.children.indexOf(item))
        else:
            self.m_children.removeAt(self.m_children.indexOf(item))

        colSpan = self.gridSpan(item)

        self.m_buttonToItem.remove(item.button)

        if (item.widget):
            item.widget.close()
            del item.widget
        if (item.label):
            item.label.close()
            del item.label
        if (item.widgetLabel):
            item.widgetLabel.close()
            del item.widgetLabel
        if (item.button):
            item.button.close()
            del item.button
        if (item.container):
            item.container.close()
            del item.container

        if (not parentItem):
            self.removeRow(self.m_mainLayout, row)
            if (colSpan > 1):
                self.removeRow(self.m_mainLayout, row)
        elif (len(parentItem.children) != 0):
            self.removeRow(parentItem.layout, row)
            if (colSpan > 1):
                self.removeRow(parentItem.layout, row)
        else:
            grandParent = parentItem.parent
            l = 0
            if (grandParent):
                l = grandParent.layout
            else:
                l = self.m_mainLayout

            parentRow = self.gridRow(parentItem)
            parentSpan = self.gridSpan(parentItem)

            l.removeWidget(parentItem.button)
            l.removeWidget(parentItem.container)
            parentItem.button.close()
            del parentItem.button
            parentItem.container.close()
            del parentItem.container
            parentItem.button = 0
            parentItem.container = 0
            parentItem.layout = 0
            if (not parentItem in self.m_recreateQueue):
                 self.m_recreateQueue.append(parentItem)
            if (parentSpan > 1):
                self.removeRow(l, parentRow + 1)

            self.updateLater()

        self.m_recreateQueue.removeAll(item)

        del item

    def insertRow(self, layout, row):
        itemToPos = QMap()
        idx = 0
        while (idx < len(layout)):
            r, c, rs, cs = layout.getItemPosition(idx)
            if (r >= row):
                itemToPos[layout.takeAt(idx)] = QRect(r + 1, c, rs, cs)
            else:
                idx += 1

        for k in itemToPos.keys():
            r = itemToPos[k]
            layout.addItem(k, r.x(), r.y(), r.width(), r.height())

    def removeRow(self, layout, row):
        itemToPos = QMap()
        idx = 0
        while (idx < len(layout)):
            r, c, rs, cs = layout.getItemPosition(idx)
            if (r > row):
                itemToPos[layout.takeAt(idx)] = QRect(r - 1, c, rs, cs)
            else:
                idx += 1

        for k in itemToPos.keys():
            r = itemToPos[k]
            layout.addItem(k, r.x(), r.y(), r.width(), r.height())

    def propertyChanged(self, index):
        item = self.m_indexToItem[index]

        self.updateItem(item)

    def updateItem(self, item):
        property = self.m_itemToIndex[item].property()
        if (item.button):
            font = item.button.font()
            font.setUnderline(property.isModified())
            item.button.setFont(font)
            item.button.setText(property.propertyName())
            item.button.setToolTip(property.toolTip())
            item.button.setStatusTip(property.statusTip())
            item.button.setWhatsThis(property.whatsThis())
            item.button.setEnabled(property.isEnabled())

        if (item.label):
            font = item.label.font()
            font.setUnderline(property.isModified())
            item.label.setFont(font)
            item.label.setText(property.propertyName())
            item.label.setToolTip(property.toolTip())
            item.label.setStatusTip(property.statusTip())
            item.label.setWhatsThis(property.whatsThis())
            item.label.setEnabled(property.isEnabled())

        if (item.widgetLabel):
            font = item.widgetLabel.font()
            font.setUnderline(False)
            item.widgetLabel.setFont(font)
            item.widgetLabel.setText(property.valueText())
            item.widgetLabel.setToolTip(property.valueText())
            item.widgetLabel.setEnabled(property.isEnabled())

        if (item.widget):
            font = item.widget.font()
            font.setUnderline(False)
            item.widget.setFont(font)
            item.widget.setEnabled(property.isEnabled())
            item.widget.setToolTip(property.valueText())
Ejemplo n.º 58
0
class ObjectGroup(Layer):
    ##
    # Objects within an object group can either be drawn top down (sorted
    # by their y-coordinate) or by index (manual stacking order).
    #
    # The default is top down.
    ##
    class DrawOrder():
        UnknownOrder = -1
        TopDownOrder = 1
        IndexOrder = 2

    ##
    # Default constructor.
    ##
    def __init__(self, *args):
        self.mObjects = QList()
        self.mColor = QColor()

        l = len(args)
        if l==0:
            super().__init__(Layer.ObjectGroupType, QString(), 0, 0, 0, 0)
        elif l==5:
            ##
            # Constructor with some parameters.
            ##
            name, x, y, width, height = args

            super().__init__(Layer.ObjectGroupType, name, x, y, width, height)
        else:
            pass
        self.mDrawOrder = ObjectGroup.DrawOrder.IndexOrder

    ##
    # Destructor.
    ##
    def __del__(self):
        self.mObjects.clear()

    ##
    # Returns a pointer to the list of objects in this object group.
    ##
    def objects(self):
        return QList(self.mObjects)

    ##
    # Returns the number of objects in this object group.
    ##
    def objectCount(self):
        return self.mObjects.size()

    ##
    # Returns the object at the specified index.
    ##
    def objectAt(self, index):
        return self.mObjects.at(index)

    ##
    # Adds an object to this object group.
    ##
    def addObject(self, object):
        self.mObjects.append(object)
        object.setObjectGroup(self)
        if (self.mMap and object.id() == 0):
            object.setId(self.mMap.takeNextObjectId())

    ##
    # Inserts an object at the specified index. This is only used for undoing
    # the removal of an object at the moment, to make sure not to change the
    # saved order of the objects.
    ##
    def insertObject(self, index, object):
        self.mObjects.insert(index, object)
        object.setObjectGroup(self)
        if (self.mMap and object.id() == 0):
            object.setId(self.mMap.takeNextObjectId())

    ##
    # Removes an object from this object group. Ownership of the object is
    # transferred to the caller.
    #
    # @return the index at which the specified object was removed
    ##
    def removeObject(self, object):
        index = self.mObjects.indexOf(object)
        self.mObjects.removeAt(index)
        object.setObjectGroup(None)
        return index

    ##
    # Removes the object at the given index. Ownership of the object is
    # transferred to the caller.
    #
    # This is faster than removeObject when you've already got the index.
    #
    # @param index the index at which to remove an object
    ##
    def removeObjectAt(self, index):
        object = self.mObjects.takeAt(index)
        object.setObjectGroup(None)

    ##
    # Moves \a count objects starting at \a from to the index given by \a to.
    #
    # The \a to index may not lie within the range of objects that is
    # being moved.
    ##
    def moveObjects(self, _from, to, count):
        # It's an error when 'to' lies within the moving range of objects
        # Nothing to be done when 'to' is the start or the end of the range, or
        # when the number of objects to be moved is 0.
        if (to == _from or to == _from + count or count == 0):
            return
        movingObjects = self.mObjects[_from:_from+count]
        self.mObjects.erase(_from, _from + count)
        if (to > _from):
            to -= count
        for i in range(count):
            self.mObjects.insert(to + i, movingObjects[i])

    ##
    # Returns the bounding rect around all objects in this object group.
    ##
    def objectsBoundingRect(self):
        boundingRect = QRectF()
        for object in self.mObjects:
            boundingRect = boundingRect.united(object.bounds())
        return boundingRect

    ##
    # Returns whether this object group contains any objects.
    ##
    def isEmpty(self):
        return self.mObjects.isEmpty()

    ##
    # Computes and returns the set of tilesets used by this object group.
    ##
    def usedTilesets(self):
        tilesets = QSet()
        for object in self.mObjects:
            tile = object.cell().tile
            if tile:
                tilesets.insert(tile.sharedTileset())
        return tilesets

    ##
    # Returns whether any tile objects in this object group reference tiles
    # in the given tileset.
    ##
    def referencesTileset(self, tileset):
        for object in self.mObjects:
            tile = object.cell().tile
            if (tile and tile.tileset() == tileset):
                return True

        return False

    ##
    # Replaces all references to tiles from \a oldTileset with tiles from
    # \a newTileset.
    ##
    def replaceReferencesToTileset(self, oldTileset, newTileset):
        for object in self.mObjects:
            tile = object.cell().tile
            if (tile and tile.tileset() == oldTileset):
                cell = object.cell()
                cell.tile = Tileset.tileAt(tile.id())
                object.setCell(cell)

    ##
    # Offsets all objects within the group by the \a offset given in pixel
    # coordinates, and optionally wraps them. The object's center must be
    # within \a bounds, and wrapping occurs if the displaced center is out of
    # the bounds.
    #
    # \sa TileLayer.offset()
    ##
    def offsetObjects(self, offset, bounds, wrapX, wrapY):
        for object in self.mObjects:
            objectCenter = object.bounds().center()
            if (not bounds.contains(objectCenter)):
                continue
            newCenter = QPointF(objectCenter + offset)
            if (wrapX and bounds.width() > 0):
                nx = math.fmod(newCenter.x() - bounds.left(), bounds.width())
                if nx < 0:
                    x = bounds.width() + nx
                else:
                    x = nx
                newCenter.setX(bounds.left() + x)

            if (wrapY and bounds.height() > 0):
                ny = math.fmod(newCenter.y() - bounds.top(), bounds.height())
                if ny < 0:
                    x = bounds.height() + ny
                else:
                    x = ny
                newCenter.setY(bounds.top() + x)

            object.setPosition(object.position() + (newCenter - objectCenter))

    def canMergeWith(self, other):
        return other.isObjectGroup()

    def mergedWith(self, other):
        og = other
        merged = self.clone()
        for mapObject in og.objects():
            merged.addObject(mapObject.clone())
        return merged

    ##
    # Returns the color of the object group, or an invalid color if no color
    # is set.
    ##
    def color(self):
        return self.mColor

    ##
    # Sets the display color of the object group.
    ##
    def setColor(self, color):
        if type(color) != QColor:
            color = QColor(color)
        self.mColor = color

    ##
    # Returns the draw order for the objects in this group.
    #
    # \sa ObjectGroup.DrawOrder
    ##
    def drawOrder(self):
        return self.mDrawOrder

    ##
    # Sets the draw order for the objects in this group.
    #
    # \sa ObjectGroup.DrawOrder
    ##
    def setDrawOrder(self, drawOrder):
        self.mDrawOrder = drawOrder

    ##
    # Returns a duplicate of this ObjectGroup.
    #
    # \sa Layer.clone()
    ##
    def clone(self):
        return self.initializeClone(ObjectGroup(self.mName, self.mX, self.mY, self.mWidth, self.mHeight))

    def initializeClone(self, clone):
        super().initializeClone(clone)
        for object in self.mObjects:
            clone.addObject(object.clone())
        clone.setColor(self.mColor)
        clone.setDrawOrder(self.mDrawOrder)
        return clone