summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorColin Noga <Tempus@Spectrum-Song.local>2011-11-08 03:43:59 -0600
committerColin Noga <Tempus@Spectrum-Song.local>2011-11-08 03:43:59 -0600
commitd7984abd4fd0efda7684bc5a48750483ed9261a8 (patch)
tree3d8af1ddaeb9aff3887e9ce47bde3e2d3c03a352 /src
parentf54d177364dbfacce45ec6768462bc81e837a274 (diff)
downloadkoopatlas-d7984abd4fd0efda7684bc5a48750483ed9261a8.tar.gz
koopatlas-d7984abd4fd0efda7684bc5a48750483ed9261a8.zip
Added a bunch more stuff for the eventual Gourped Object Selector Widget. Its a complicated bugger, especially when I am in no position to test
Diffstat (limited to 'src')
-rw-r--r--src/tileset.py434
-rw-r--r--src/ui.py87
2 files changed, 452 insertions, 69 deletions
diff --git a/src/tileset.py b/src/tileset.py
index a193a7f..8d0c3d3 100644
--- a/src/tileset.py
+++ b/src/tileset.py
@@ -1,26 +1,59 @@
from common import *
-class KPObjectDefinition(object):
- def __init__(self):
- self.rows = []
-
+
+
+class KPTileObject(object):
+ def __init__(self, tilelist, height, width, wrapmode, icon):
+
+ # A list of lists of tile indices
+ self.tiles = tilelist
+
+ # Dimensions
+ self.height = height
+ self.width = width
+
+ # QPixmap of Rendered 1:1 Object
+ self.icon = icon
+
+ # Wrapmode
+ self.wrap = wrapmode
+
+ # --- Wrapmodes ---
+ # 'Repeat'
+ # 'Stretch Center'
+ # 'Stretch X'
+ # 'Stretch Y'
+ # 'Repeat Bottom'
+ # 'Repeat Top'
+ # 'Repeat Left'
+ # 'Repeat Right'
+ # 'Upward slope' (LL to UR)
+ # 'Downward slope' (UL to LR)
+
+ self.itemsize = QtCore.QSize(self.width * 24 + 4, self.height * 24 + 4)
+
+
+
def render(self, size):
+ '''Returns a tilemap of the object at a certain size as a list of lists.'''
+
+ if self.wrap > 7:
+ self._renderSlope(size)
+
+
+ # size is a tuple of (width, height)
+
buf = []
beforeRepeat = []
- inRepeat = []
+ inRepeat = self.tiles.copy()
afterRepeat = []
- foundRepeat = False
- for row in self.rows:
- if (row[0][0] & 2) != 0:
- inRepeat.append(row)
- foundRepeat = True
- else:
- if foundRepeat:
- afterRepeat.append(row)
- else:
- beforeRepeat.append(row)
+ if (self.wrap == 1) or (self.wrap == 3) or (self.wrap == 5):
+ beforeRepeat = inRepeat.pop(0)
+
+ if (self.wrap == 1) or (self.wrap == 3) or (self.wrap == 4):
+ afterRepeat = inRepeat.pop()
bC, iC, aC = len(beforeRepeat), len(inRepeat), len(afterRepeat)
@@ -44,19 +77,14 @@ class KPObjectDefinition(object):
buf = [-1 for i in xrange(width)]
beforeRepeat = []
- inRepeat = []
+ inRepeat = row
afterRepeat = []
- foundRepeat = False
- for tile in row:
- if (tile[0] & 1) != 0:
- inRepeat.append(tile[1])
- foundRepeat = True
- else:
- if foundRepeat:
- afterRepeat.append(tile[1])
- else:
- beforeRepeat.append(tile[1])
+ if (self.wrap == 1) or (self.wrap == 2) or (self.wrap == 6):
+ beforeRepeat = inRepeat.pop(0)
+
+ if (self.wrap == 1) or (self.wrap == 2) or (self.wrap == 7):
+ afterRepeat = inRepeat.pop()
bC, iC, aC = len(beforeRepeat), len(inRepeat), len(afterRepeat)
@@ -77,14 +105,170 @@ class KPObjectDefinition(object):
return buf
+ def _renderSlope(self, size):
+ # Slopes are annoying
+
+ buf = []
+ w = xrange(size[0])
+ h = xrange(size[1])
+
+ # Koopuzzle really only does slopes that are two blocks tall.
+ # Suck it, multi height slopes.
+
+ mainblock = self.tiles[0]
+ subblock = self.tiles[1]
+
+ if self.wrap == 8: # Upward (LL to UR)
+
+ # List Comprehension to make a palette
+ buf = [[-1 for x in w] for x in h]
+
+ offset = 0
+
+ # Paint the mainblock
+ for row in h:
+ for tile in w:
+ if (tile == ((size[1] - row - 1) * self.width) + offset):
+ buf[size[1] - row - 2][tile] = mainblock[offset]
+ offset += 1
+ offset = 0
+
+ # Paint the subblock
+ for row in h:
+ for tile in w:
+ if (tile == ((size[1] - row) * self.width) + offset):
+ buf[size[1] - row - 1][tile] = mainblock[offset]
+ offset += 1
+ offset = 0
+
+
+ elif self.wrap == 9: # Downward (UL to LR)
+
+ # List Comprehension to make a palette
+ buf = [[-1 for x in w] for x in h]
+
+ offset = 0
+
+ # Paint the mainblock
+ for row in h:
+ for tile in w:
+ if (tile == (row * self.width) + offset):
+ buf[row][tile] = mainblock[offset]
+ offset += 1
+ offset = 0
+
+ # Paint the subblock
+ for row in h:
+ for tile in w:
+ if (tile == ((row - 1) * self.width) + offset):
+ buf[row][tile] = mainblock[offset]
+ offset += 1
+ offset = 0
+
+
+
+
+
+
+class KPGroupModel(QtCore.QAbstractListModel):
+ """Model containing all the grouped objects in a tileset"""
+
+ def __init__(self, groupItem):
+ self.container = groupItem
+
+ QtCore.QAbstractListModel.__init__(self)
+
+
+ def rowCount(self, parent=None):
+ return self.container.objectCount()
+
+
+ def data(self, index, role=QtCore.Qt.DisplayRole):
+ # Should return the contents of a row when asked for the index
+
+ if not index.isValid(): return None
+ n = index.row()
+
+ if n < 0: return None
+ if n >= self.container.objectCount(): return None
+
+ item = self.container.getItem(n)
+
+ if role == QtCore.Qt.DecorationRole:
+ if isInstance(item, KPTileObject):
+ return item.icon
+
+ if role == QtCore.Qt.BackgroundRole:
+ return QtGui.qApp.palette().base()
+
+ if role == QtCore.Qt.UserRole:
+ if isInstance(item, KPTileObject):
+ return item.itemsize
+
+ if role == QtCore.Qt.DisplayRole:
+ if isInstance(item, KPGroupItem):
+ return item.name
+
+ if role == QtCore.Qt.TextAlignmentRole:
+ if isInstance(item, KPGroupItem):
+ return item.alignment
+
+
+ return None
+
+
+
+class KPGroupItem(object):
+ """Hierarchal object for making a 2D hierarchy recursively using classes"""
+
+ def __init__(self, name):
+
+ self.objects = []
+ self.groups = []
+ self.startIndex = 0
+ self.endIndex = 0
+
+ self.name = name
+ self.alignment = QtCore.Qt.AlignCenter
+
+
+ def objectCount(self):
+ ''' Retrieves the total number of items in this group and all it's children '''
+
+ objCount = 0
+
+ objCount += len(self.objects)
+ objCount += len(self.groups)
+ for group in groups:
+ objCount += group.objectCount()
+
+ return objCount
+
+
+ def getItem(index):
+ ''' Retrieves an item of a specific index. The index is already checked for validity '''
+
+ if index == self.startIndex:
+ return self
+
+ if (index < self.startIndex + len(self.objects)):
+ return self.objects[index - self.startIndex - 1]
+
+ else:
+
+ for group in groups:
+
+ if (group.startIndex < index) and (index < group.endIndex):
+ return group.getItem(index)
+
class KPTileset(object):
- def __init__(self, imageBuffer, objectBuffer, groupBuffer):
- '''A Koopatlas Tileset class. To initialize, pass it an image buffer, an
- object buffer, and a group buffer from a Koopatlas tileset file.
+ def __init__(self, imageBuffer, objectBuffer, objectMetadata, groupBuffer):
+ '''A Koopatlas Tileset class. To initialize, pass it image data,
+ object data, and group data as read from a Koopatlas tileset file.
tiles -> has a list of all 512 tiles.
objects -> has a list of objects in the tileset
@@ -94,46 +278,58 @@ class KPTileset(object):
Methods of Note:
- overrideTile(Tile Index, QPixmap) # Takes a 24x24 QPixmap and a tile index
+ getTile(TileIndex)
+ # Returns a tile image as a QPixmap
+
+ getObject(ObjectIndex)
+ # Returns a KPTileObject
+ getObjectIcon(ObjectIndex)
+ getObjectIcon(KPTileObject)
+ # Returns a QPixmap for the Object
+ getObjectIcon(ObjectIndex, (width, height))
+ getObjectIcon(KPTileObject, (width, height))
+ # Returns a render map for the Object at the given size
+ overrideTile(Tile Index, QPixmap)
+ # Takes a 24x24 QPixmap and a tile index
'''
self.tiles = []
self.objects = []
- self.groupModel = QtGui.QTreeModel
+ self.groupItem = KPGroupItem("All Groups")
self.processImage(imageBuffer)
- self.processObjects(objectBuffer)
+ self.processObjects(objectBuffer, objectMetadata)
self.processGroup(groupBuffer)
-
+ self.groupModel = KPGroupModel(self.groupItem)
def processImage(self, imageBuffer):
'''Takes an imageBuffer from a Koopatlas Tileset and converts it into 24x24
tiles, which get put into KPTileset.tiles.'''
- dest = self.RGB4A3Decode(Image)
-
- self.tileImage = QtGui.QPixmap.fromImage(dest)
-
- # Makes us some nice Tiles!
- Xoffset = 4
- Yoffset = 4
- for i in range(512):
- self.tiles.append(self.tileImage.copy(Xoffset,Yoffset,24,24))
- Xoffset += 32
- if Xoffset >= 1024:
- Xoffset = 4
- Yoffset += 32
+ dest = self.RGB4A3Decode(Image)
+
+ self.tileImage = QtGui.QPixmap.fromImage(dest)
+
+ # Makes us some nice Tiles!
+ Xoffset = 4
+ Yoffset = 4
+ for i in range(512):
+ self.tiles.append(self.tileImage.copy(Xoffset,Yoffset,24,24))
+ Xoffset += 32
+ if Xoffset >= 1024:
+ Xoffset = 4
+ Yoffset += 32
- def RGB4A3Decode(tex):
+ def RGB4A3Decode (tex):
dest = QtGui.QImage(1024,512,QtGui.QImage.Format_ARGB32)
dest.fill(QtCore.Qt.transparent)
@@ -168,44 +364,141 @@ class KPTileset(object):
return dest
- def processObjects(self, objectBuffer):
+ def processObjects(self, objstrings, metadata):
+ '''Takes the object files from a Koopatlas Tileset and converts it into
+ KPTileObject classes, which get put into KPTileset.objects.'''
+
+ # Load Objects
+
+ meta = []
+ for i in xrange(len(metadata)/5):
+ meta.append(struct.unpack_from('>H3B', metadata, i * 5))
+
+ tilelist = []
+ rowlist = []
+
+ for entry in meta:
+ offset = entry[0]
+ row = 0
+
+ tex = QtGui.QPixmap(entry[2] * 24, entry[1] * 24)
+ tex.fill(QtCore.Qt.transparent)
+ painter = QtGui.QPainter(tex)
+
+
+ for tilesA in xrange(entry[2]):
+ for tilesB in xrange(entry[1]):
+ untile = struct.unpack_from('>h', objstrings, offset)[0]
+
+ if untile != -1:
+ painter.drawPixmap(tilesA*24, tilesB*24, self.tiles[untile])
+
+ rowlist.append(untile)
+ offset += 2
+
+ tilelist.append(rowlist)
+ rowlist = []
+
+ painter.end()
+
+
+ self.objects.append(KPTileObject(tilelist, entry[2], entry[1], entry[3], tex))
+
+ tilelist = []
def processGroup(self, groupBuffer):
- grovyle = cPickle.loads(group)
- makeTree(grovyle, treecko)
+ grovyle = cPickle.loads(group)
+ self.makeTree(grovyle, self.groupItem)
+
+
def makeTree(self, grovyle, treecko):
-
- for razorLeaf in grovyle:
+
+ indexNum = treecko.startIndex
+
+ for razorLeaf in grovyle:
+
+ if (type(razorLeaf) is str) and (razorLeaf[:6] == "Object"):
+
+ objnum = int(razorLeaf[7:])
+ obj = self.objects(objnum)
- if (type(razorLeaf) is str) and (razorLeaf[:6] == "Object"):
+ treecko.objects.append(obj)
- pix = QtGui.QPixmap(24, 24)
- pix.fill(QtCore.Qt.transparent)
- painter = QtGui.QPainter(pix)
- painter.drawPixmap(0, 0, pix)
- painter.end()
+ else:
+
+ a = KPGroupItem(razorLeaf[0])
+ treecko.groups.append(a)
+
+ makeTree(razorLeaf[1], a)
+
+ treecko.endIndex = treecko.startIndex + treecko.objCount()
- a = QtGui.QTreeWidgetItem(treecko)
- a.setText(0, razorLeaf)
- a.setFlags(QtCore.Qt.ItemFlags(0x25))
- a.setIcon(1, QtGui.QIcon(pix))
- else:
- a = QtGui.QTreeWidgetItem(treecko)
- a.setText(0, razorLeaf[0])
- a.setFlags(QtCore.Qt.ItemFlags(0x2F))
- a.setChildIndicatorPolicy(QtGui.QTreeWidgetItem.ShowIndicator)
- a.setExpanded(True)
+ def getTile(index):
+ '''Takes a tile index and returns a tile image as a QPixmap, or -1 if failed.'''
+ if index > 511:
+ return false
+
+ if index < 0:
+ return false
+
+ return self.tiles[index]
+
+
+ def getObject(index):
+ '''Takes an object index and returns a KPTileObject, or false if failed.'''
+
+ if index < 0:
+ return false
+
+ return self.tiles[index]
+
+
+ def getObjectIcon(index):
+ '''Takes an object index or a KPTileObject and returns a QPixmap for the
+ object, or false if failed.'''
+
+ if isInstance(index, KPTileObject):
+ return index.icon
+
+ if isInstance(index, int):
+ try:
+ return self.objects[index].icon
+ except:
+ return false
+
+ return false
+
+
+ def getObjectRender(index, size):
+ '''Takes an object index or a KPTileObject and returns a render map for the
+ object, or false if failed.'''
+
+ if isInstance(index, KPTileObject):
+ return index.render(size)
+
+ if isInstance(index, int):
+ try:
+ return self.objects[index].render(size)
+ except:
+ return false
+
+ return false
+
+
+ def getMode():
+ '''Returns the Group Model'''
+
+ return self.groupModel
- makeTree(razorLeaf[1], a)
def overrideTile(index, pixmap):
@@ -214,6 +507,9 @@ class KPTileset(object):
if index > 511:
return false
+ if index < 0:
+ return false
+
if not isInstance(pixmap, QtGui.QPixmap):
return false
diff --git a/src/ui.py b/src/ui.py
index 57c396b..3949bdc 100644
--- a/src/ui.py
+++ b/src/ui.py
@@ -53,6 +53,87 @@ class KPLayerList(QtGui.QWidget):
KP.map.moveLayer(index, index + 2)
+
+
+class KPObjectSelector(QtGui.QListView):
+
+ def __init__(self):
+ """Initialises the widget. Remember to call setModel() on it with a KPGroupModel
+ whenever the layer changes."""
+
+ QtGui.QListView.__init__(self)
+ self.setFlow(QtGui.QListView.LeftToRight)
+ self.setLayoutMode(QtGui.QListView.SinglePass)
+ self.setMovement(QtGui.QListView.Static)
+ self.setResizeMode(QtGui.QListView.Adjust)
+ self.setWrapping(True)
+
+ # self.setItemDelegate(KPObjectSelector.ObjectItemDelegate())
+
+ self.clicked.connect(self.HandleObjReplace)
+
+
+ def toggleTopLevel(self, id):
+ """Changes the top level group in the list view."""
+
+ # Not quite sure how to implement this yet. Basically, the model is hierarchal,
+ # and it'll return items from whatever the top level KPGroupItem is. But removing
+ # and adding stuff isn't possible, since I need to retain my recursive object.
+ #
+ # So here's the structure. Above this QListView, there will be a QComboBox.
+ #
+ # The QComboBox will list all groups in my hierarchy.
+ # Selecting a group will cause the QListView to only show items for those rows,
+ # even though all rows are retained.
+ #
+ # It's kind of like a custom QSortFilterProxyModel. Maybe I should subclass that.
+
+
+ self.setCurrentIndex(self.model().index(sel, 0, QtCore.QModelIndex()))
+
+ @QtCore.pyqtSlot(QtCore.QModelIndex, QtCore.QModelIndex)
+ def currentChanged(self, current, previous):
+ """Throws a signal when the selected object changed"""
+ self.ObjChanged.emit(current.row())
+
+ def HandleObjReplace(self, index):
+ """Throws a signal when the selected object is used as a replacement"""
+ if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.AltModifier:
+ self.ObjReplace.emit(index.row())
+
+ ObjChanged = QtCore.pyqtSignal(int)
+ ObjReplace = QtCore.pyqtSignal(int)
+
+
+ class KPGroupItemDelegate(QtGui.QAbstractItemDelegate):
+ """Handles tileset groups, objects and their rendering"""
+
+ def __init__(self):
+ """Initialises the delegate"""
+ QtGui.QAbstractItemDelegate.__init__(self)
+
+ def paint(self, painter, option, index):
+ """Paints an object"""
+ if option.state & QtGui.QStyle.State_Selected:
+ painter.fillRect(option.rect, option.palette.highlight())
+
+ p = index.model().data(index, QtCore.Qt.DecorationRole)
+ painter.drawPixmap(option.rect.x()+2, option.rect.y()+2, p)
+ #painter.drawText(option.rect, str(index.row()))
+
+ def sizeHint(self, option, index):
+ """Returns the size for the object"""
+ p = index.model().data(index, QtCore.Qt.UserRole)
+ return p
+
+
+
+
+
+
+
+
+
class KPMainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
@@ -77,6 +158,12 @@ class KPMainWindow(QtGui.QMainWindow):
self.layerListDock = QtGui.QDockWidget('Layers')
self.layerListDock.setWidget(self.layerList)
+ self.objectSelector = KPObjectSelector()
+ # self.objectSelector.setModel(SomeTileset.getModel())
+ self.objectSelectorDock = QtGui.QDockWidget('Layers')
+ self.objectSelectorDock.setWidget(self.layerList)
+
+
self.addDockWidget(Qt.RightDockWidgetArea, self.layerListDock)