def save_file(self, obj, filetype): if obj[filetype.lower()] is None: with wx.MessageDialog(self, " No {} Loaded".format(filetype), "Warning", wx.OK) as dlg: dlg.ShowModal() return with wx.FileDialog(self, "Choose a file", obj['dirname'], "", "*." + filetype.lower(), wx.FD_SAVE) as dlg: if dlg.ShowModal() == wx.ID_OK: filename = dlg.GetFilename() obj['dirname'] = dlg.GetDirectory() self.statusbar.SetStatusText("Saving...") create_backup(obj['dirname'], filename) path = os.path.join(obj['dirname'], filename) removed_nodes = obj[filetype.lower()].save(path) msg = '' if removed_nodes: msg = "The following animation nodes were removed:\n{}".format( '\n'.join( [' * ' + node for node in sorted(removed_nodes)])) self.statusbar.SetStatusText("Saved {}".format(path)) with MultiMessageDialog( self, "Saved to {} successfully".format(path), filetype + " Saved", msg, wx.OK) as saved: saved.ShowModal()
def show_multi_rename_dialog(root, obj_type, obj_list, names, selected, rename_func): with MultiRenameDialog(root) as dlg: if dlg.ShowModal() == wx.ID_OK: pattern = dlg.GetPattern() repl = dlg.GetReplace() changed_bones = '' for obj in obj_list: new_name = pattern.sub(repl, obj.name) if new_name in names and new_name != obj.name: changed_bones += ' * {} (skipping due to naming conflict)\n'.format(obj.name) elif new_name == obj.name: changed_bones += ' * {} (no change)\n'.format(obj.name) else: changed_bones += ' * {} -> {}\n'.format(obj.name, new_name) msg = "You are about to change multiple {}. Are you sure you want to do that?\n".format(obj_type) with MultiMessageDialog(root, msg, "Warning", changed_bones, wx.YES | wx.NO) as warn: if warn.ShowModal() != wx.ID_YES: return for i, obj in enumerate(obj_list): new_name = pattern.sub(repl, obj.name) if new_name in names: continue old_name, obj.name = obj.name, new_name rename_func(selected[i], obj, old_name, new_name) root.SetStatusText("Renamed {} {}".format(len(obj_list), obj_type))
def transform(self, selected, bone_index, flag, found_func, w, x, y, z): animations = [self.root.main['ean'].animations[i] for i in selected] msg = '' animations_changed = len(selected) for animation in animations: for node in animation.nodes: if node.bone_index == bone_index: for keyframed_animation in node.keyframed_animations: if keyframed_animation.flag == flag: for keyframe in keyframed_animation.keyframes: found_func(keyframe, w, x, y, z) break else: keyframed_animation = KeyframedAnimation() keyframed_animation.flag = flag keyframed_animation.keyframes.append( Keyframe(0, w, x, y, z)) keyframed_animation.keyframes.append( Keyframe(animation.frame_count - 1, w, x, y, z)) node.keyframed_animations.append(keyframed_animation) break else: msg += ' * {}\n'.format(animation.name) animations_changed -= 1 if msg: with MultiMessageDialog(self, 'Skipped animations:', "Warning", msg, wx.OK) as dlg: dlg.ShowModal() self.root.SetStatusText("Edited {} animation(s)".format(len(selected)))
def show_invalid_colors(self, invalid_colors): if not invalid_colors: return msg = "\n".join( f" * Part Color ({c.part_colors}, {c.color})" for c in sorted(invalid_colors, key=lambda x: (x.part_colors, x.color))) with MultiMessageDialog( self, 'Some color selectors were not added because they referenced invalid Part Colors', 'Warning', msg, wx.OK) as dlg: dlg.ShowModal()
def on_paste(self, _): selected = self.entry_list.GetSelections() if not selected: return success = False cdo = wx.CustomDataObject("BDMEntry") if wx.TheClipboard.Open(): success = wx.TheClipboard.GetData(cdo) wx.TheClipboard.Close() if success: paste_data = pickle.loads(cdo.GetData()) paste_length = len(paste_data) selected_length = len(selected) if selected_length > paste_length: for item in selected[paste_length:]: self.entry_list.Unselect(item) selected = selected[:paste_length] item = selected[-1] self.entry_list.Select(item) for n in range(paste_length - selected_length): item = self.entry_list.GetNextItem(item) if not item.IsOk(): with wx.MessageDialog( self, f'Not enough entries to paste over. Expected {paste_length}' ) as dlg: dlg.ShowModal() return self.entry_list.Select(item) selected.append(item) if len(selected) > 1: msg = '\n'.join([ f' * {self.entry_list.GetItemData(item).id}: Entry' for item in selected ]) with MultiMessageDialog( self, 'Are you sure you want to replace the following entries?', 'Warning', msg, wx.YES | wx.NO) as dlg: if dlg.ShowModal() != wx.ID_YES: return for n, paste in enumerate(paste_data): data = self.entry_list.GetItemData(selected[n]) data.paste(paste) self.on_select(None) pub.sendMessage('set_status_bar', text=f'Pasted {len(paste_data)} entry(s)')
def on_paste(self, _): selected = self.bone_list.GetSelections() if not selected or not self.copied_bones: return copied_bones = pickle.loads(self.copied_bones) if len(selected) > 1: with wx.MessageDialog( self, 'Only one pyxenoverse can be selected to paste over', 'Warning') as dlg: dlg.ShowModal() return temp_bone_list = {} root = selected[-1] root_bone = self.bone_list.GetItemData(root) self.bone_list.UnselectAll() all_bone_list = self.get_bone_names_index( self.bone_list.GetFirstItem()) current_bone_list = {root_bone.name: root} current_bone_list.update( self.get_bone_names_index(self.bone_list.GetFirstChild(root))) changed_bones = '' for bone in copied_bones: if bone.name in current_bone_list: changed_bones += ' * ' + bone.name + '\n' if changed_bones: msg = "You are about to change multiple bones. Are you sure you want to do that?" with MultiMessageDialog(self, msg, "Warning", changed_bones, wx.YES | wx.NO) as dlg: if dlg.ShowModal() != wx.ID_YES: return for bone in copied_bones: new_bone = Bone() new_bone.paste(bone) if new_bone.name in current_bone_list: item = current_bone_list[new_bone.name] self.bone_list.SetItemData(item, new_bone) else: new_bone = get_unique_name(new_bone, all_bone_list) if new_bone.parent_index in temp_bone_list: item = self.bone_list.AppendItem( temp_bone_list[new_bone.parent_index], '', data=new_bone) else: item = self.bone_list.AppendItem(root, '', data=new_bone) self.bone_list.Select(item) self.bone_list.Expand(item) self.bone_list.CheckItem(item) temp_bone_list[new_bone.index] = item self.recalculate_bone_tree() self.root.SetStatusText("Pasted {} bones".format(len(copied_bones)))
def on_replace_all(self, _): if not color_db.bcs: self.status_bar.SetStatusText("BCS Not Loaded") return item_type, fields = FIND_ITEM_TYPES[self.items.GetSelection()] entry_type = fields[self.entry.GetSelection()] find = self.find_ctrl.GetValue() replace = self.replace_ctrl.GetValue() if "name" not in entry_type: try: find = int(self.find_ctrl.GetValue(), 0) replace = int(self.replace_ctrl.GetValue(), 0) except ValueError: self.status_bar.SetStatusText("Invalid Value") return count = 0 skipped = 0 skipped_entries = set() item, _ = get_first_item(self.part_sets_list) while item.IsOk(): data = self.part_sets_list.GetItemData(item) res = self.replace_item(data, item_type, entry_type, find, replace, skipped_entries) if res == Replace.REPLACED: count += 1 elif res == Replace.SKIPPED: skipped += 1 item = get_next_item(self.part_sets_list, item) self.main_panel.pages["Part Sets"].on_select(None) pub.sendMessage('reindex_part_sets') msg = f'Replaced {count} entry(s) (skipped {skipped}). ' if skipped: msg += "Check your part colors" self.status_bar.SetStatusText(msg) if item_type == ColorSelector and skipped_entries: if entry_type == "part_colors": msg = "\n".join( f" * Color Selector ({cs[0]}, {cs[1]}) -> ({replace}, {cs[1]})" for cs in sorted(skipped_entries)) else: msg = "\n".join( f" * Color Selector ({cs[0]}, {cs[1]}) -> ({cs[0]}, {replace})" for cs in sorted(skipped_entries)) with MultiMessageDialog( self, f"The following Color Selectors were skipped.\n" f"Please check your part colors.", "Warning", msg, wx.OK) as dlg: dlg.ShowModal()
def exception_hook(self, e, value, trace): with MultiMessageDialog( self, '', 'Error', ''.join(traceback.format_exception(e, value, trace)), wx.OK) as dlg: dlg.ShowModal()
def on_paste(self, _): if not self.parent.copied: with wx.MessageDialog( self, f'No entries are copied from the right panel to Paste' ) as dlg: dlg.ShowModal() return selected = self.entry_list.GetSelections() if not selected: with wx.MessageDialog( self, f'No entries are selected to Paste onto') as dlg: dlg.ShowModal() return copied = pickle.loads(self.parent.copied) # Cut length of selected to match copied copy_length = len(copied) selected_length = len(selected) if selected_length > copy_length: for item in selected[copy_length:]: self.entry_list.Unselect(item) selected = selected[:copy_length] item = selected[-1] self.entry_list.Select(item) # Increase length to match selected for n in range(copy_length - selected_length): item = self.entry_list.GetNextItem(item) if not item.IsOk(): with wx.MessageDialog( self, f'Not enough entries to paste over. Expected {copy_length}' ) as dlg: dlg.ShowModal() return self.entry_list.Select(item) selected.append(item) selected_data = [ self.entry_list.GetItemData(item) for item in selected ] # Warn about changing multiple entries if len(copied) > 1: msg = '' for n, copied_data in enumerate(copied): msg += f' * {selected_data[n].index} -> {copied_data.index}\n' with MultiMessageDialog( self, 'Are you sure you want to replace the following entries?', 'Warning', msg, wx.YES | wx.NO) as dlg: if dlg.ShowModal() != wx.ID_YES: return # Paste entries selected_values = [] copied_values = [] changed_values = defaultdict(list) for n, copied_data in enumerate(copied): selected_values.append( (selected_data[n].index, selected_data[n].get_static_values())) copied_values.append(copied_data.get_static_values()) for selected_val, copied_val in zip(selected_values, copied_values): # Example: # Item type: Animation # entry: Index # dependency: Type # depend_value: 5 # entry_values: {1, 2, 3} for item_type, v1 in copied_val.items(): if item_type not in [Animation, Hitbox, Camera]: continue for entry_pair, v2 in v1.items(): for depend_value, entry_values in v2.items(): # Skip if dependency isn't a character if item_type.dependencies[entry_pair][ depend_value] != 'Character': continue if not self.get_changed_values( changed_values, item_type, entry_pair, depend_value, entry_values, selected_val, selected_data): return # Finally copy BAC Entries for n, copied_data in enumerate(copied): entry = self.entry_list.GetItemData(selected[n]) entry.paste(copied_data, self.links) # Display message msg = f'Pasted {len(copied)} entry(s)' pub.sendMessage('set_status_bar', text=msg) with ChangedDialog(self, changed_values) as dlg: dlg.ShowModal()
def on_paste(self, _): selected = list(get_selected_items(self.anim_list)) if not self.copied_animations or not selected: return copied_animations = pickle.loads(self.copied_animations) bone_list = self.root.main['ean_bone_list'] # Expand/truncate selection selected = selected[:len(copied_animations)] difference = len(copied_animations) - len(selected) if difference: last_index = selected[-1] selected.extend( list(range(last_index + 1, last_index + difference + 1))) for i in range(self.anim_list.GetItemCount()): self.anim_list.Select(i, i in selected) bone_filters = set() item = bone_list.GetFirstItem() while item.IsOk(): if bone_list.GetCheckedState(item) != wx.CHK_UNCHECKED: bone_filters.add(bone_list.GetItemData(item).name) item = bone_list.GetNextItem(item) # Warn if multiple animations are being copied if len(copied_animations) > 1: changed_animations = '' for i, animation in enumerate(copied_animations): changed_animations += ' * ' dst_index = selected[i] if dst_index < len(self.root.main['ean'].animations): changed_animations += self.root.main['ean'].animations[ dst_index].name changed_animations += ' -> {}\n'.format(animation.name) with wx.MessageDialog( self, "You are about to change multiple animations. Are you sure you want to do that?\n" + changed_animations, "Warning", wx.YES | wx.NO) as dlg: if dlg.ShowModal() != wx.ID_YES: return skipped_nodes = set() # Add missing bones bone_filters.update( self.root.main['ean_bone_panel'].add_missing_bones()) # Do the copying names = [ animation.name for animation in self.root.main['ean'].animations ] for i, copied_animation in enumerate(copied_animations): dst_index = selected[i] if dst_index < len(self.root.main['ean'].animations): animation = self.root.main['ean'].animations[dst_index] skipped_nodes.update( animation.paste(copied_animation, bone_filters, True)) else: animation = Animation(self.root.main['ean']) animation.frame_float_size = copied_animation.frame_float_size skipped_nodes.update(animation.paste(copied_animation)) animation = get_unique_name(animation, names) self.root.main['ean'].animations.append(animation) self.anim_list.InsertItem(dst_index, str(dst_index)) self.anim_list.SetItem(dst_index, 1, animation.name) self.anim_list.SetItem(dst_index, 2, str(animation.frame_count)) self.anim_list.Select(dst_index) self.reindex() pasted_msg = "Pasted {} animation(s)".format(len(copied_animations)) self.root.SetStatusText(pasted_msg) msg = '' if skipped_nodes: skipped_list = '\n'.join( [' * ' + node for node in sorted(skipped_nodes)]) msg = 'The following animation nodes were skipped:\n' + skipped_list with MultiMessageDialog(self, pasted_msg, "Warning", msg, wx.OK) as dlg: dlg.ShowModal()
def generate_xml(self, _, part_set): xml = part_set.generate_xml(color_db.bcs.part_colors) pyperclip.copy(xml) with MultiMessageDialog(self, "The generated XML has been copied to the clipboard", "Info", xml, wx.OK) as dlg: dlg.ShowModal()
def on_delete(self, _): items_to_delete = self.get_selected_root_nodes() if not items_to_delete: return for item in reversed(items_to_delete): data = self.entry_list.GetItemData(item) text = self.entry_list.GetItemText(item) parent = self.entry_list.GetItemParent(item) parent_data = self.entry_list.GetItemData(parent) index = -1 conflicts = [] # Get Index if not isinstance(data, list): index = int(text.split(':')[0]) # Delete from BCS if isinstance(data, list) and isinstance(data[0], Physics): parent_data.physics.clear() elif isinstance(data, list) and isinstance(data[0], ColorSelector): parent_data.color_selectors.clear() elif isinstance(data, PartSet): color_db.bcs.part_sets.pop(index) elif isinstance(data, Part): name = text.split(':')[1].strip().replace(' ', '_').lower() parent_data.parts.pop(name) elif isinstance(data, ColorSelector): part_set_item = self.entry_list.GetItemParent(parent) part_set = self.entry_list.GetItemData(part_set_item) part_set.color_selectors.pop(index) elif isinstance(data, Physics): part_set_item = self.entry_list.GetItemParent(parent) part_set = self.entry_list.GetItemData(part_set_item) part_set.physics.pop(index) elif isinstance(data, PartColor): conflicts = self.check_color_conflicts(index) if conflicts: msg = "\n".join([f"* Part Set {c[0]}, {c[1]}" for c in conflicts]) with MultiMessageDialog(self, f"Cannot delete Part Color {index}." "The following parts are still using it:", "Warning", msg, wx.OK) as dlg: dlg.ShowModal() else: color_db.bcs.part_colors.pop(index) color_db.pop(index) self.adjust_colors(index, delete=True) elif isinstance(data, Color): parent_text = self.entry_list.GetItemText(parent) parent_index = int(parent_text.split(':')[0]) conflicts = self.check_color_conflicts(parent_index, index) if conflicts: msg = "\n".join([f"* Part Set {c[0]}, {c[1]}" for c in conflicts]) with MultiMessageDialog(self, f"Cannot delete Part Color {parent_index}, Color {index}." "The following parts are still using it:", "Warning", msg, wx.OK) as dlg: dlg.ShowModal() else: parent_data.colors.pop(index) color_db[parent_index].pop(index) self.adjust_colors(parent_index, index, delete=True) elif isinstance(data, Body): color_db.bcs.bodies.pop(index) elif isinstance(data, BoneScale): parent_data.bone_scales.pop(index) elif isinstance(data, Skeleton): color_db.bcs.skeletons.pop(index) elif isinstance(data, Bone): parent_data.bones.pop(index) # Finally Delete from Tree if not conflicts: self.entry_list.Delete(item) pub.sendMessage(self.reindex_name) pub.sendMessage('set_status_bar', text="Deleted successfully")