class ConvertExtension(SimplePanel, BaseWorldProgram): def __init__(self, container, world: World): SimplePanel.__init__( self, container ) self.world = world self._close_world_button = wx.Button(self, wx.ID_ANY, label='Close World') self._close_world_button.Bind(wx.EVT_BUTTON, self._close_world) self.add_object(self._close_world_button, 0, wx.ALL | wx.CENTER) self._input = SimplePanel(self, wx.HORIZONTAL) self.add_object(self._input, 0, wx.ALL|wx.CENTER) self._input.add_object( wx.StaticText( self._input, wx.ID_ANY, 'Input World: ', wx.DefaultPosition, wx.DefaultSize, 0, ), 0, wx.ALL|wx.CENTER ) self._input.add_object( WorldUI(self._input, self.world.world_wrapper), 0, wx.ALL|wx.CENTER ) self._output = SimplePanel(self, wx.HORIZONTAL) self.add_object(self._output, 0, wx.ALL | wx.CENTER) self._output.add_object( wx.StaticText( self._output, wx.ID_ANY, 'Output World: ', wx.DefaultPosition, wx.DefaultSize, 0, ), 0, wx.ALL | wx.CENTER ) self._select_output_button = wx.Button(self, wx.ID_ANY, label='Select Output World') self._select_output_button.Bind(wx.EVT_BUTTON, self._show_world_select) self.add_object(self._select_output_button, 0, wx.ALL | wx.CENTER) self._convert_bar = SimplePanel(self, wx.HORIZONTAL) self.add_object(self._convert_bar, 0, wx.ALL | wx.CENTER) self.loading_bar = wx.Gauge( self._convert_bar, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL, ) self._convert_bar.add_object(self.loading_bar, options=wx.ALL | wx.EXPAND) self.loading_bar.SetValue(0) self.convert_button = wx.Button(self._convert_bar, wx.ID_ANY, label=lang.get('convert')) self._convert_bar.add_object(self.convert_button) self.convert_button.Bind(wx.EVT_BUTTON, self._convert_event) self.out_world_path = None def menu(self, menu: MenuData) -> MenuData: menu.setdefault('&Help', {}).setdefault('control', {}).setdefault('Controls', lambda evt: self._help_controls()) return menu def _help_controls(self): webbrowser.open("https://github.com/Amulet-Team/Amulet-Map-Editor/blob/master/amulet_map_editor/programs/convert/readme.md") def _show_world_select(self, evt): select_world = WorldSelectDialog(self, self._output_world_callback) select_world.ShowModal() def _output_world_callback(self, path): if path == self.world.world_path: wx.MessageBox( 'The input and output worlds must be different' ) return try: out_world_format = world_interface.load_format(path) self.out_world_path = path except Exception: return for child in list(self._output.GetChildren())[1:]: child.Destroy() self._output.add_object( WorldUI(self._output, out_world_format), 0 ) self._output.Layout() self._output.Fit() self.Layout() # self.Fit() def _update_loading_bar(self, chunk_index, chunk_total): wx.CallAfter(self.loading_bar.SetValue, int(100*chunk_index/chunk_total)) def _convert_event(self, evt): if self.out_world_path is None: wx.MessageBox( 'Select a world before converting' ) return self.convert_button.Disable() global work_count work_count += 1 thread_pool_executor.submit(self._convert_method) # self.world.save(self.out_world, self._update_loading_bar) def _convert_method(self): global work_count try: out_world = world_interface.load_format(self.out_world_path) log.info(f'Converting world {self.world.world_path} to {out_world.world_path}') out_world: WorldFormatWrapper out_world.open() self.world.save(out_world, self._update_loading_bar) out_world.close() message = 'World conversion completed' log.info(f'Finished converting world {self.world.world_path} to {out_world.world_path}') except Exception as e: message = f'Error during conversion\n{e}' log.error(message, exc_info=True) self._update_loading_bar(0, 100) self.convert_button.Enable() wx.MessageBox( message ) work_count -= 1 def is_closeable(self): if work_count: log.info(f'World {self.world.world_path} is still being converted. Please let it finish before closing') return work_count == 0 def _close_world(self, evt): self.GetGrandParent().GetParent().close_world(self.world.world_path)
class ConvertExtension(BaseWorldProgram): def __init__(self, container, world: World): super(ConvertExtension, self).__init__(container) self.world = world self._close_world_button = wx.Button(self, wx.ID_ANY, label='Close World') self._close_world_button.Bind(wx.EVT_BUTTON, self._close_world) self.add_object(self._close_world_button, 0, wx.ALL | wx.CENTER) self._input = SimplePanel(self, wx.HORIZONTAL) self.add_object(self._input, 0, wx.ALL | wx.CENTER) self._input.add_object( wx.StaticText( self._input, wx.ID_ANY, 'Input World: ', wx.DefaultPosition, wx.DefaultSize, 0, ), 0, wx.ALL | wx.CENTER) self._input.add_object(WorldUI(self._input, self.world.world_path), 0, wx.ALL | wx.CENTER) self._output = SimplePanel(self, wx.HORIZONTAL) self.add_object(self._output, 0, wx.ALL | wx.CENTER) self._output.add_object( wx.StaticText( self._output, wx.ID_ANY, 'Output World: ', wx.DefaultPosition, wx.DefaultSize, 0, ), 0, wx.ALL | wx.CENTER) self._select_output_button = wx.Button(self, wx.ID_ANY, label='Select Output World') self._select_output_button.Bind(wx.EVT_BUTTON, self._show_world_select) self.add_object(self._select_output_button, 0, wx.ALL | wx.CENTER) self._convert_bar = SimplePanel(self, wx.HORIZONTAL) self.add_object(self._convert_bar, 0, wx.ALL | wx.CENTER) self.loading_bar = wx.Gauge( self._convert_bar, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL, ) self._convert_bar.add_object(self.loading_bar, options=wx.ALL | wx.EXPAND) self.loading_bar.SetValue(0) self.convert_button = wx.Button(self._convert_bar, wx.ID_ANY, label=lang.get('convert')) self._convert_bar.add_object(self.convert_button) self.convert_button.Bind(wx.EVT_BUTTON, self._convert_event) self.out_world_path = None def _show_world_select(self, evt): self.Disable() WorldSelectWindow(self._output_world_callback, self.Enable) def _output_world_callback(self, path): if path == self.world.world_path: wx.MessageBox('The input and output worlds must be different') return try: out_world = world_interface.load_format(path) self.out_world_path = path except Exception: return for child in list(self._output.GetChildren())[1:]: child.Destroy() self._output.add_object(WorldUI(self._output, self.out_world_path), 0) self._output.Layout() self._output.Fit() self.Layout() # self.Fit() def _update_loading_bar(self, chunk_index, chunk_total): wx.CallAfter(self.loading_bar.SetValue, int(100 * chunk_index / chunk_total)) def _convert_event(self, evt): if self.out_world_path is None: wx.MessageBox('Select a world before converting') return self.convert_button.Disable() global work_count work_count += 1 thread_pool_executor.submit(self._convert_method) # self.world.save(self.out_world, self._update_loading_bar) def _convert_method(self): global work_count try: out_world = world_interface.load_format(self.out_world_path) log.info( f'Converting world {self.world.world_path} to {out_world.world_path}' ) out_world: Format out_world.open() self.world.save(out_world, self._update_loading_bar) out_world.close() message = 'World conversion completed' log.info( f'Finished converting world {self.world.world_path} to {out_world.world_path}' ) except Exception as e: message = f'Error during conversion\n{e}' log.error(message, exc_info=True) self._update_loading_bar(0, 100) self.convert_button.Enable() wx.MessageBox(message) work_count -= 1 def is_closeable(self): if work_count: log.info( f'World {self.world.world_path} is still being converted. Please let it finish before closing' ) return work_count == 0 def _close_world(self, evt): self.GetGrandParent().GetParent().close_world(self.world.world_path)
class BlockDefine(BlockSelect): def __init__(self, parent, translation_manager: PyMCTranslate.TranslationManager, platform: str = None, version: Tuple[int, int, int] = None, blockstate: bool = None, namespace: str = None, base_name: str = None, properties: Dict[str, str] = None, wildcard: bool = False): self._properties: List[SimplePanel] = [] self._wildcard = wildcard self._properties_panel: Optional[SimplePanel] = None super().__init__(parent, translation_manager, platform, version, blockstate, namespace, base_name) self._populate_properties(platform, version, blockstate, namespace, base_name, properties) @property def options( self ) -> Tuple[str, Tuple[int, int, int], bool, str, str, Dict[str, str]]: return self.platform, self.version, self.force_blockstate, self.namespace, self.base_name, self.properties def _populate_version(self, platform: str = None, version: Tuple[int, int, int] = None, blockstate: bool = None): pass def _populate_block(self, platform: str = None, version: Tuple[int, int, int] = None, blockstate: bool = None, namespace: str = None, base_name: str = None): pass def _populate_properties(self, platform: str = None, version: Tuple[int, int, int] = None, blockstate: bool = None, namespace: str = None, base_name: str = None, properties: Dict[str, str] = None): self._set_platform(platform=platform, version=version, blockstate=blockstate, namespace=namespace, base_name=base_name, properties=properties) def _setup_ui(self): super()._setup_ui() self._base_name_list.Bind(wx.EVT_CHOICE, self._update_properties) self._properties_panel = SimplePanel(self, wx.VERTICAL) self.add_object(self._properties_panel, 0) @property def properties(self) -> Dict[str, str]: return { prop.GetChildren()[0].GetLabel(): prop.GetChildren()[1].GetString( prop.GetChildren()[1].GetSelection()) for prop in self._properties } @property def block(self) -> Block: if self._wildcard: raise Exception( 'block property cannot be used when BlockDefine is in wildcard mode' ) else: return Block( self.namespace, self.base_name, { key: amulet_nbt.from_snbt(value) for key, value in self.properties.items() }) def _clear_properties(self): for prop in self._properties: prop.Destroy() self._properties.clear() self._properties_panel.Layout() def _add_property(self, property_name: str, property_values: List[str], default: str = None): prop_panel = SimplePanel(self._properties_panel, wx.HORIZONTAL) self._properties.append(prop_panel) self._properties_panel.add_object(prop_panel, 0) name_text = SimpleText(prop_panel, property_name) prop_panel.add_object(name_text, 0, wx.CENTER | wx.ALL) name_list = SimpleChoice(prop_panel) prop_panel.add_object(name_list, 0, wx.CENTER | wx.ALL) if self._wildcard: property_values.insert(0, "*") name_list.SetItems(property_values) if default and default in property_values: name_list.SetSelection(property_values.index(default)) else: name_list.SetSelection(0) def _update_properties(self, evt): self._set_properties() evt.Skip() def _set_properties(self, properties: Dict[str, str] = None): self._clear_properties() specification = self._translation_manager.get_version( self.platform, self.version).block.get_specification(self.namespace, self.base_name, self.force_blockstate) if properties is None: properties = {} if 'properties' in specification: for prop, options in specification['properties'].items(): self._add_property(prop, options, properties.get(prop, None)) self.Fit() self.Layout() self.GetTopLevelParent().Fit()
class EditExtension(BaseWorldProgram): def __init__(self, parent, world: 'World'): super().__init__(parent, wx.HORIZONTAL) self._world = world self._canvas: Optional[ControllableEditCanvas] = None self._temp = wx.StaticText( self, wx.ID_ANY, 'Please wait while the renderer loads', wx.DefaultPosition, wx.DefaultSize, 0, ) self._menu: Optional[SimplePanel] = None self._operation_ui: Optional[OperationUI] = None self._select_destination_ui: Optional[SelectDestinationUI] = None self._menu_buttons: List[wx.Button] = [] self._dim_options: Optional[SimpleChoiceAny] = None self._options_button: Optional[wx.Button] = None self._undo_button: Optional[wx.Button] = None self._redo_button: Optional[wx.Button] = None self._save_button: Optional[wx.Button] = None self._temp.SetFont(wx.Font(40, wx.DECORATIVE, wx.NORMAL, wx.NORMAL)) self.Bind(wx.EVT_SIZE, self._on_resize) def menu(self, menu: MenuData) -> MenuData: menu.setdefault('&Edit', {}).setdefault('control', {}).setdefault( 'Undo\tCtrl+z', lambda evt: self._world.undo()) menu.setdefault('&Edit', {}).setdefault('control', {}).setdefault( 'Redo\tCtrl+y', lambda evt: self._world.redo()) # menu.setdefault('&Edit', {}).setdefault('control', {}).setdefault('Cut', lambda evt: self.world.save()) menu.setdefault('&Edit', {}).setdefault('control', {}).setdefault('Copy\tCtrl+c', lambda evt: self._copy()) menu.setdefault('&Edit', {}).setdefault('control', {}).setdefault( 'Paste\tCtrl+v', lambda evt: self._paste()) return menu def _on_resize(self, event): if self._canvas is not None: self._canvas.SetSize(self.GetSize()[0], self.GetSize()[1]) event.Skip() def _undo_event(self, evt): self._world.undo() self._update_buttons() def _redo_event(self, evt): self._world.redo() self._update_buttons() def _update_buttons(self): self._undo_button.SetLabel( f"Undo | {self._world.chunk_history_manager.undo_count}") self._redo_button.SetLabel( f"Redo | {self._world.chunk_history_manager.redo_count}") self._save_button.SetLabel( f"Save | {self._world.chunk_history_manager.unsaved_changes}") def _save_event(self, evt): self._save_world() def _save_world(self): self._canvas.disable_threads() self._world.save() self._update_buttons() self._canvas.enable_threads() def _get_box(self) -> Optional[Selection]: box = self._canvas._selection_box # TODO: make a way to publicly access this if box.select_state == 2: return Selection((SubSelectionBox(box.min, box.max), )) else: wx.MessageBox( "You must select an area of the world before running this operation" ) return None def _enable_operation_ui(self): self._select_destination_ui.Hide() self._operation_ui.Show() self._canvas.select_mode = 0 self._menu.Fit() def _enable_select_destination_ui(self, structure: Structure): self._operation_ui.Hide() self._select_destination_ui.Show() self._menu.Fit() self._canvas.structure = structure self._canvas.select_mode = 1 def _run_operation(self, evt): operation_path = self._operation_ui.operation operation = operations.operations[operation_path] features = operation.get("features", []) operation_input_definitions = operation.get("inputs", []) if any(feature in features for feature in ("dst_location_absolute", )): if "structure_callable" in operation: operation_inputs = [] for inp in operation.get("structure_callable_inputs", []): if inp == "src_selection": selection = self._get_box() if selection is None: return operation_inputs.append(selection) elif inp == "options": operation_inputs.append( operations.options.get(operation_path, {})) self._operation_ui.Disable() self._canvas.disable_threads() try: structure = self._world.run_operation( operation["structure_callable"], self._canvas.dimension, *operation_inputs, create_undo=False) except Exception as e: wx.MessageBox(f"Error running structure operation: {e}") self._world.restore_last_undo_point() self._canvas.enable_threads() return self._canvas.enable_threads() self._operation_ui.Enable() if not isinstance(structure, Structure): wx.MessageBox( "Object returned from structure_callable was not a Structure. Aborting." ) return else: selection = self._get_box() if selection is None: return self._operation_ui.Disable() structure = Structure.from_world(self._world, selection, self._canvas.dimension) self._operation_ui.Enable() if "dst_location_absolute" in features: # trigger UI to show select box UI self._select_destination_ui.setup( operation_path, operation["operation"], operation_input_definitions, structure, operations.options.get(operation_path, {})) self._enable_select_destination_ui(structure) else: # trigger UI to show select box multiple UI raise NotImplementedError else: self._operation_ui.Disable() self._run_main_operation(operation_path, operation["operation"], operation_input_definitions) self._operation_ui.Enable() evt.Skip() def _destination_select_cancel(self): self._enable_operation_ui() def _destination_select_confirm(self, *args, **kwargs): self._select_destination_ui.Disable() self._run_main_operation(*args, **kwargs) self._select_destination_ui.Enable() self._enable_operation_ui() def _run_main_operation(self, operation_path: str, operation: Callable, operation_input_definitions: List[str], options=None, structure=None): operation_inputs = [] for inp in operation_input_definitions: if inp == "src_selection": selection = self._get_box() if selection is None: return operation_inputs.append(selection) elif inp == "structure": operation_inputs.append(structure) elif inp == "options": if options: operations.options[operation_path] = options operation_inputs.append(options) else: operation_inputs.append( operations.options.get(operation_path, {})) self._canvas.disable_threads() try: self._world.run_operation(operation, self._canvas.dimension, *operation_inputs) self._update_buttons() except Exception as e: wx.MessageBox(f"Error running operation: {e}") self._world.restore_last_undo_point() self._canvas.enable_threads() def enable(self): if self._canvas is None: self.Update() self._menu = SimplePanel(self) self._menu.Hide() self.add_object(self._menu, 0, wx.EXPAND) self._menu.Bind(wx.EVT_ENTER_WINDOW, self._steal_focus_menu) dim_label = wx.StaticText(self._menu, label="Dimension:") self._dim_options = SimpleChoiceAny(self._menu) self._dim_options.SetItems( dict( zip(self._world.world_wrapper.dimensions.values(), self._world.world_wrapper.dimensions.keys()))) self._dim_options.SetValue("overworld") self._dim_options.Bind(wx.EVT_CHOICE, self._on_dimension_change) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(dim_label, 0, wx.ALL, 5) sizer.Add(self._dim_options, 0, wx.ALL, 5) self._menu.add_object(sizer, 0) def create_button(text, operation): button = wx.Button(self._menu, label=text) button.Bind(wx.EVT_BUTTON, operation) self._menu.add_object(button, 0) self._menu_buttons.append(button) return button self._undo_button = create_button('Undo', self._undo_event) self._redo_button = create_button('Redo', self._redo_event) self._save_button = create_button('Save', self._save_event) create_button('Close', self._close_world) self._update_buttons() self._operation_ui = OperationUI(self._menu, self._world, self._run_operation) self._menu.add_object(self._operation_ui, options=0) self._operation_ui.Bind(wx.EVT_ENTER_WINDOW, self._steal_focus_operation) self._operation_ui.Layout() self._operation_ui.Fit() self._canvas = ControllableEditCanvas(self, self._world) self._select_destination_ui = SelectDestinationUI( self._menu, self._destination_select_cancel, self._destination_select_confirm, self._canvas.structure_locations) self._menu.add_object(self._select_destination_ui, options=0) self._select_destination_ui.Bind(wx.EVT_ENTER_WINDOW, self._steal_focus_destination) self._select_destination_ui.Layout() self._select_destination_ui.Fit() self._select_destination_ui.Hide() self.add_object(self._canvas, 0, wx.EXPAND) self._temp.Destroy() self._menu.Show() self.GetParent().Layout() self._menu.Layout() self._menu.Fit() self.Update() self._canvas.set_size(self.GetSize()[0], self.GetSize()[1]) self._canvas.draw() self._canvas.Update() self._canvas.enable() self._change_dimension() def disable(self): if self._canvas is not None: self._canvas.disable() def close(self): self.disable() if self._canvas is not None: self._canvas.close() def is_closeable(self): if self._canvas is not None: return self._canvas.is_closeable() and not bool( self._world.chunk_history_manager.unsaved_changes) return not bool(self._world.chunk_history_manager.unsaved_changes) def _close_world(self, _): unsaved_changes = self._world.chunk_history_manager.unsaved_changes if unsaved_changes: msg = wx.MessageDialog( self, f"There {'is' if unsaved_changes == 1 else 'are'} {unsaved_changes} unsaved change{'s' if unsaved_changes >= 2 else ''}. Would you like to save?", style=wx.YES_NO | wx.CANCEL | wx.CANCEL_DEFAULT) response = msg.ShowModal() if response == wx.ID_YES: self._save_world() elif response == wx.ID_CANCEL: return self.GetGrandParent().GetParent().close_world(self._world.world_path) def _copy(self): selection = self._get_box() if selection is None: return structure = Structure.from_world(self._world, selection, self._canvas.dimension) structure_buffer.append(structure) def _paste(self): structure = structure_buffer[-1] self._select_destination_ui.setup(None, paste, ["structure", "options"], structure, {}) self._enable_select_destination_ui(structure) def _on_dimension_change(self, evt): self._change_dimension() evt.Skip() def _change_dimension(self): dimension = self._dim_options.GetAny() self._canvas.dimension = dimension def _steal_focus_menu(self, evt): self._menu.SetFocus() evt.Skip() def _steal_focus_operation(self, evt): self._operation_ui.SetFocus() evt.Skip() def _steal_focus_destination(self, evt): self._select_destination_ui.SetFocus() evt.Skip()