Basic Analyze Tool
Just some first things.
This commit is contained in:
parent
fde3d1624b
commit
fa65763c10
67
src/mcedit2/editorcommands/analyze.py
Normal file
67
src/mcedit2/editorcommands/analyze.py
Normal file
@ -0,0 +1,67 @@
|
||||
"""
|
||||
analyze
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from PySide import QtGui, QtCore
|
||||
import logging
|
||||
|
||||
from mcedit2.util.load_ui import load_ui
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class AnalyzeOutputDialog(QtGui.QDialog):
|
||||
def __init__(self, editorSession, blockCount, entityCount, tileEntityCount, *args, **kwargs):
|
||||
super(AnalyzeOutputDialog, self).__init__(*args, **kwargs)
|
||||
self.editorSession = editorSession
|
||||
self.blocktypes = editorSession.worldEditor.blocktypes
|
||||
|
||||
load_ui("analyze.ui", baseinstance=self)
|
||||
blockTable = self.blockOutputTable
|
||||
self.setupBlockTable(blockCount, blockTable)
|
||||
|
||||
entityTable = self.entityOutputTable
|
||||
self.setupEntityTable(entityCount, tileEntityCount, entityTable)
|
||||
|
||||
self.sizeHint()
|
||||
self.exec_()
|
||||
|
||||
|
||||
|
||||
def setupBlockTable(self, blockCount, table):
|
||||
blockCounts = sorted([(self.editorSession.worldEditor.blocktypes[ i & 0xfff, i >> 12], blockCount[i])
|
||||
for i in blockCount.nonzero()[0]])
|
||||
table.setRowCount(len(blockCounts))
|
||||
table.setColumnCount(4)
|
||||
table.setHorizontalHeaderLabels(['Name', 'ID', 'Data', 'Count'])
|
||||
|
||||
for n, output in enumerate(blockCounts):
|
||||
nameItem = QtGui.QTableWidgetItem(output[0].displayName)
|
||||
idItem = QtGui.QTableWidgetItem(str(output[0].ID))
|
||||
dataItem = QtGui.QTableWidgetItem(str(output[0].meta))
|
||||
countItem = QtGui.QTableWidgetItem(str(output[1]))
|
||||
table.setItem(n, 0, nameItem)
|
||||
table.setItem(n, 1, idItem)
|
||||
table.setItem(n, 2, dataItem)
|
||||
table.setItem(n, 3, countItem)
|
||||
|
||||
table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
|
||||
table.resizeColumnsToContents()
|
||||
table.resizeRowsToContents()
|
||||
table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
|
||||
def setupEntityTable(self, entityCount, tileEntityCount, table):
|
||||
table.setRowCount(len(entityCount.items())+len(tileEntityCount.items()))
|
||||
table.setColumnCount(2)
|
||||
table.setHorizontalHeaderLabels(['Name', 'Count'])
|
||||
|
||||
for c in (entityCount, tileEntityCount):
|
||||
for n, (id, count) in enumerate(sorted(c.iteritems())):
|
||||
idItem = QtGui.QTableWidgetItem(str(id))
|
||||
countItem = QtGui.QTableWidgetItem(str(count))
|
||||
table.setItem(n, 0, idItem)
|
||||
table.setItem(n, 1, countItem)
|
||||
|
||||
table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
|
||||
table.resizeColumnsToContents()
|
||||
table.resizeRowsToContents()
|
||||
table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
@ -10,6 +10,7 @@ from mcedit2 import editortools
|
||||
from mcedit2.command import SimpleRevisionCommand
|
||||
from mcedit2.editorcommands.fill import fillCommand
|
||||
from mcedit2.editorcommands.find_replace import FindReplaceDialog
|
||||
from mcedit2.editorcommands.analyze import AnalyzeOutputDialog
|
||||
from mcedit2.editortools.select import SelectCommand
|
||||
from mcedit2.panels.player import PlayerPanel
|
||||
from mcedit2.util.dialogs import NotImplementedYet
|
||||
@ -197,6 +198,10 @@ class EditorSession(QtCore.QObject):
|
||||
self.actionFindReplace.setShortcut(QtGui.QKeySequence.Find)
|
||||
self.actionFindReplace.setObjectName("actionFindReplace")
|
||||
|
||||
self.actionAnalyze = QtGui.QAction(self.tr("Analyze"), self, triggered=self.analyze, enabled=True)
|
||||
#self.actionAnalyze.setShortcut(QtGui.QKeySequence.Analyze)
|
||||
self.actionAnalyze.setObjectName("actionAnalyze")
|
||||
|
||||
undoAction = self.undoStack.createUndoAction(self.menuEdit)
|
||||
undoAction.setShortcut(QtGui.QKeySequence.Undo)
|
||||
redoAction = self.undoStack.createRedoAction(self.menuEdit)
|
||||
@ -218,6 +223,8 @@ class EditorSession(QtCore.QObject):
|
||||
self.menuEdit.addAction(self.actionFill)
|
||||
self.menuEdit.addSeparator()
|
||||
self.menuEdit.addAction(self.actionFindReplace)
|
||||
self.menuEdit.addAction(self.actionAnalyze)
|
||||
|
||||
|
||||
self.menus.append(self.menuEdit)
|
||||
|
||||
@ -500,7 +507,14 @@ class EditorSession(QtCore.QObject):
|
||||
|
||||
def findReplace(self):
|
||||
self.findReplaceDialog.exec_()
|
||||
|
||||
|
||||
def analyze(self):
|
||||
task = self.currentDimension.analyzeIter(self.currentSelection)
|
||||
showProgress("Analyzing...", task)
|
||||
outputDialog = AnalyzeOutputDialog(self, self.worldEditor.analyzeBlockOutput,
|
||||
self.worldEditor.analyzeEntityOutput,
|
||||
self.worldEditor.analyzeTileEntityOutput)
|
||||
|
||||
def deleteSelection(self):
|
||||
command = SimpleRevisionCommand(self, "Delete")
|
||||
with command.begin():
|
||||
|
28
src/mcedit2/ui/analyze.ui
Normal file
28
src/mcedit2/ui/analyze.ui
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>409</width>
|
||||
<height>193</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Analyze Output</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,2">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="blockOutputTable">
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="entityOutputTable">
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
</ui>
|
87
src/mceditlib/operations/analyze.py
Normal file
87
src/mceditlib/operations/analyze.py
Normal file
@ -0,0 +1,87 @@
|
||||
"""
|
||||
block_fill.py
|
||||
|
||||
Optimized functions for mass-replacing blocks in a world.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
import numpy
|
||||
from collections import defaultdict
|
||||
from mceditlib.operations import Operation
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
class AnalyzeOperation(Operation):
|
||||
def __init__(self, dimension, selection):
|
||||
"""
|
||||
Analyze all blocks in a selection.
|
||||
|
||||
If blocksToReplace is given, it may be a list or tuple of blocktypes to replace with the given blocktype.
|
||||
|
||||
Additionally, blockType may be given as a list of (oldBlockType, newBlockType) pairs
|
||||
to perform multiple replacements.
|
||||
|
||||
If updateLights is True, also checks to see if block changes require lighting updates and performs them.
|
||||
|
||||
:type dimension: WorldEditorDimension
|
||||
:type selection: `~.BoundingBox`
|
||||
"""
|
||||
super(AnalyzeOperation, self).__init__(dimension, selection)
|
||||
|
||||
self.createSections = False
|
||||
self.blocks = numpy.zeros(65536, dtype='uint32')
|
||||
self.selection = selection
|
||||
self.entityCounts = defaultdict(int)
|
||||
self.tileEntityCounts = defaultdict(int)
|
||||
|
||||
self.chunkCount = 0
|
||||
self.skipped = 0
|
||||
self.sections = 0
|
||||
log.info("Analyzing %s blocks", selection.volume)
|
||||
|
||||
def done(self):
|
||||
log.info(u"Analyze: Skipped {0}/{1} sections".format(self.skipped, self.sections))
|
||||
self.dimension.worldEditor.analyzeBlockOutput = self.blocks
|
||||
self.dimension.worldEditor.analyzeEntityOutput = self.entityCounts
|
||||
self.dimension.worldEditor.analyzeTileEntityOutput = self.tileEntityCounts
|
||||
|
||||
def operateOnChunk(self, chunk):
|
||||
self.chunkCount += 1
|
||||
|
||||
cx, cz = chunk.cx, chunk.cz
|
||||
for cy in chunk.bounds.sectionPositions(cx, cz):
|
||||
section = chunk.getSection(cy, create=False)
|
||||
|
||||
if section is None:
|
||||
continue
|
||||
self.sections += 1
|
||||
|
||||
sectionMask = self.selection.section_mask(cx, cy, cz)
|
||||
if sectionMask is None:
|
||||
self.skipped += 1
|
||||
continue
|
||||
|
||||
maskSize = sectionMask.sum()
|
||||
if maskSize == 0:
|
||||
self.skipped += 1
|
||||
continue
|
||||
|
||||
for ref in chunk.Entities:
|
||||
if ref.Position in self.selection:
|
||||
self.entityCounts[ref.rootTag["id"].value] += 1
|
||||
|
||||
for ref in chunk.TileEntities:
|
||||
if ref.Position in self.selection:
|
||||
self.tileEntityCounts[ref.rootTag["id"].value] += 1
|
||||
|
||||
|
||||
blocks = numpy.array(section.Blocks[sectionMask], dtype='uint16')
|
||||
blocks |= (numpy.array(section.Data[sectionMask], dtype='uint16') << 12)
|
||||
b = numpy.bincount(blocks.ravel())
|
||||
self.blocks[:b.shape[0]] += b
|
||||
|
||||
|
||||
|
@ -12,13 +12,14 @@ from mceditlib import cachefunc
|
||||
from mceditlib.block_copy import copyBlocksIter
|
||||
from mceditlib.nbtattr import NBTListProxy
|
||||
from mceditlib.operations.block_fill import FillBlocksOperation
|
||||
from mceditlib.operations.analyze import AnalyzeOperation
|
||||
from mceditlib.selection import BoundingBox
|
||||
from mceditlib.findadapter import findAdapter
|
||||
from mceditlib.multi_block import getBlocks, setBlocks
|
||||
from mceditlib.schematic import createSchematic
|
||||
from mceditlib.util import displayName, chunk_pos, exhaust, matchEntityTags
|
||||
from mceditlib.util.lazyprop import weakrefprop
|
||||
|
||||
from mceditlib.blocktypes import BlockType
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -598,6 +599,8 @@ class WorldEditor(object):
|
||||
if not len(matches):
|
||||
raise ValueError("Could not parse a dimension number from %s", dimName)
|
||||
return int(matches[-1])
|
||||
|
||||
analyzeOutput = None
|
||||
|
||||
class WorldEditorDimension(object):
|
||||
def __init__(self, worldEditor, dimName):
|
||||
@ -752,7 +755,6 @@ class WorldEditorDimension(object):
|
||||
|
||||
def exportSchematic(self, selection):
|
||||
"""
|
||||
|
||||
:type selection: mceditlib.box.BoundingBox
|
||||
:return:
|
||||
:rtype: WorldEditor
|
||||
@ -773,6 +775,14 @@ class WorldEditorDimension(object):
|
||||
|
||||
def fillBlocks(self, box, block, blocksToReplace=(), updateLights=True):
|
||||
return exhaust(self.fillBlocksIter(box, block, blocksToReplace, updateLights))
|
||||
|
||||
# --- Analyze ---
|
||||
|
||||
def analyzeIter(self, selection):
|
||||
return AnalyzeOperation(self, selection)
|
||||
|
||||
def analyze(self, selection):
|
||||
return exhaust(self.analyzeIter(selection))
|
||||
|
||||
# --- Blocks by single coordinate ---
|
||||
|
||||
|
Reference in New Issue
Block a user