More work on NBT editor
Adding to Compounds is possible, showing a tag type menu. Adding to an empty list uses the menu too setModel changed to setRootTag tries to keep tree expand state when model changes (e.g. due to revision change) chunk editor treeView->nbtEditor
This commit is contained in:
parent
1361389187
commit
817c3a2624
@ -35,6 +35,8 @@ class ChunkTool(EditorTool):
|
|||||||
self.toolWidget.cxSpinBox.valueChanged.connect(self.chunkPositionDidChange)
|
self.toolWidget.cxSpinBox.valueChanged.connect(self.chunkPositionDidChange)
|
||||||
self.toolWidget.czSpinBox.valueChanged.connect(self.chunkPositionDidChange)
|
self.toolWidget.czSpinBox.valueChanged.connect(self.chunkPositionDidChange)
|
||||||
|
|
||||||
|
self.toolWidget.nbtEditor.editorSession = self.editorSession
|
||||||
|
|
||||||
self.currentChunk = None
|
self.currentChunk = None
|
||||||
self.selectionNode = None
|
self.selectionNode = None
|
||||||
self.overlayNode = scenegraph.Node()
|
self.overlayNode = scenegraph.Node()
|
||||||
@ -126,15 +128,10 @@ class ChunkTool(EditorTool):
|
|||||||
def updateNBTView(self):
|
def updateNBTView(self):
|
||||||
chunk = self.currentChunk
|
chunk = self.currentChunk
|
||||||
if chunk is None:
|
if chunk is None:
|
||||||
self.toolWidget.nbtEditor.setModel(None)
|
self.toolWidget.nbtEditor.setRootTag(None)
|
||||||
return
|
return
|
||||||
|
|
||||||
model = NBTTreeModel(chunk.rootTag)
|
self.toolWidget.nbtEditor.setRootTag(chunk.rootTag)
|
||||||
|
|
||||||
self.toolWidget.nbtEditor.setModel(model)
|
|
||||||
# self.toolWidget.nbtEditor.expandToDepth(0)
|
|
||||||
# self.toolWidget.nbtEditor.resizeColumnToContents(0)
|
|
||||||
# self.toolWidget.nbtEditor.resizeColumnToContents(1)
|
|
||||||
|
|
||||||
self.toolWidget.cxSpinBox.setValue(chunk.cx)
|
self.toolWidget.cxSpinBox.setValue(chunk.cx)
|
||||||
self.toolWidget.czSpinBox.setValue(chunk.cz)
|
self.toolWidget.czSpinBox.setValue(chunk.cz)
|
||||||
|
@ -47,6 +47,7 @@ class EntityTool(EditorTool):
|
|||||||
def createToolWidget(self):
|
def createToolWidget(self):
|
||||||
self.toolWidget = load_ui("editortools/select_entity.ui")
|
self.toolWidget = load_ui("editortools/select_entity.ui")
|
||||||
self.toolWidget.entityListBox.currentIndexChanged.connect(self.setSelectedEntity)
|
self.toolWidget.entityListBox.currentIndexChanged.connect(self.setSelectedEntity)
|
||||||
|
self.toolWidget.nbtEditor.editorSession = self.editorSession
|
||||||
|
|
||||||
def mousePress(self, event):
|
def mousePress(self, event):
|
||||||
command = SelectEntityCommand(self, event.ray)
|
command = SelectEntityCommand(self, event.ray)
|
||||||
@ -69,10 +70,9 @@ class EntityTool(EditorTool):
|
|||||||
|
|
||||||
def setSelectedEntity(self, index):
|
def setSelectedEntity(self, index):
|
||||||
if len(self.selectedEntities):
|
if len(self.selectedEntities):
|
||||||
model = NBTTreeModel(self.selectedEntities[index].raw_tag())
|
self.toolWidget.nbtEditor.setRootTag(self.selectedEntities[index].raw_tag())
|
||||||
self.toolWidget.treeView.setModel(model)
|
|
||||||
else:
|
else:
|
||||||
self.toolWidget.treeView.setModel(None)
|
self.toolWidget.nbtEditor.setRootTag(None)
|
||||||
|
|
||||||
|
|
||||||
def entitiesOnRay(dimension, ray, rayWidth=2.0, maxDistance = 1000):
|
def entitiesOnRay(dimension, ray, rayWidth=2.0, maxDistance = 1000):
|
||||||
|
@ -22,9 +22,6 @@ log = logging.getLogger(__name__)
|
|||||||
class PlayerPropertyChangeCommand(SimpleRevisionCommand):
|
class PlayerPropertyChangeCommand(SimpleRevisionCommand):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class NBTDataChangeCommand(SimpleRevisionCommand):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class PlayerPanel(QtGui.QWidget):
|
class PlayerPanel(QtGui.QWidget):
|
||||||
def __init__(self, editorSession, *args, **kwargs):
|
def __init__(self, editorSession, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -72,6 +69,9 @@ class PlayerPanel(QtGui.QWidget):
|
|||||||
self.editorSession.revisionChanged.connect(self.revisionDidChange)
|
self.editorSession.revisionChanged.connect(self.revisionDidChange)
|
||||||
self.initPropertiesWidget()
|
self.initPropertiesWidget()
|
||||||
|
|
||||||
|
self.nbtEditor.editorSession = self.editorSession
|
||||||
|
self.nbtEditor.editMade.connect(self.nbtEditWasMade)
|
||||||
|
|
||||||
centerWidgetInScreen(self)
|
centerWidgetInScreen(self)
|
||||||
|
|
||||||
def initPropertiesWidget(self):
|
def initPropertiesWidget(self):
|
||||||
@ -109,34 +109,16 @@ class PlayerPanel(QtGui.QWidget):
|
|||||||
model.propertyChanged.connect(self.propertyDidChange)
|
model.propertyChanged.connect(self.propertyDidChange)
|
||||||
|
|
||||||
def updateNBTTree(self):
|
def updateNBTTree(self):
|
||||||
model = NBTTreeModel(self.selectedPlayer.rootTag)
|
self.nbtEditor.undoCommandPrefixText = ("Player %s: " % self.selectedUUID) if self.selectedUUID else "Single-player: "
|
||||||
model.dataChanged.connect(self.nbtDataDidChange)
|
self.nbtEditor.setRootTag(self.selectedPlayer.rootTag)
|
||||||
self.nbtEditor.setModel(model)
|
|
||||||
|
def nbtEditWasMade(self):
|
||||||
|
self.selectedPlayer.dirty = True
|
||||||
|
|
||||||
def revisionDidChange(self):
|
def revisionDidChange(self):
|
||||||
self.initPropertiesWidget()
|
self.initPropertiesWidget()
|
||||||
self.updateNBTTree()
|
self.updateNBTTree()
|
||||||
|
|
||||||
def nbtDataDidChange(self, index):
|
|
||||||
model = self.nbtEditor.model
|
|
||||||
parent = model.parent(index)
|
|
||||||
item = model.getItem(index)
|
|
||||||
if parent is not None and parent.isList:
|
|
||||||
name = str(parent.tag.index(item.tag))
|
|
||||||
else:
|
|
||||||
name = item.tag.name
|
|
||||||
|
|
||||||
if self.selectedUUID != "":
|
|
||||||
text = "Change player %s NBT tag %s" % (self.selectedUUID, name)
|
|
||||||
else:
|
|
||||||
text = "Change single-player NBT tag %s" % name
|
|
||||||
|
|
||||||
command = NBTDataChangeCommand(self.editorSession, text)
|
|
||||||
with command.begin():
|
|
||||||
self.selectedPlayer.dirty = True
|
|
||||||
self.editorSession.worldEditor.syncToDisk()
|
|
||||||
self.editorSession.pushCommand(command)
|
|
||||||
|
|
||||||
def propertyDidChange(self, name, value):
|
def propertyDidChange(self, name, value):
|
||||||
if self.selectedUUID != "":
|
if self.selectedUUID != "":
|
||||||
text = "Change player %s property %s" % (self.selectedUUID, name)
|
text = "Change player %s property %s" % (self.selectedUUID, name)
|
||||||
@ -169,7 +151,7 @@ class PlayerPanel(QtGui.QWidget):
|
|||||||
self.selectedUUID = UUID
|
self.selectedUUID = UUID
|
||||||
except PlayerNotFound:
|
except PlayerNotFound:
|
||||||
log.info("PlayerPanel: player %s not found!", UUID)
|
log.info("PlayerPanel: player %s not found!", UUID)
|
||||||
self.nbtEditor.setModel(None)
|
self.nbtEditor.setRootTag(None)
|
||||||
else:
|
else:
|
||||||
self.updateNBTTree()
|
self.updateNBTTree()
|
||||||
|
|
||||||
|
@ -29,17 +29,25 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeView" name="treeView">
|
<widget class="NBTEditorWidget" name="nbtEditor" native="true">
|
||||||
<property name="alternatingRowColors">
|
<property name="sizePolicy">
|
||||||
<bool>true</bool>
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
</property>
|
<horstretch>0</horstretch>
|
||||||
<property name="sortingEnabled">
|
<verstretch>1</verstretch>
|
||||||
<bool>true</bool>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>NBTEditorWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>nbteditorwidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -28,6 +28,7 @@ _iconTypes = [
|
|||||||
"array.png", # 12 - shortarray
|
"array.png", # 12 - shortarray
|
||||||
]
|
]
|
||||||
|
|
||||||
|
NBTPathRole = QtCore.Qt.UserRole + 1
|
||||||
|
|
||||||
def NBTIcon(type):
|
def NBTIcon(type):
|
||||||
icon = _nbtIcons.get(type)
|
icon = _nbtIcons.get(type)
|
||||||
@ -98,15 +99,21 @@ class NBTTreeCompound(object):
|
|||||||
return "%s items" % len(tag)
|
return "%s items" % len(tag)
|
||||||
return summary
|
return summary
|
||||||
|
|
||||||
def insertChildren(self, position, count):
|
def insertChildren(self, position, count, tagID):
|
||||||
if position < 0 or position > len(self.childItems):
|
if position < 0 or position > len(self.childItems):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for row in range(count):
|
for row in range(count):
|
||||||
data = nbt.TAG_Byte()
|
name = "Unnamed"
|
||||||
self.tag.insert(position + row, data)
|
i = 0
|
||||||
|
while name in self.tag:
|
||||||
|
i += 1
|
||||||
|
name = "Unnamed %d" % i
|
||||||
|
|
||||||
item = NBTTreeItem(data, self)
|
tag = nbt.tag_classes[tagID]()
|
||||||
|
self.tag[name] = tag
|
||||||
|
|
||||||
|
item = NBTTreeItem(tag, self)
|
||||||
self.childItems.insert(position + row, item)
|
self.childItems.insert(position + row, item)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -119,13 +126,23 @@ class NBTTreeCompound(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
for row in range(count):
|
for row in range(count):
|
||||||
self.childItems.pop(position)
|
name = self.childItems.pop(position).tag.name
|
||||||
|
del self.tag[name]
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def setValue(self, value):
|
def setValue(self, value):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def nbtPath(self, child=None):
|
||||||
|
if self.parentItem is None:
|
||||||
|
path = []
|
||||||
|
else:
|
||||||
|
path = self.parentItem.nbtPath(self)
|
||||||
|
if child:
|
||||||
|
path.append(child.tag.name)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
class NBTTreeList(object):
|
class NBTTreeList(object):
|
||||||
isCompound = False
|
isCompound = False
|
||||||
@ -163,14 +180,14 @@ class NBTTreeList(object):
|
|||||||
|
|
||||||
return ", ".join((fmt % i.value) for i in self.tag)
|
return ", ".join((fmt % i.value) for i in self.tag)
|
||||||
|
|
||||||
def insertChildren(self, position, count):
|
def insertChildren(self, position, count, tagID):
|
||||||
if position < 0 or position > len(self.childItems):
|
if position < 0 or position > len(self.childItems):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
for row in range(count):
|
for row in range(count):
|
||||||
data = nbt.tag_classes[self.tag.list_type or nbt.ID_BYTE]()
|
tag = nbt.tag_classes[self.tag.list_type or tagID]()
|
||||||
self.tag.insert(position + row, data)
|
self.tag.insert(position + row, tag)
|
||||||
item = NBTTreeItem(data, self)
|
item = NBTTreeItem(tag, self)
|
||||||
self.childItems.insert(position + row, item)
|
self.childItems.insert(position + row, item)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -184,12 +201,23 @@ class NBTTreeList(object):
|
|||||||
|
|
||||||
for row in range(count):
|
for row in range(count):
|
||||||
self.childItems.pop(position)
|
self.childItems.pop(position)
|
||||||
|
self.tag.pop(position)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def setValue(self, value):
|
def setValue(self, value):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def nbtPath(self, child=None):
|
||||||
|
if self.parentItem is None:
|
||||||
|
path = []
|
||||||
|
else:
|
||||||
|
path = self.parentItem.nbtPath(self)
|
||||||
|
if child:
|
||||||
|
row = self.childItems.index(child)
|
||||||
|
path.append(row)
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
class NBTTreeItem(object):
|
class NBTTreeItem(object):
|
||||||
isCompound = False
|
isCompound = False
|
||||||
@ -229,9 +257,15 @@ class NBTTreeItem(object):
|
|||||||
self.tag.value = value
|
self.tag.value = value
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def nbtPath(self):
|
||||||
|
if self.parentItem is None:
|
||||||
|
return []
|
||||||
|
return self.parentItem.nbtPath(self)
|
||||||
|
|
||||||
class NBTTreeModel(QtCore.QAbstractItemModel):
|
class NBTTreeModel(QtCore.QAbstractItemModel):
|
||||||
def __init__(self, rootTag, parent=None):
|
def __init__(self, rootTag, parent=None):
|
||||||
super(NBTTreeModel, self).__init__(parent)
|
super(NBTTreeModel, self).__init__(parent)
|
||||||
|
self._internalPointers = {}
|
||||||
|
|
||||||
self.rootItem = MakeNBTTreeItem(rootTag, None)
|
self.rootItem = MakeNBTTreeItem(rootTag, None)
|
||||||
self.rootTag = rootTag
|
self.rootTag = rootTag
|
||||||
@ -245,20 +279,13 @@ class NBTTreeModel(QtCore.QAbstractItemModel):
|
|||||||
def tagID(self, index):
|
def tagID(self, index):
|
||||||
return self.getItem(index).tag.tagID
|
return self.getItem(index).tag.tagID
|
||||||
|
|
||||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
# --- Data ---
|
||||||
item = self.getItem(index)
|
|
||||||
column = index.column()
|
|
||||||
|
|
||||||
if role == QtCore.Qt.DecorationRole:
|
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
||||||
if column == 0:
|
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
|
||||||
return NBTIcon(item.tag.tagID)
|
return ("Name", "Value", "", "")[section]
|
||||||
if column == 2:
|
|
||||||
return self.addIcon if item.isList or item.isCompound else None
|
|
||||||
if column == 3:
|
|
||||||
return self.removeIcon
|
|
||||||
|
|
||||||
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
|
return None
|
||||||
return item.data(column)
|
|
||||||
|
|
||||||
def flags(self, index):
|
def flags(self, index):
|
||||||
flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
|
flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
|
||||||
@ -269,55 +296,85 @@ class NBTTreeModel(QtCore.QAbstractItemModel):
|
|||||||
flags |= QtCore.Qt.ItemIsEditable
|
flags |= QtCore.Qt.ItemIsEditable
|
||||||
return flags
|
return flags
|
||||||
|
|
||||||
|
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||||
|
item = self.getItem(index)
|
||||||
|
column = index.column()
|
||||||
|
|
||||||
|
if role == QtCore.Qt.DecorationRole:
|
||||||
|
if column == 0:
|
||||||
|
return NBTIcon(item.tag.tagID)
|
||||||
|
if column == 2:
|
||||||
|
return self.addIcon if item.isList or item.isCompound else None
|
||||||
|
if column == 3:
|
||||||
|
return self.removeIcon if item is not self.rootItem else None
|
||||||
|
|
||||||
|
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
|
||||||
|
return item.data(column)
|
||||||
|
|
||||||
|
if role == NBTPathRole:
|
||||||
|
return item.nbtPath()
|
||||||
|
|
||||||
|
# --- Structure ---
|
||||||
|
|
||||||
def getItem(self, index):
|
def getItem(self, index):
|
||||||
if index.isValid():
|
if index.isValid():
|
||||||
item = index.internalPointer()
|
item = self._internalPointers[index.internalId()]
|
||||||
if item:
|
if item:
|
||||||
return item
|
return item
|
||||||
else:
|
else:
|
||||||
return self.rootItem
|
return None
|
||||||
|
|
||||||
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||||
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
|
if not parent.isValid():
|
||||||
return ("Name", "Value", "", "")[section]
|
return 1
|
||||||
|
parentItem = self.getItem(parent)
|
||||||
|
|
||||||
return None
|
return parentItem.childCount()
|
||||||
|
|
||||||
|
def createIndex(self, row, column, item):
|
||||||
|
self._internalPointers[id(item)] = item
|
||||||
|
return super(NBTTreeModel, self).createIndex(row, column, id(item))
|
||||||
|
|
||||||
def index(self, row, column, parent=QtCore.QModelIndex()):
|
def index(self, row, column, parent=QtCore.QModelIndex()):
|
||||||
if not parent.isValid():
|
if not parent.isValid():
|
||||||
|
assert row == 0
|
||||||
return self.createIndex(row, column, self.rootItem)
|
return self.createIndex(row, column, self.rootItem)
|
||||||
|
|
||||||
parentItem = self.getItem(parent)
|
parentItem = self.getItem(parent)
|
||||||
childItem = parentItem.child(row)
|
if parentItem is None:
|
||||||
if childItem:
|
|
||||||
return self.createIndex(row, column, childItem)
|
|
||||||
else:
|
|
||||||
return QtCore.QModelIndex()
|
return QtCore.QModelIndex()
|
||||||
|
|
||||||
def insertRow(self, position, parent=QtCore.QModelIndex()):
|
childItem = parentItem.child(row)
|
||||||
return self.insertRows(position, 1, parent)
|
if childItem is None:
|
||||||
|
return QtCore.QModelIndex()
|
||||||
|
|
||||||
def insertRows(self, row, count, parent=QtCore.QModelIndex()):
|
return self.createIndex(row, column, childItem)
|
||||||
parentItem = self.getItem(parent)
|
|
||||||
self.beginInsertRows(parent, row, row + count - 1)
|
|
||||||
success = parentItem.insertChildren(row, count)
|
|
||||||
self.endInsertRows()
|
|
||||||
|
|
||||||
return success
|
|
||||||
|
|
||||||
def parent(self, index):
|
def parent(self, index):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return QtCore.QModelIndex()
|
return QtCore.QModelIndex()
|
||||||
|
|
||||||
childItem = self.getItem(index)
|
item = self.getItem(index)
|
||||||
parentItem = childItem.parent()
|
parentItem = item.parent()
|
||||||
|
|
||||||
if parentItem is None:
|
if parentItem is None: # item is self.rootItem
|
||||||
return QtCore.QModelIndex()
|
return QtCore.QModelIndex()
|
||||||
|
|
||||||
return self.createIndex(parentItem.childNumber(), 0, parentItem)
|
return self.createIndex(parentItem.childNumber(), 0, parentItem)
|
||||||
|
|
||||||
|
# --- Editing ---
|
||||||
|
|
||||||
|
def insertRow(self, position, parent=QtCore.QModelIndex(), tagID=None):
|
||||||
|
return self.insertRows(position, 1, parent, tagID)
|
||||||
|
|
||||||
|
def insertRows(self, row, count, parent=QtCore.QModelIndex(), tagID=None):
|
||||||
|
parentItem = self.getItem(parent)
|
||||||
|
self.beginInsertRows(parent, row, row + count - 1)
|
||||||
|
success = parentItem.insertChildren(row, count, tagID)
|
||||||
|
self.endInsertRows()
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
def removeRow(self, position, parent=QtCore.QModelIndex()):
|
def removeRow(self, position, parent=QtCore.QModelIndex()):
|
||||||
self.removeRows(position, 1, parent)
|
self.removeRows(position, 1, parent)
|
||||||
|
|
||||||
@ -330,13 +387,6 @@ class NBTTreeModel(QtCore.QAbstractItemModel):
|
|||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
|
||||||
if not parent.isValid():
|
|
||||||
return 1
|
|
||||||
parentItem = self.getItem(parent)
|
|
||||||
|
|
||||||
return parentItem.childCount()
|
|
||||||
|
|
||||||
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
||||||
if role != QtCore.Qt.EditRole:
|
if role != QtCore.Qt.EditRole:
|
||||||
return False
|
return False
|
||||||
|
@ -6,17 +6,28 @@ import logging
|
|||||||
|
|
||||||
from PySide import QtGui, QtCore
|
from PySide import QtGui, QtCore
|
||||||
from PySide.QtCore import Qt
|
from PySide.QtCore import Qt
|
||||||
from mcedit2.util.resources import resourcePath
|
from mcedit2.command import SimpleRevisionCommand
|
||||||
|
|
||||||
from mcedit2.widgets.nbttree.nbttreemodel import NBTFilterProxyModel
|
from mcedit2.widgets.nbttree.nbttreemodel import NBTFilterProxyModel, NBTPathRole, NBTIcon, NBTTreeModel
|
||||||
from mcedit2.util.load_ui import registerCustomWidget
|
from mcedit2.util.load_ui import registerCustomWidget
|
||||||
from mcedit2.widgets.layout import Row, Column
|
from mcedit2.widgets.layout import Column
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NBTDataChangeCommand(SimpleRevisionCommand):
|
||||||
|
pass
|
||||||
|
|
||||||
@registerCustomWidget
|
@registerCustomWidget
|
||||||
class NBTEditorWidget(QtGui.QWidget):
|
class NBTEditorWidget(QtGui.QWidget):
|
||||||
|
undoCommandPrefixText = ""
|
||||||
|
editorSession = NotImplemented
|
||||||
|
proxyModel = None
|
||||||
|
rootTag = None
|
||||||
|
|
||||||
|
editMade = QtCore.Signal() # emitted to allow clients to mark the NBT tree's parent structure as dirty - xxx really??
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(NBTEditorWidget, self).__init__(*args, **kwargs)
|
super(NBTEditorWidget, self).__init__(*args, **kwargs)
|
||||||
self.model = None
|
self.model = None
|
||||||
@ -27,37 +38,197 @@ class NBTEditorWidget(QtGui.QWidget):
|
|||||||
|
|
||||||
self.setLayout(Column(self.treeView))
|
self.setLayout(Column(self.treeView))
|
||||||
|
|
||||||
def setModel(self, model):
|
self.nbtTypesMenu = QtGui.QMenu()
|
||||||
self.model = model
|
self.nbtTypesMenu.addAction(NBTIcon(1), self.tr("Byte"), self.addByte)
|
||||||
self.proxyModel = proxyModel = NBTFilterProxyModel(self)
|
self.nbtTypesMenu.addAction(NBTIcon(2), self.tr("Short"), self.addShort)
|
||||||
proxyModel.setSourceModel(model)
|
self.nbtTypesMenu.addAction(NBTIcon(3), self.tr("Int"), self.addInt)
|
||||||
proxyModel.setDynamicSortFilter(True)
|
self.nbtTypesMenu.addAction(NBTIcon(4), self.tr("Long"), self.addLong)
|
||||||
|
self.nbtTypesMenu.addAction(NBTIcon(5), self.tr("Float"), self.addFloat)
|
||||||
|
self.nbtTypesMenu.addAction(NBTIcon(6), self.tr("Double"), self.addDouble)
|
||||||
|
self.nbtTypesMenu.addAction(NBTIcon(8), self.tr("String"), self.addString)
|
||||||
|
self.nbtTypesMenu.addAction(NBTIcon(9), self.tr("List"), self.addList)
|
||||||
|
self.nbtTypesMenu.addAction(NBTIcon(10), self.tr("Compound"), self.addCompound)
|
||||||
|
|
||||||
self.treeView.setModel(proxyModel)
|
self.nbtTypesMenu.addAction(NBTIcon(7), self.tr("Byte Array"), self.addByteArray)
|
||||||
|
self.nbtTypesMenu.addAction(NBTIcon(11), self.tr("Int Array"), self.addIntArray)
|
||||||
|
self.nbtTypesMenu.addAction(NBTIcon(12), self.tr("Short Array"), self.addShortArray)
|
||||||
|
|
||||||
|
|
||||||
|
def setRootTag(self, rootTag, keepExpanded=False):
|
||||||
|
if rootTag is self.rootTag:
|
||||||
|
return
|
||||||
|
self.rootTag = rootTag
|
||||||
|
if rootTag is None:
|
||||||
|
self.treeView.setModel(None)
|
||||||
|
self.model = None
|
||||||
|
return
|
||||||
|
|
||||||
|
self.model = NBTTreeModel(rootTag)
|
||||||
|
expanded = []
|
||||||
|
current = None
|
||||||
|
if keepExpanded and self.proxyModel:
|
||||||
|
current = self.proxyModel.data(self.treeView.currentIndex(), NBTPathRole)
|
||||||
|
def addExpanded(parentIndex):
|
||||||
|
for row in range(self.proxyModel.rowCount(parentIndex)):
|
||||||
|
index = self.proxyModel.index(row, 0, parentIndex)
|
||||||
|
if self.treeView.isExpanded(index):
|
||||||
|
expanded.append(self.proxyModel.data(index, NBTPathRole))
|
||||||
|
addExpanded(index)
|
||||||
|
|
||||||
|
addExpanded(QtCore.QModelIndex())
|
||||||
|
|
||||||
|
|
||||||
|
self.model.dataChanged.connect(self.dataDidChange)
|
||||||
|
self.model.rowsInserted.connect(self.rowsDidInsert)
|
||||||
|
self.model.rowsRemoved.connect(self.rowsDidRemove)
|
||||||
|
|
||||||
|
self.proxyModel = NBTFilterProxyModel(self)
|
||||||
|
self.proxyModel.setSourceModel(self.model)
|
||||||
|
# self.proxyModel.setDynamicSortFilter(True)
|
||||||
|
|
||||||
|
self.treeView.setModel(self.model)
|
||||||
header = self.treeView.header()
|
header = self.treeView.header()
|
||||||
header.setStretchLastSection(False)
|
header.setStretchLastSection(False)
|
||||||
header.setResizeMode(1, header.ResizeMode.Stretch)
|
header.setResizeMode(1, header.ResizeMode.Stretch)
|
||||||
header.setResizeMode(2, header.ResizeMode.Fixed)
|
header.setResizeMode(2, header.ResizeMode.Fixed)
|
||||||
header.setResizeMode(3, header.ResizeMode.Fixed)
|
header.setResizeMode(3, header.ResizeMode.Fixed)
|
||||||
|
|
||||||
|
if keepExpanded:
|
||||||
|
for path in expanded:
|
||||||
|
matches = self.proxyModel.match(self.proxyModel.index(0, 0, QtCore.QModelIndex()),
|
||||||
|
NBTPathRole, path, flags=Qt.MatchExactly | Qt.MatchRecursive)
|
||||||
|
for i in matches:
|
||||||
|
self.treeView.setExpanded(i, True)
|
||||||
|
if current is not None:
|
||||||
|
matches = self.proxyModel.match(self.proxyModel.index(0, 0, QtCore.QModelIndex()),
|
||||||
|
NBTPathRole, current, flags=Qt.MatchExactly | Qt.MatchRecursive)
|
||||||
|
if len(matches):
|
||||||
|
self.treeView.setCurrentIndex(matches[0])
|
||||||
|
else:
|
||||||
|
self.treeView.expandToDepth(0)
|
||||||
self.treeView.sortByColumn(0, Qt.AscendingOrder)
|
self.treeView.sortByColumn(0, Qt.AscendingOrder)
|
||||||
self.treeView.resizeColumnToContents(0)
|
self.treeView.resizeColumnToContents(0)
|
||||||
self.treeView.resizeColumnToContents(1)
|
self.treeView.resizeColumnToContents(1)
|
||||||
self.treeView.resizeColumnToContents(2)
|
self.treeView.resizeColumnToContents(2)
|
||||||
self.treeView.resizeColumnToContents(3)
|
self.treeView.resizeColumnToContents(3)
|
||||||
|
|
||||||
def itemExpanded(self, index):
|
def itemExpanded(self):
|
||||||
self.treeView.resizeColumnToContents(0)
|
self.treeView.resizeColumnToContents(0)
|
||||||
|
|
||||||
|
indexAddingTo = None
|
||||||
|
|
||||||
def itemClicked(self, index):
|
def itemClicked(self, index):
|
||||||
index = self.proxyModel.mapToSource(index)
|
#index = self.proxyModel.mapToSource(index)
|
||||||
item = self.model.getItem(index)
|
item = self.model.getItem(index)
|
||||||
if index.column() == 2:
|
if index.column() == 2:
|
||||||
if item.isList:
|
if item.isList and item.tag.list_type:
|
||||||
self.model.insertRow(item.childCount(), index)
|
row = item.childCount()
|
||||||
elif item.isCompound:
|
self.model.insertRow(row, index)
|
||||||
""" show tag type menu """
|
newItemIndex = self.model.index(row, 1, index)
|
||||||
|
#self.treeView.setCurrentIndex(self.proxyModel.mapFromSource(newItemIndex))
|
||||||
|
#self.treeView.edit(self.proxyModel.mapFromSource(newItemIndex))
|
||||||
|
|
||||||
|
if item.isCompound or (item.isList and not item.tag.list_type):
|
||||||
|
self.indexAddingTo = index
|
||||||
|
self.nbtTypesMenu.move(QtGui.QCursor.pos())
|
||||||
|
self.nbtTypesMenu.show()
|
||||||
if index.column() == 3:
|
if index.column() == 3:
|
||||||
parent = self.model.parent(index)
|
parent = self.model.parent(index)
|
||||||
|
self.doomedTagName = self.tagNameForUndo(index)
|
||||||
self.model.removeRow(index.row(), parent)
|
self.model.removeRow(index.row(), parent)
|
||||||
|
|
||||||
|
def addItemWithType(self, tagID):
|
||||||
|
if not self.indexAddingTo:
|
||||||
|
return
|
||||||
|
item = self.model.getItem(self.indexAddingTo)
|
||||||
|
row = item.childCount()
|
||||||
|
self.model.insertRow(row, self.indexAddingTo, tagID)
|
||||||
|
newItemIndex = self.model.index(row, 0 if item.isCompound else 1, self.indexAddingTo)
|
||||||
|
#self.treeView.setCurrentIndex(self.proxyModel.mapFromSource(newItemIndex))
|
||||||
|
#self.treeView.edit(self.proxyModel.mapFromSource(newItemIndex))
|
||||||
|
self.indexAddingTo = None
|
||||||
|
|
||||||
|
def addByte(self):
|
||||||
|
self.addItemWithType(1)
|
||||||
|
|
||||||
|
def addShort(self):
|
||||||
|
self.addItemWithType(2)
|
||||||
|
|
||||||
|
def addInt(self):
|
||||||
|
self.addItemWithType(3)
|
||||||
|
|
||||||
|
def addLong(self):
|
||||||
|
self.addItemWithType(4)
|
||||||
|
|
||||||
|
def addFloat(self):
|
||||||
|
self.addItemWithType(5)
|
||||||
|
|
||||||
|
def addDouble(self):
|
||||||
|
self.addItemWithType(6)
|
||||||
|
|
||||||
|
def addByteArray(self):
|
||||||
|
self.addItemWithType(7)
|
||||||
|
|
||||||
|
def addString(self):
|
||||||
|
self.addItemWithType(8)
|
||||||
|
|
||||||
|
def addList(self):
|
||||||
|
self.addItemWithType(9)
|
||||||
|
|
||||||
|
def addCompound(self):
|
||||||
|
self.addItemWithType(10)
|
||||||
|
|
||||||
|
def addIntArray(self):
|
||||||
|
self.addItemWithType(11)
|
||||||
|
|
||||||
|
def addShortArray(self):
|
||||||
|
self.addItemWithType(12)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def tagNameForUndo(self, index):
|
||||||
|
parent = self.model.parent(index)
|
||||||
|
item = self.model.getItem(index)
|
||||||
|
parentItem = self.model.getItem(parent)
|
||||||
|
if parentItem is not None and parentItem.isList:
|
||||||
|
name = "%s #%d" % (self.tagNameForUndo(parent), parentItem.tag.index(item.tag))
|
||||||
|
else:
|
||||||
|
name = item.tag.name
|
||||||
|
return name
|
||||||
|
|
||||||
|
def dataDidChange(self, index):
|
||||||
|
name = self.tagNameForUndo(index)
|
||||||
|
if index.column() == 0:
|
||||||
|
text = "%sRename NBT tag %s" % (self.undoCommandPrefixText, name)
|
||||||
|
elif index.column() == 1:
|
||||||
|
text = "%sChange value of NBT tag %s" % (self.undoCommandPrefixText, name)
|
||||||
|
else:
|
||||||
|
text = "Unknown data changed."
|
||||||
|
|
||||||
|
command = NBTDataChangeCommand(self.editorSession, text)
|
||||||
|
with command.begin():
|
||||||
|
self.editMade.emit()
|
||||||
|
self.editorSession.worldEditor.syncToDisk()
|
||||||
|
self.editorSession.pushCommand(command)
|
||||||
|
|
||||||
|
def rowsDidInsert(self, index):
|
||||||
|
name = self.tagNameForUndo(index.parent())
|
||||||
|
text = "%sInsert NBT tag under %s" % (self.undoCommandPrefixText, name)
|
||||||
|
|
||||||
|
command = NBTDataChangeCommand(self.editorSession, text)
|
||||||
|
with command.begin():
|
||||||
|
self.editMade.emit()
|
||||||
|
self.editorSession.worldEditor.syncToDisk()
|
||||||
|
self.editorSession.pushCommand(command)
|
||||||
|
|
||||||
|
doomedTagName = None
|
||||||
|
|
||||||
|
def rowsDidRemove(self, index, start, end):
|
||||||
|
name = self.tagNameForUndo(index)
|
||||||
|
text = "%sRemove NBT tag %s from %s" % (self.undoCommandPrefixText, self.doomedTagName, name)
|
||||||
|
|
||||||
|
command = NBTDataChangeCommand(self.editorSession, text)
|
||||||
|
with command.begin():
|
||||||
|
self.editMade.emit()
|
||||||
|
self.editorSession.worldEditor.syncToDisk()
|
||||||
|
self.editorSession.pushCommand(command)
|
||||||
|
Reference in New Issue
Block a user