Source code for editortools.filter

"""Copyright (c) 2010-2012 David Rio Vierra

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
# Modified by D.C.-G. for translation purpose
import collections
import os
import traceback
import copy
from albow import FloatField, IntField, AttrRef, Row, Label, Widget, TabPanel, \
    CheckBox, Column, Button, TextFieldWrapped, translate
from editortools.tooloptions import ToolOptions
from albow.extended_widgets import CheckBoxLabel
_ = translate._
import albow
from config import config
from editortools.blockview import BlockButton
from editortools.editortool import EditorTool
from glbackground import Panel
from mceutils import setWindowCaption, alertException
from albow import ChoiceButton, showProgress, TextInputRow
import mcplatform
from operation import Operation
from albow.dialogs import wrapped_label, alert, Dialog
import pymclevel
from pymclevel import BoundingBox, MCEDIT_DEFS, MCEDIT_IDS
import urllib2
import urllib
import json
import shutil
import directories
import sys
import keys
import imp
from nbtexplorer import NBTExplorerToolPanel

import logging
log = logging.getLogger(__name__)

[docs]class FilterUtils(object): def __init__(self, **kwargs): self._given_data = [] for arg in kwargs: self._given_data.append(arg) self.__dict__[arg] = kwargs[arg]
[docs] def Available_Attributes(self): return self._given_data
[docs]def alertFilterException(func): def _func(*args, **kw): try: func(*args, **kw) except Exception, e: print traceback.format_exc() alert(_(u"Exception during filter operation. See console for details.\n\n{0}").format(e)) return _func
[docs]def addNumField(page, optionName, oName, val, min_value=None, max_value=None, increment=0.1): if isinstance(val, float): field_type = FloatField if isinstance(increment, int): increment = float(increment) else: field_type = IntField if increment == 0.1: increment = 1 if isinstance(increment, float): increment = int(round(increment)) if min_value == max_value: min_value = None max_value = None field = field_type(value=val, width=200, min=min_value, max=max_value) field._increment = increment page.optionDict[optionName] = AttrRef(field, 'value') row = Row([Label(oName, doNotTranslate=True), field]) return row
[docs]class JsonDictProperty(dict): def __init__(self, filename, **kwargs): super(JsonDictProperty, self).__init__(**kwargs) self._filename = filename def __setitem__(self, key, value): data = self._getJson() data[key] = value self._putJson(data) def __getitem__(self, key): return self._getJson()[key] def __delitem__(self, key): data = self._getJson() del data[key] self._putJson(data) def _putJson(self, data): with open(self._filename, 'wb') as f: json.dump(data, f) def _getJson(self): try: filter_json = json.load(open(self._filename), 'rb') if "Macros" not in filter_json.keys(): filter_json["Macros"] = {} return filter_json except (ValueError, IOError): return {"Macros": {}}
[docs]class SingleFileChooser(Widget): OPEN_FILE = 0 SAVE_FILE = 1 def _open_file(self): file_types = [] for f_type in self.file_types: file_types.append(f_type.replace("*.", "")) file_path = mcplatform.askOpenFile(title='Select a file...', suffixes=file_types) if file_path: self._button.set_text("{filen}".format(filen=os.path.basename(file_path)), True) self.file_path = file_path else: self._button.set_text("Choose a file") self.file_path = None self._button.shrink_wrap() self.shrink_wrap() def _save_file(self): file_types = 'Custom File\0{}\0'.format(';'.join(self.file_types)) if not self.file_types[0].startswith("*"): name = self.file_types[0] else: name = self.file_types[0][1:] file_path = mcplatform.askSaveFile(".", "Save as...", name, file_types, self.file_types[0][1:]) if file_path: self._button.set_text(os.path.basename(file_path), True) self.file_path = file_path else: self._button.set_text('Save a file') self.file_path = None self._button.shrink_wrap() self.shrink_wrap() def __init__(self, file_types=None, operation=0, **kwds): Widget.__init__(self, **kwds) if file_types is None: self.file_types = ["*.*",] else: self.file_types = file_types self.file_path = None self._button = None if operation == self.OPEN_FILE: self._button = Button("Choose a file", action=self._open_file) elif operation == self.SAVE_FILE: self._button = Button("Save a file", action=self._save_file) self.add(self._button) self.shrink_wrap()
[docs]class MacroModuleOptions(Widget): is_gl_container = True def __init__(self, macro_data, *args, **kw): self._parent = None self._macro_data = macro_data if '_parent' in kw.keys(): self._parent = kw.pop('_parent') Widget.__init__(self, *args, **kw) infoColList = [] stepsLabel = wrapped_label("Number of steps: %s" % macro_data["Number of steps"], 300) infoColList.append(stepsLabel) for step in sorted(macro_data.keys()): if step != "Number of steps": infoColList.append(wrapped_label("Step %s: %s" % (int(step) + 1, macro_data[step]["Name"]), 300)) self.add(Column(infoColList)) self.shrink_wrap() @property def options(self): return {} @options.setter def options(self, value): pass
[docs] def run(self): pass
@alertFilterException
[docs] def confirm(self, tool): with setWindowCaption("Applying Macro..."): options = [] filters = [] for step in sorted(self._macro_data.keys()): if step != "Number of steps": filters.append(tool.filterModules[self._macro_data[step]["Name"]]) for module_input in self._macro_data[step]["Inputs"].keys(): if not isinstance(self._macro_data[step]["Inputs"][module_input], (str, unicode)): continue if not self._macro_data[step]["Inputs"][module_input].startswith("block-"): continue toFind = self._macro_data[step]["Inputs"][module_input][6:].split(":") block = tool.editor.materials.get((toFind[0], toFind[1])) self._macro_data[step]["Inputs"][module_input] = block options.append(self._macro_data[step]["Inputs"]) op = MacroOperation(tool.editor, tool.editor.level, tool.selectionBox(), filters, options) tool.editor.level.showProgress = showProgress tool.editor.addOperation(op) tool.editor.addUnsavedEdit() tool.editor.invalidateBox(tool.selectionBox())
[docs]class FilterModuleOptions(Widget): is_gl_container = True def __init__(self, tool, module, *args, **kw): self._parent = None self.nbttree = None self.module = module if '_parent' in kw.keys(): self._parent = kw.pop('_parent') Widget.__init__(self, *args, **kw) self.spacing = 2 self.tool = tool self.pages = pages = TabPanel() pages.is_gl_container = True self.optionDict = {} self.giveEditorObject(module) log.info("Creating options for " + str(module)) if hasattr(module, "inputs"): trn = getattr(module, "trn", None) self.trn = trn if isinstance(module.inputs, list): self.pgs = [] for tabData in module.inputs: title, page, pageRect = self.makeTabPage(self.tool, tabData, trn=trn) self.pgs.append((title, page)) pages.set_parent(None) self.pages = pages = TabPanel(self.pgs) elif isinstance(module.inputs, tuple): title, page, pageRect = self.makeTabPage(self.tool, module.inputs, trn=trn) pages.add_page(title, page) pages.set_rect(pageRect) else: self.size = (0, 0) pages.shrink_wrap() self.add(pages) self.shrink_wrap() if len(pages.pages): if pages.current_page is not None: pages.show_page(pages.current_page) else: pages.show_page(pages.pages[0]) for eachPage in pages.pages: self.optionDict = dict(self.optionDict.items() + eachPage.optionDict.items())
[docs] def rebuildTabPage(self, inputs, **kwargs): title, page, rect = self.makeTabPage(self.tool, inputs, self.trn, **kwargs) for i, t, p, s, r in self.pages.iter_tabs(): if t == title: self.pages.remove_page(p) self.pages.add_page(title, page, idx=i) self.pages.show_page(page) break
[docs] def makeTabPage(self, tool, inputs, trn=None, **kwargs): page = Widget(**kwargs) page.is_gl_container = True rows = [] cols = [] max_height = tool.editor.mainViewport.height - tool.editor.toolbar.height - tool.editor.subwidgets[0].height -\ self._parent.filterSelectRow.height - self._parent.confirmButton.height - self.pages.tab_height page.optionDict = {} page.tool = tool title = "Tab" for optionSpec in inputs: optionName = optionSpec[0] optionType = optionSpec[1] if trn is not None: n = trn._(optionName) else: n = optionName if n == optionName: oName = _(optionName) else: oName = n if isinstance(optionType, tuple): if isinstance(optionType[0], (int, long, float)): if len(optionType) == 3: val, min, max = optionType increment = 0.1 elif len(optionType) == 2: min, max = optionType val = min increment = 0.1 else: val, min, max, increment = optionType rows.append(addNumField(page, optionName, oName, val, min, max, increment)) if isinstance(optionType[0], (str, unicode)): isChoiceButton = False if optionType[0] == "string": kwds = [] wid = None val = None for keyword in optionType: if isinstance(keyword, (str, unicode)) and keyword != "string": kwds.append(keyword) for keyword in kwds: splitWord = keyword.split('=') if len(splitWord) > 1: v = None try: v = int(splitWord[1]) except ValueError: pass key = splitWord[0] if v is not None: if key == "width": wid = v else: if key == "value": val = "=".join(splitWord[1:]) if val is None: val = "" if wid is None: wid = 200 field = TextFieldWrapped(value=val, width=wid) page.optionDict[optionName] = AttrRef(field, 'value') row = Row((Label(oName, doNotTranslate=True), field)) rows.append(row) elif optionType[0] == "block": blockButton = BlockButton(tool.editor.level.materials) try: blockButton.blockInfo = tool.editor.level.materials[optionType[1]] except AttributeError: blockButton.blockInfo = tool.editor.level.materials[0] except KeyError: if tool.editor.level.materials == pymclevel.pocketMaterials: blockButton.blockInfo = pymclevel.alphaMaterials[optionType[1]] else: raise row = Column((Label(oName, doNotTranslate=True), blockButton)) page.optionDict[optionName] = AttrRef(blockButton, 'blockInfo') rows.append(row) elif optionType[0] == "file-save": if len(optionType) == 2: file_chooser = SingleFileChooser(file_types=optionType[1], operation=SingleFileChooser.SAVE_FILE) else: file_chooser = SingleFileChooser(operation=SingleFileChooser.SAVE_FILE) row = Row((Label(oName, doNotTranslate=True), file_chooser)) page.optionDict[optionName] = AttrRef(file_chooser, 'file_path') rows.append(row) elif optionType[0] == "file-open": if len(optionType) == 2: file_chooser = SingleFileChooser(file_types=optionType[1], operation=SingleFileChooser.OPEN_FILE) else: file_chooser = SingleFileChooser(operation=SingleFileChooser.OPEN_FILE) row = Row((Label(oName, doNotTranslate=True), file_chooser)) page.optionDict[optionName] = AttrRef(file_chooser, 'file_path') rows.append(row) else: isChoiceButton = True if isChoiceButton: if trn is not None: __ = trn._ else: __ = _ choices = [__("%s" % a) for a in optionType] choiceButton = ChoiceButton(choices, doNotTranslate=True) page.optionDict[optionName] = AttrRef(choiceButton, 'selectedChoice') rows.append(Row((Label(oName, doNotTranslate=True), choiceButton))) elif isinstance(optionType, bool): cbox = CheckBox(value=optionType) page.optionDict[optionName] = AttrRef(cbox, 'value') row = Row((Label(oName, doNotTranslate=True), cbox)) rows.append(row) elif isinstance(optionType, (int, float)): rows.append(addNumField(self, optionName, oName, optionType)) elif optionType == "blocktype" or isinstance(optionType, pymclevel.materials.Block): blockButton = BlockButton(tool.editor.level.materials) if isinstance(optionType, pymclevel.materials.Block): blockButton.blockInfo = optionType row = Column((Label(oName, doNotTranslate=True), blockButton)) page.optionDict[optionName] = AttrRef(blockButton, 'blockInfo') rows.append(row) elif optionType == "label": rows.append(wrapped_label(oName, 50, doNotTranslate=True)) elif optionType == "string": inp = None # not sure how to pull values from filters, # but leaves it open for the future. Use this variable to set field width. if inp is not None: size = inp else: size = 200 field = TextFieldWrapped(value="") row = TextInputRow(oName, ref=AttrRef(field, 'value'), width=size, doNotTranslate=True) page.optionDict[optionName] = AttrRef(field, 'value') rows.append(row) elif optionType == "title": title = oName elif optionType == "file-save": file_chooser = SingleFileChooser(operation=SingleFileChooser.SAVE_FILE) row = Row((Label(oName, doNotTranslate=True), file_chooser)) page.optionDict[optionName] = AttrRef(file_chooser, 'file_path') rows.append(row) elif optionType == "file-open": file_chooser = SingleFileChooser(operation=SingleFileChooser.OPEN_FILE) row = Row((Label(oName, doNotTranslate=True), file_chooser)) page.optionDict[optionName] = AttrRef(file_chooser, 'file_path') rows.append(row) elif type(optionType) == list and optionType[0].lower() == "nbttree": kw = {'close_text': None, 'load_text': None} if len(optionType) >= 3: def close(): self.pages.show_page(self.pages.pages[optionType[2]]) kw['close_action'] = close kw['close_text'] = "Go Back" if len(optionType) >= 4: if optionType[3]: kw['load_text'] = optionType[3] if hasattr(self.module, 'nbt_ok_action'): kw['ok_action'] = getattr(self.module, 'nbt_ok_action') self.nbttree = NBTExplorerToolPanel(self.tool.editor, nbtObject=optionType[1], height=max_height, no_header=True, copy_data=False, **kw) self.module.set_tree(self.nbttree.tree) for meth_name in dir(self.module): if meth_name.startswith('nbttree_'): setattr(self.nbttree.tree.treeRow, meth_name.split('nbttree_')[-1], getattr(self.module, meth_name)) # elif meth_name.startswith('nbt_'): # setattr(self.nbttree, meth_name.split('nbt_')[-1], getattr(self.module, meth_name)) page.optionDict[optionName] = AttrRef(self, 'rebuildTabPage') rows.append(self.nbttree) self.nbttree.page = len(self.pgs) else: raise ValueError(("Unknown option type", optionType)) height = sum(r.height for r in rows) + (len(rows) - 1) * self.spacing if height > max_height: h = 0 for i, r in enumerate(rows): h += r.height if h > height / 2: if rows[:i]: cols.append(Column(rows[:i], spacing=0)) rows = rows[i:] break if len(rows): cols.append(Column(rows, spacing=0)) if len(cols): page.add(Row(cols, spacing=0)) page.shrink_wrap() return title, page, page._rect
@property def options(self): options = {} for k, v in self.optionDict.iteritems(): options[k] = v.get() if not isinstance(v.get(), pymclevel.materials.Block) else copy.copy(v.get()) if self.pages.current_page is not None: options["__page_index__"] = self.pages.pages.index(self.pages.current_page) return options @options.setter def options(self, val): for k in val: if k in self.optionDict: self.optionDict[k].set(val[k]) index = val.get("__page_index__", -1) if len(self.pages.pages) > index > -1: self.pages.show_page(self.pages.pages[index])
[docs] def giveEditorObject(self, module): module.editor = self.tool.editor
@staticmethod
[docs] def confirm(tool): with setWindowCaption("Applying Filter... - "): filterModule = tool.filterModules[tool.panel.filterSelect.selectedChoice] op = FilterOperation(tool.editor, tool.editor.level, tool.selectionBox(), filterModule, tool.panel.filterOptionsPanel.options) tool.editor.level.showProgress = showProgress tool.editor.addOperation(op) tool.editor.addUnsavedEdit() tool.editor.invalidateBox(tool.selectionBox())
[docs]class FilterToolPanel(Panel): BACKUP_FILTER_JSON = False """If set to true, the filter.json is backed up to the hard disk every time it's edited. The default is false, which makes the file save only whenever the tool gets closed. If MCEdit were to crash, any recorded macros would not be saved.""" def __init__(self, tool): Panel.__init__(self, name='Panel.FilterToolPanel') self.macro_steps = [] self.current_step = 0 self._filter_json = None self.keys_panel = None self.filterOptionsPanel = None self.filterSelect = ChoiceButton([], choose=self.filterChanged, doNotTranslate=True) self.binding_button = Button("", action=self.bind_key, tooltipText="Click to bind this filter to a key") self.filterLabel = Label("Filter:", fg_color=(177, 177, 255, 255)) self.filterLabel.mouse_down = lambda x: mcplatform.platform_open(directories.getFiltersDir()) self.filterLabel.tooltipText = "Click to open filters folder" self.macro_button = Button("Record Macro", action=self.start_record_macro) self.filterSelectRow = Row((self.filterLabel, self.filterSelect, self.macro_button, self.binding_button)) self.confirmButton = Button("Filter", action=self.confirm) self._recording = False self._save_macro = False self.tool = tool self.selectedName = self.filter_json.get("Last Filter Opened", "") utils = FilterUtils( editor=tool.editor, materials=self.tool.editor.level.materials, custom_widget=tool.editor.addExternalWidget, resize_selection_box=tool.editor._resize_selection_box ) utils_module = imp.new_module("filter_utils") utils_module = utils sys.modules["filter_utils"] = utils_module @staticmethod
[docs] def load_filter_json(): filter_json_file = os.path.join(directories.getDataDir(), "filters.json") filter_json = {} if FilterToolPanel.BACKUP_FILTER_JSON: filter_json = JsonDictProperty(filter_json_file) else: try: if os.path.exists(filter_json_file): filter_json = json.load(open(filter_json_file, 'rb')) except (ValueError, IOError) as e: log.error("Error while loading filters.json %s", e) if "Macros" not in filter_json.keys(): filter_json["Macros"] = {} return filter_json
@property def filter_json(self): if self._filter_json is None: self._filter_json = FilterToolPanel.load_filter_json() return self._filter_json
[docs] def close(self): self._saveOptions() self.filter_json["Last Filter Opened"] = self.selectedName if not FilterToolPanel.BACKUP_FILTER_JSON: with open(os.path.join(directories.getDataDir(), "filters.json"), 'w') as f: json.dump(self.filter_json, f)
[docs] def reload(self): for i in list(self.subwidgets): self.remove(i) tool = self.tool # Display "No filter modules found" if there are no filters if len(tool.filterModules) is 0: self.add(Label("No filter modules found!")) self.shrink_wrap() return names_list = sorted([n for n in tool.filterNames if not n.startswith("[")]) # We get a list of names like ["[foo] bar", "[test] thing"] # The to sort on is created by splitting on "[": "[foo", " bar" and then # removing the first char: "foo", "bar" subfolder_names_list = sorted([n for n in tool.filterNames if n.startswith("[")], key=lambda x: x.split("]")[0][1:]) names_list.extend(subfolder_names_list) names_list.extend([macro for macro in self.filter_json["Macros"].keys()]) if self.selectedName is None or self.selectedName not in names_list: self.selectedName = names_list[0] # Remove any keybindings that don't have a filter for (i, j) in config.config.items("Filter Keys"): if i == "__name__": continue if not any([i == m.lower() for m in names_list]): config.config.remove_option("Filter Keys", i) self.filterSelect.choices = names_list name = self.selectedName.lower() names = [k for (k, v) in config.config.items("Filter Keys")] btn_name = config.config.get("Filter Keys", name) if name in names else "*" self.binding_button.set_text(btn_name) self.filterOptionsPanel = None while self.filterOptionsPanel is None: module = self.tool.filterModules.get(self.selectedName, None) if module is not None: try: self.filterOptionsPanel = FilterModuleOptions(self.tool, module, _parent=self) except Exception as e: alert(_("Error creating filter inputs for {0}: {1}").format(module, e)) traceback.print_exc() self.tool.filterModules.pop(self.selectedName) self.selectedName = tool.filterNames[0] if len(tool.filterNames) == 0: raise ValueError("No filters loaded!") if not self._recording: self.confirmButton.set_text("Filter") else: # We verified it was an existing macro already macro_data = self.filter_json["Macros"][self.selectedName] self.filterOptionsPanel = MacroModuleOptions(macro_data) self.confirmButton.set_text("Run Macro") # This has to be recreated every time in case a macro has a longer name then everything else. self.filterSelect = ChoiceButton(names_list, choose=self.filterChanged, doNotTranslate=True) self.filterSelect.selectedChoice = self.selectedName self.filterSelectRow = Row((self.filterLabel, self.filterSelect, self.macro_button, self.binding_button)) self.add(Column((self.filterSelectRow, self.filterOptionsPanel, self.confirmButton))) self.shrink_wrap() if self.parent: height = self.parent.mainViewport.height - self.parent.toolbar.height self.centery = height / 2 + self.parent.subwidgets[0].height if self.selectedName in self.tool.savedOptions: self.filterOptionsPanel.options = self.tool.savedOptions[self.selectedName]
@property def macroSelected(self): return self.filterSelect.selectedChoice not in self.tool.filterNames
[docs] def filterChanged(self): # if self.filterSelect.selectedChoice not in self.tool.filterModules: # return self._saveOptions() self.selectedName = self.filterSelect.selectedChoice if self.macroSelected: # Is macro self.macro_button.set_text("Delete Macro") self.macro_button.action = self.delete_macro elif not self._recording: self.macro_button.set_text("Record Macro") self.macro_button.action = self.start_record_macro self.reload()
[docs] def delete_macro(self): macro_name = self.selectedName if macro_name in self.filter_json["Macros"]: del self.filter_json["Macros"][macro_name] if len(self.filterSelect.choices) == 1: # Just this macro available self.reload() return choices = self.filterSelect.choices self.filterSelect.selectedChoice = choices[0] if choices[0] != macro_name else choices[1] self.filterChanged()
[docs] def stop_record_macro(self): macro_dialog = Dialog() macroNameLabel = Label("Macro Name: ") macroNameField = TextFieldWrapped(width=200) def save_macro(): macro_name = "{Macro} " + macroNameField.get_text() self.filter_json["Macros"][macro_name] = {} self.filter_json["Macros"][macro_name]["Number of steps"] = len(self.macro_steps) self.filterSelect.choices.append(macro_name) for entry in self.macro_steps: for inp in entry["Inputs"].keys(): if not isinstance(entry["Inputs"][inp], pymclevel.materials.Block): if not entry["Inputs"][inp] == "blocktype": continue _inp = entry["Inputs"][inp] entry["Inputs"][inp] = "block-{0}:{1}".format(_inp.ID, _inp.blockData) self.filter_json["Macros"][macro_name][entry["Step"]] = {"Name": entry["Name"], "Inputs": entry["Inputs"]} stop_dialog() self.filterSelect.selectedChoice = macro_name self.filterChanged() def stop_dialog(): self.macro_button.text = "Record Macro" self.macro_button.tooltipText = None self.macro_button.action = self.start_record_macro macro_dialog.dismiss() self.macro_steps = [] self.current_step = 0 self._recording = False input_row = Row((macroNameLabel, macroNameField)) saveButton = Button("Save", action=save_macro) closeButton = Button("Cancel", action=stop_dialog) button_row = Row((saveButton, closeButton)) macro_dialog.add(Column((input_row, button_row))) macro_dialog.shrink_wrap() macro_dialog.present()
[docs] def start_record_macro(self): self.macro_button.text = "Stop recording" self.macro_button.tooltipText = "Currently recording a macro" self.macro_button.action = self.stop_record_macro self.confirmButton.text = "Add macro" self.confirmButton.width += 75 self.confirmButton.centerx = self.centerx self._recording = True
def _addMacroStep(self, name=None, inputs=None): data = {"Name": name, "Step": self.current_step, "Inputs": inputs} self.current_step += 1 self.macro_steps.append(data)
[docs] def unbind_key(self): config.config.remove_option("Filter Keys", self.selectedName) self.binding_button.text = "*" self.keys_panel.dismiss() # self.saveOptions() self.reload()
[docs] def bind_key(self, message=None): panel = Panel(name='Panel.FilterToolPanel.bind_key') panel.bg_color = (0.5, 0.5, 0.6, 1.0) if not message: message = _("Press a key to assign to the filter \"{0}\"\n\n" "Press ESC to cancel.").format(self.selectedName) label = albow.Label(message) unbind_button = Button("Press to unbind", action=self.unbind_key) column = Column((label, unbind_button)) panel.add(column) panel.shrink_wrap() def panelKeyUp(evt): _key_name = self.root.getKey(evt) panel.dismiss(_key_name) def panelMouseUp(evt): button = keys.remapMouseButton(evt.button) _key_name = None if button == 3: _key_name = "Button 3" elif button == 4: _key_name = "Scroll Up" elif button == 5: _key_name = "Scroll Down" elif button == 6: _key_name = "Button 4" elif button == 7: _key_name = "Button 5" if 2 < button < 8: panel.dismiss(_key_name) panel.key_up = panelKeyUp panel.mouse_up = panelMouseUp self.keys_panel = panel key_name = panel.present() if type(key_name) is bool: return True if key_name != "Escape": if key_name in ["Alt-F4", "F1", "F2", "F3", "F4", "F5", "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ctrl-Alt-F9", "Ctrl-Alt-F10"]: self.bind_key(_("You can't use the key {0}.\n" "Press a key to assign to the filter \"{1}\"\n\n" "" "Press ESC to cancel.").format(_(key_name), self.selectedName)) return True keysUsed = [(j, i) for (j, i) in config.config.items("Keys") if i == key_name] if keysUsed: self.bind_key(_("Can't bind. {0} is already used by {1}.\n" "Press a key to assign to the filter \"{2}\"\n\n" "" "Press ESC to cancel.").format(_(key_name), keysUsed[0][0], self.selectedName)) return True filter_keys = [i for (i, j) in config.config.items("Filter Keys") if j == key_name] if filter_keys: self.bind_key(_("Can't bind. {0} is already used by the \"{1}\" filter.\n" "Press a new key.\n\n" "" "Press ESC to cancel.").format(_(key_name), filter_keys[0])) return True config.config.set("Filter Keys", self.selectedName.lower(), key_name) config.save() self.reload()
def _saveOptions(self): """Should never be called. Call filterchanged() or close() instead, which will then call this. :return: """ if self.filterOptionsPanel is not None: options = {} options.update(self.filterOptionsPanel.options) options.pop("", "") self.tool.savedOptions[self.selectedName] = options @alertFilterException
[docs] def confirm(self): if self._recording: self._addMacroStep(self.selectedName, self.filterOptionsPanel.options) else: self.filterOptionsPanel.confirm(self.tool)
[docs]class FilterOperation(Operation): def __init__(self, editor, level, box, filter, options): super(FilterOperation, self).__init__(editor, level) self.box = box self.filter = filter self.options = options self.canUndo = False
[docs] def perform(self, recordUndo=True): if self.level.saving: alert(_("Cannot perform action while saving is taking place")) return if recordUndo: self.undoLevel = self.extractUndo(self.level, self.box) # Inject the defs for blocks/entities in the module # Need to reimport the defs and ids to get the 'fresh' ones from pymclevel import MCEDIT_DEFS, MCEDIT_IDS self.filter.MCEDIT_DEFS = MCEDIT_DEFS self.filter.MCEDIT_IDS = MCEDIT_IDS self.filter.perform(self.level, BoundingBox(self.box), self.options) self.canUndo = True
[docs] def dirtyBox(self): return self.box
[docs]class MacroOperation(Operation): def __init__(self, editor, level, box, filters, options): super(MacroOperation, self).__init__(editor, level) self._box = box self.options = options self.filters = filters self.canUndo = False
[docs] def perform(self, recordUndo=True): if self.level.saving: alert(_("Cannot perform action while saving is taking place")) return if recordUndo: self.undoLevel = self.extractUndo(self.level, self._box) for o, f in zip(self.options, self.filters): f.perform(self.level, BoundingBox(self._box), o) self.canUndo = True
[docs] def dirtyBox(self): return self._box
[docs]class FilterToolOptions(ToolOptions): def __init__(self, tool): ToolOptions.__init__(self, name='Panel.FilterToolOptions') self.tool = tool self.notifications_disabled = False disable_error_popup = CheckBoxLabel("Disable Error Notification", ref=AttrRef(self, 'notifications_disabled')) ok_button = Button("Ok", action=self.dismiss) col = Column((disable_error_popup, ok_button,), spacing=2) self.add(col) self.shrink_wrap()
[docs]class FilterTool(EditorTool): tooltipText = "Filter" toolIconName = "filter" def __init__(self, editor): EditorTool.__init__(self, editor) self.filterModules = {} self.savedOptions = {} self.filters_not_imported = [] self.optionsPanel = FilterToolOptions(self) @property def statusText(self): return "Choose a filter, then click Filter or press Enter to apply it."
[docs] def toolEnabled(self): return not (self.selectionBox() is None)
[docs] def toolSelected(self): self.showPanel()
@alertException def showPanel(self): self.panel = FilterToolPanel(self) self.not_imported_filters = [] self.reloadFilters() self.panel.reload() height = self.editor.mainViewport.height - self.editor.toolbar.height self.panel.centery = height / 2 + self.editor.subwidgets[0].height self.panel.left = self.editor.left self.editor.add(self.panel)
[docs] def hidePanel(self): if self.panel is None: return self.panel.close() if self.panel.parent: self.panel.parent.remove(self.panel) self.panel = None
[docs] def reloadFilters(self): filterFiles = [] unicode_module_names = [] # Tracking stock and custom filters names in order to load correctly the translations. stock_filters = [] cust_filters = [] def searchForFiltersInDir(searchFolder, stock=False): for root, folders, files in os.walk(os.path.join(searchFolder), True): filter_dir = os.path.basename(root) if filter_dir.startswith('demo') or filter_dir.startswith('lib'): continue subFolderString = root.replace(searchFolder, "") if subFolderString.endswith(os.sep): subFolderString = subFolderString[:len(os.sep)] if subFolderString.startswith(os.sep): subFolderString = subFolderString[len(os.sep):] if len(subFolderString) > 0: subFolderString = "[" + subFolderString + "]" try: root = str(root) if root not in sys.path: sys.path.append(root) except UnicodeEncodeError: unicode_module_names.extend([filter_name for filter_name in files]) for possible_filter in files: if possible_filter.endswith(".py"): if stock: stock_filters.append(possible_filter) _stock = True else: cust_filters.append(possible_filter) _stock = False # Force the 'stock' parameter if the filter was found in the stock-filters directory if possible_filter in stock_filters: _stock = True filterFiles.append((root, possible_filter, _stock, subFolderString)) # Search first for the stock filters. searchForFiltersInDir(os.path.join(directories.getDataDir(), "stock-filters"), True) searchForFiltersInDir(directories.getFiltersDir(), False) filterModules = [] org_lang = albow.translate.lang # If the path has unicode chars, there's no way of knowing what order to add the # files to the sys.modules. To fix this, we keep trying to import until we import # fail to import all leftover files. shouldContinue = True while shouldContinue: shouldContinue = False for f in filterFiles: if f[1] in self.not_imported_filters: continue module = tryImport(f[0], f[1], org_lang, f[2], f[3], f[1] in unicode_module_names, notify=(not self.optionsPanel.notifications_disabled)) if module is None: self.not_imported_filters.append(f[1]) continue filterModules.append(module) filterFiles.remove(f) shouldContinue |= True displayNames = [] for m in filterModules: while m.displayName in displayNames: m.displayName += "_" displayNames.append(m) filterModules = filter(lambda mod: hasattr(mod, "perform"), filterModules) self.filterModules = collections.OrderedDict(sorted( [(FilterTool.moduleDisplayName(x), x) for x in filterModules], key=lambda module_name: (module_name[0].lower(), module_name[1])))
@staticmethod
[docs] def moduleDisplayName(module): subFolderString = getattr(module, 'foldersForDisplayName', "") subFolderString = subFolderString if len(subFolderString) < 1 else subFolderString + " " name = getattr(module, "displayName", module.__name__) return subFolderString + _(name[0].upper() + name[1:])
@property def filterNames(self): return [FilterTool.moduleDisplayName(module) for module in self.filterModules.itervalues()] #-# WIP. Reworking on the filters translations. #-# The 'new_method' variable is used to select the latest working code or the actual under development one. #-# This variable must be on False when releasing unless the actual code is fully working.
new_method = True
[docs]def tryImport_old(_root, name, org_lang, stock=False, subFolderString="", unicode_name=False, notify=True): with open(os.path.join(_root, name)) as module_file: module_name = name.split(os.path.sep)[-1].replace(".py", "") try: if unicode_name: source_code = module_file.read() module = imp.new_module(module_name) exec (source_code, module.__dict__) if module_name not in sys.modules.keys(): sys.modules[module_name] = module else: module = imp.load_source(module_name, os.path.join(_root, name), module_file) module.foldersForDisplayName = subFolderString if not (hasattr(module, 'displayName')): module.displayName = module_name # Python is awesome if not stock: if "trn" in sys.modules.keys(): del sys.modules["trn"] if "albow.translate" in sys.modules.keys(): del sys.modules["albow.translate"] from albow import translate as trn if directories.getFiltersDir() in name: trn_path = os.path.split(name)[0] else: trn_path = directories.getFiltersDir() trn_path = os.path.join(trn_path, subFolderString[1:-1], module_name) module.trn = trn if os.path.exists(trn_path): module.trn.setLangPath(trn_path) module.trn.buildTranslation(config.settings.langCode.get()) n = module.displayName if hasattr(module, "trn"): n = module.trn._(module.displayName) if n == module.displayName: n = _(module.displayName) module.displayName = n import albow.translate albow.translate.lang = org_lang return module except Exception as e: traceback.print_exc() if notify: alert(_(u"Exception while importing filter module {}. " + u"See console for details.\n\n{}").format(name, e)) return None
[docs]def tryImport_new(_root, name, org_lang, stock=False, subFolderString="", unicode_name=False, notify=True): with open(os.path.join(_root, name)) as module_file: module_name = name.split(os.path.sep)[-1].replace(".py", "") try: if unicode_name: source_code = module_file.read() module = imp.new_module(module_name) exec (source_code, module.__dict__) if module_name not in sys.modules.keys(): sys.modules[module_name] = module else: module = imp.load_source(module_name, os.path.join(_root, name), module_file) module.foldersForDisplayName = subFolderString if not (hasattr(module, 'displayName')): module.displayName = module_name # Python is awesome if not stock: # This work fine with custom filters, but the choice buttons are broken for the stock ones... if directories.getFiltersDir() in name: trn_path = os.path.split(name)[0] else: trn_path = directories.getFiltersDir() trn_path = os.path.join(trn_path, subFolderString[1:-1], module_name) if os.path.exists(trn_path): albow.translate.buildTranslation(config.settings.langCode.get(), extend=True, langPath=trn_path) # module.trn = albow.translate module.displayName = _(module.displayName) module.trn = albow.translate return module except Exception as e: traceback.print_exc() if notify: alert(_(u"Exception while importing filter module {}. " + u"See console for details.\n\n{}").format(name, e)) return None
if new_method: tryImport = tryImport_new else: tryImport = tryImport_old