Implement "Replace All" function for NBT search

Add replacement inputs to results widget and synchronize them
Add default value to MCESettingsOption
This commit is contained in:
David Vierra 2015-03-18 15:51:33 -10:00
parent 891649d9d3
commit 83826f7179
4 changed files with 260 additions and 45 deletions

View File

@ -13,10 +13,11 @@ from mcedit2.util.resources import resourcePath
from mcedit2.util.showprogress import showProgress
from mcedit2.widgets.blockpicker import BlockTypeButton
from mcedit2.widgets.layout import Row, Column
from mceditlib.selection import BoundingBox
log = logging.getLogger(__name__)
class NBTResultsEntry(namedtuple("NBTResultsEntry", "displayName location value resultType")):
class NBTResultsEntry(namedtuple("NBTResultsEntry", "tagName value id path position uuid resultType")):
EntityResult = "ENTITY"
TileEntityResult = "TILE_ENTITY"
ItemResult = "ITEM"
@ -75,14 +76,16 @@ class NBTResultsModel(QtCore.QAbstractItemModel):
if role in (Qt.DisplayRole, Qt.EditRole):
column = index.column()
if column == 0:
return entry.displayName
return entry.tagName
elif column == 1:
return entry.value
elif column == 2:
path = "/".join(str(p) for p in entry.path)
if entry.resultType == entry.EntityResult:
return "%s@%s/%s (%s)" % entry.location
return "%s@%s/%s (%s)" % (entry.id, entry.position, path, entry.uuid)
elif entry.resultType == entry.TileEntityResult:
return "%s@%s/%s" % entry.location[:3]
return "%s@%s/%s" % (entry.id, entry.position, path)
else:
return ""
# value = entry.value
@ -100,6 +103,11 @@ class NBTResultsModel(QtCore.QAbstractItemModel):
self.results.extend(results)
self.endInsertRows()
def clear(self):
size = len(self.results)
self.beginRemoveRows(QtCore.QModelIndex(), 0, size - 1)
self.results[:] = []
self.endRemoveRows()
# def setData(self, index, value, role=Qt.EditRole):
# row = index.row()
@ -214,15 +222,34 @@ class FindReplaceBlocks(QtCore.QObject):
self.editorSession.pushCommand(command)
nbtReplaceSettings = settings.Settings().getNamespace("findreplace/nbt")
nbtReplaceSettings.nameField = nbtReplaceSettings.getOption("nameField", unicode)
nbtReplaceSettings.valueField = nbtReplaceSettings.getOption("valueField", unicode)
nbtReplaceSettings.nameField = nbtReplaceSettings.getOption("nameField", unicode, "")
nbtReplaceSettings.valueField = nbtReplaceSettings.getOption("valueField", unicode, "")
nbtReplaceSettings.entityIDField = nbtReplaceSettings.getOption("entityIDField", unicode)
nbtReplaceSettings.searchEntitiesChecked = nbtReplaceSettings.getOption("searchEntitiesChecked", bool)
nbtReplaceSettings.entityIDField = nbtReplaceSettings.getOption("entityIDField", unicode, "")
nbtReplaceSettings.searchEntitiesChecked = nbtReplaceSettings.getOption("searchEntitiesChecked", bool, False)
nbtReplaceSettings.tileEntityIDField = nbtReplaceSettings.getOption("tileEntityIDField", unicode)
nbtReplaceSettings.searchTileEntitiesChecked = nbtReplaceSettings.getOption("searchTileEntitiesChecked", bool)
nbtReplaceSettings.tileEntityIDField = nbtReplaceSettings.getOption("tileEntityIDField", unicode, "")
nbtReplaceSettings.searchTileEntitiesChecked = nbtReplaceSettings.getOption("searchTileEntitiesChecked", bool, False)
nbtReplaceSettings.replaceNameField = nbtReplaceSettings.getOption("replaceNameField", unicode, "")
nbtReplaceSettings.replaceValueField = nbtReplaceSettings.getOption("replaceValueField", unicode, "")
nbtReplaceSettings.replaceValueTagType = nbtReplaceSettings.getOption("replaceValueTagType", int, 0)
class ReplaceValueTagType(object):
"""
duplicated from find_replace_nbt.ui
"""
EXISTING = 0
STRING = 1
BYTE = 2
SHORT = 3
INT = 4
LONG = 5
FLOAT = 6
DOUBLE = 7
class NBTReplaceCommand(SimpleRevisionCommand):
pass
class FindReplaceNBT(QtCore.QObject):
def __init__(self, editorSession, dialog):
@ -231,24 +258,29 @@ class FindReplaceNBT(QtCore.QObject):
self.widget = load_ui("find_replace_nbt.ui")
self.dialog = dialog
self.resultsWidgetContents = load_ui("find_replace_nbt_results.ui")
self.resultsWidget = QtGui.QDockWidget("NBT Search", objectName="nbtSearch")
self.resultsWidget.setWidget(self.resultsWidgetContents)
self.resultsWidget.hide()
self.resultsWidget = load_ui("find_replace_nbt_results.ui")
self.resultsDockWidget = QtGui.QDockWidget("NBT Search", objectName="nbtSearch")
self.resultsDockWidget.setWidget(self.resultsWidget)
self.resultsDockWidget.hide()
self.resultsModel = NBTResultsModel()
self.resultsWidgetContents.resultsView.setModel(self.resultsModel)
self.resultsWidget.resultsView.setModel(self.resultsModel)
# --- Buttons ---
self.widget.findButton.clicked.connect(self.find)
self.resultsWidgetContents.stopButton.clicked.connect(self.stop)
self.resultsWidgetContents.findAgainButton.clicked.connect(dialog.exec_)
self.resultsWidget.stopButton.clicked.connect(self.stop)
self.resultsWidget.findAgainButton.clicked.connect(dialog.exec_)
self.resultsWidget.replaceSelectedButton.clicked.connect(self.replaceSelected)
self.resultsWidget.replaceAllButton.clicked.connect(self.replaceAll)
self.widget.searchNameCheckbox.toggled.connect(self.searchForToggled)
self.widget.searchValueCheckbox.toggled.connect(self.searchForToggled)
self.findTimer = None
self.finder = None
# --- Search for... ---
self.widget.nameField.setText(nbtReplaceSettings.nameField.value(""))
self.widget.searchNameCheckbox.setChecked(len(self.widget.nameField.text()) > 0)
self.widget.nameField.textChanged.connect(self.nameFieldChanged)
@ -257,18 +289,37 @@ class FindReplaceNBT(QtCore.QObject):
self.widget.searchValueCheckbox.setChecked(len(self.widget.valueField.text()) > 0)
self.widget.valueField.textChanged.connect(self.valueFieldChanged)
self.widget.searchEntitiesCheckbox.setChecked(nbtReplaceSettings.searchEntitiesChecked.value(False))
# --- Search in... ---
self.widget.searchEntitiesCheckbox.setChecked(nbtReplaceSettings.searchEntitiesChecked.value())
self.widget.searchEntitiesCheckbox.toggled.connect(nbtReplaceSettings.searchEntitiesChecked.setValue)
self.widget.entityIDField.setText(nbtReplaceSettings.entityIDField.value(""))
self.widget.entityIDField.setText(nbtReplaceSettings.entityIDField.value())
self.widget.entityIDField.textChanged.connect(self.entityIDFieldChanged)
self.widget.searchTileEntitiesCheckbox.setChecked(nbtReplaceSettings.searchTileEntitiesChecked.value(False))
self.widget.searchTileEntitiesCheckbox.setChecked(nbtReplaceSettings.searchTileEntitiesChecked.value())
self.widget.searchTileEntitiesCheckbox.toggled.connect(nbtReplaceSettings.searchTileEntitiesChecked.setValue)
self.widget.tileEntityIDField.setText(nbtReplaceSettings.tileEntityIDField.value(""))
self.widget.tileEntityIDField.setText(nbtReplaceSettings.tileEntityIDField.value())
self.widget.tileEntityIDField.textChanged.connect(self.tileEntityIDFieldChanged)
# --- Replace with... ---
self.widget.replaceNameField.setText(nbtReplaceSettings.replaceNameField.value())
self.resultsWidget.replaceNameField.setText(nbtReplaceSettings.replaceNameField.value())
self.widget.replaceNameField.textChanged.connect(self.replaceNameFieldChanged)
self.widget.replaceNameCheckbox.setChecked(len(self.widget.replaceNameField.text()))
self.resultsWidget.replaceNameCheckbox.setChecked(len(self.widget.replaceNameField.text()))
self.widget.replaceValueField.setText(nbtReplaceSettings.replaceValueField.value())
self.resultsWidget.replaceValueField.setText(nbtReplaceSettings.replaceValueField.value())
self.widget.replaceValueField.textChanged.connect(self.replaceValueFieldChanged)
self.widget.replaceValueCheckbox.setChecked(len(self.widget.replaceValueField.text()))
self.resultsWidget.replaceValueCheckbox.setChecked(len(self.widget.replaceValueField.text()))
self.widget.replaceValueTagTypeComboBox.setCurrentIndex(nbtReplaceSettings.replaceValueTagType.value())
self.widget.replaceValueTagTypeComboBox.currentIndexChanged[int].connect(self.valueTagTypeChanged)
def dialogOpened(self):
currentSelection = self.editorSession.currentSelection
self.widget.inSelectionCheckbox.setChecked(currentSelection is not None and currentSelection.volume > 0)
@ -295,6 +346,29 @@ class FindReplaceNBT(QtCore.QObject):
if len(value):
self.widget.searchTileEntitiesCheckbox.setChecked(True)
def replaceNameFieldChanged(self, value):
if value != nbtReplaceSettings.replaceNameField.value():
nbtReplaceSettings.replaceNameField.setValue(value)
self.widget.replaceNameCheckbox.setChecked(len(value) > 0)
self.widget.replaceNameField.setText(value)
self.resultsWidget.replaceNameCheckbox.setChecked(len(value) > 0)
self.resultsWidget.replaceNameField.setText(value)
def replaceValueFieldChanged(self, value):
if value != nbtReplaceSettings.replaceValueField.value():
nbtReplaceSettings.replaceValueField.setValue(value)
self.widget.replaceValueCheckbox.setChecked(len(value) > 0)
self.widget.replaceValueField.setText(value)
self.resultsWidget.replaceValueCheckbox.setChecked(len(value) > 0)
self.resultsWidget.replaceValueField.setText(value)
def valueTagTypeChanged(self, index):
nbtReplaceSettings.replaceValueTagType.setValue(index)
def find(self):
searchNames = self.widget.searchNameCheckbox.isChecked()
targetName = self.widget.nameField.text()
@ -360,9 +434,15 @@ class FindReplaceNBT(QtCore.QObject):
result = _findTag(name, subtag, path)
if result:
name, path, value = result
location = entity.id, entity.Position, path, uuid
results.append(NBTResultsEntry(name, location, value, NBTResultsEntry.EntityResult))
results.append(NBTResultsEntry(tagName=name,
value=value,
id=entity.id,
path=path,
position=entity.Position,
uuid=uuid,
resultType=NBTResultsEntry.EntityResult))
self.resultsModel.addResults(results)
@ -379,18 +459,24 @@ class FindReplaceNBT(QtCore.QObject):
result = _findTag(name, subtag, path)
if result:
name, path, value = result
location = tileEntity.id, tileEntity.Position, path, None
results.append(NBTResultsEntry(name, location, value, NBTResultsEntry.TileEntityResult))
results.append(NBTResultsEntry(tagName=name,
value=value,
id=tileEntity.id,
path=path,
position=tileEntity.Position,
uuid=None,
resultType=NBTResultsEntry.TileEntityResult))
self.resultsModel.addResults(results)
def _find():
self.resultsWidget.show()
self.resultsDockWidget.show()
self.resultsModel.clear()
self.dialog.accept()
self.resultsWidgetContents.findAgainButton.setEnabled(False)
self.resultsWidget.findAgainButton.setEnabled(False)
self.resultsWidgetContents.progressBar.setMaximum(selection.chunkCount-1)
self.resultsWidget.progressBar.setMaximum(selection.chunkCount-1)
for i, cPos in enumerate(selection.chunkPositions()):
if dim.containsChunk(*cPos):
chunk = dim.getChunk(*cPos)
@ -400,7 +486,7 @@ class FindReplaceNBT(QtCore.QObject):
_findTileEntitiesInChunk(chunk)
yield
self.resultsWidgetContents.progressBar.setValue(i)
self.resultsWidget.progressBar.setValue(i)
self.stop()
@ -414,26 +500,76 @@ class FindReplaceNBT(QtCore.QObject):
self.findTimer = QtCore.QTimer(timeout=find, interval=1.0)
self.findTimer.start()
self.resultsWidgetContents.stopButton.setEnabled(True)
self.resultsWidget.stopButton.setEnabled(True)
def stop(self):
if self.findTimer:
self.findTimer.stop()
self.widget.findButton.setEnabled(True)
self.resultsWidgetContents.stopButton.setEnabled(False)
self.resultsWidgetContents.findAgainButton.setEnabled(True)
self.resultsWidget.stopButton.setEnabled(False)
self.resultsWidget.findAgainButton.setEnabled(True)
def replaceEntries(self, entries):
shouldReplaceName = self.widget.replaceNameCheckbox.isChecked()
newName = self.widget.replaceNameField.text()
shouldReplaceValue = self.widget.replaceValueCheckbox.isChecked()
newValue = self.widget.replaceValueField.text()
# newTagType = self.widget.replaceTagTypeComboBox.currentIndex()
def walkNBT(tag, path=""):
def _replaceInTag(result, tag):
for component in result.path:
tag = tag[component]
if shouldReplaceName:
subtag = tag.pop(result.tagName)
tag[newName] = subtag
if shouldReplaceValue:
subtag = tag[result.tagName]
# xxx newTagType
subtag.value = newValue
def _replace():
for result in entries:
if result.resultType == result.TileEntityResult:
tileEntity = self.editorSession.currentDimension.getTileEntity(result.position)
tag = tileEntity.raw_tag()
_replaceInTag(result, tag)
if result.resultType == result.EntityResult:
box = BoundingBox(result.position, (1, 1, 1)).chunkBox(self.editorSession.currentDimension)
entity = self.editorSession.currentDimension.getEntities(box, UUID=result.uuid)
tag = entity.raw_tag()
_replaceInTag(result, tag)
# if result.resultType == result.ItemResult: # xxx
yield
command = NBTReplaceCommand(self.editorSession, "Replace NBT data") # xxx replace details
with command.begin():
replacer = _replace()
showProgress("Replacing NBT data...", replacer)
self.editorSession.pushCommand(command)
def replaceAll(self):
self.replaceEntries(self.resultsModel.results)
def replaceSelected(self):
pass
def walkNBT(tag, path=None):
if path is None:
path = []
if tag.isCompound():
for name, subtag in tag.iteritems():
yield (name, subtag, path)
walkNBT(subtag, path + "/" + name)
walkNBT(subtag, path + [name])
if tag.isList():
for i, subtag in enumerate(tag):
yield (i, subtag, path)
walkNBT(subtag, path + "/" + str(i))
walkNBT(subtag, path + [i])
class FindReplaceDialog(QtGui.QDialog):
@ -449,8 +585,8 @@ class FindReplaceDialog(QtGui.QDialog):
self.nbtTab.setLayout(Column(self.findReplaceNBT.widget, margin=0))
self.resultsWidgets = [
# self.findReplaceBlocks.resultsWidget,
self.findReplaceNBT.resultsWidget,
# self.findReplaceBlocks.resultsDockWidget,
self.findReplaceNBT.resultsDockWidget,
]
self.adjustSize()

View File

@ -215,7 +215,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="replaceTagTypeWithComboBox">
<widget class="QComboBox" name="replaceValueTagTypeComboBox">
<item>
<property name="text">
<string>Existing</string>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>578</width>
<height>345</height>
<width>771</width>
<height>458</height>
</rect>
</property>
<property name="windowTitle">
@ -27,6 +27,81 @@
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="replaceNameCheckbox">
<property name="text">
<string>Replace Name With:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="replaceNameField"/>
</item>
<item>
<widget class="QCheckBox" name="replaceValueCheckbox">
<property name="text">
<string>Replace Value With:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="replaceValueField"/>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Tag Type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="replaceValueTagTypeComboBox">
<item>
<property name="text">
<string>Existing</string>
</property>
</item>
<item>
<property name="text">
<string>String</string>
</property>
</item>
<item>
<property name="text">
<string>Byte</string>
</property>
</item>
<item>
<property name="text">
<string>Short</string>
</property>
</item>
<item>
<property name="text">
<string>Int</string>
</property>
</item>
<item>
<property name="text">
<string>Long</string>
</property>
</item>
<item>
<property name="text">
<string>Float</string>
</property>
</item>
<item>
<property name="text">
<string>Double</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>

View File

@ -22,13 +22,17 @@ def Settings():
class MCESettingsOption(QtCore.QObject):
def __init__(self, settings, key, type, *args, **kwargs):
def __init__(self, settings, key, type, default=None, *args, **kwargs):
super(MCESettingsOption, self).__init__(*args, **kwargs)
self.settings = settings
self.key = key
self.type = type
self.default = default
def value(self, default=None):
if default is None:
default = self.default
value = self.settings.value(self.key, default)
if self.type:
value = self.type(value)
@ -53,8 +57,8 @@ class MCESettingsNamespace(object):
self.prefix = prefix
def getOption(self, key, type=None):
return self.rootSettings.getOption(self.prefix + key, type)
def getOption(self, key, type=None, default=None):
return self.rootSettings.getOption(self.prefix + key, type, default)
class MCESettings(QtCore.QSettings):
@ -123,7 +127,7 @@ class MCESettings(QtCore.QSettings):
def setJsonValue(self, key, value):
self.setValue(key, json.dumps(value))
def getOption(self, key, type=None):
def getOption(self, key, type=None, default=None):
"""
Return an object that represents the setting at 'key'. The object may be used to get and set the value and
get the value's valueChanged signal. Among other uses, the object's setValue attribute may be connected to the
@ -138,7 +142,7 @@ class MCESettings(QtCore.QSettings):
if option:
return option
option = MCESettingsOption(self, key, type)
option = MCESettingsOption(self, key, type, default)
self.options[key] = option
return option