def __init__(self, *args, **kwargs): BitmapDisplayFrame.__init__(self, *args, **kwargs) for w in (self.hsb, self.vsb, self.root_canvas): w.pack_forget() self.preview_label = tk.Label(self, text="Bitmap preview\t") self.image_canvas = tk.Canvas(self, highlightthickness=0) self.channel_menu = ScrollMenu(self, menu_width=9, can_scroll=True, variable=self.channel_index) self.save_button = ttk.Button(self, width=11, text="Save as...", command=self.save_as) padx = self.horizontal_padx pady = self.horizontal_pady self.image_canvas.config(width=1, height=1) self.preview_label.pack(side='left', padx=padx, pady=pady, fill='x') self.channel_menu.pack(side='left', padx=padx, pady=pady) self.save_button.pack(side='left', padx=padx, pady=pady) self.image_canvas.pack(side='left', padx=padx, pady=pady, fill='x', expand=True) self.apply_style()
def __init__(self, master, bitmap_tag=None, *args, **kwargs): self.bitmap_tag = bitmap_tag textures = kwargs.get('textures', ()) BitmapDisplayFrame.__init__(self, master, *args, **kwargs) self.sequence_index = tk.IntVar(self) self.sprite_index = tk.IntVar(self) labels = [] labels.append(tk.Label(self.controls_frame0, text="Sequence index")) labels.append(tk.Label(self.controls_frame1, text="Sprite index")) for lbl in labels: lbl.config(width=15, anchor='w') self.sequence_menu = ScrollMenu(self.controls_frame0, menu_width=7, variable=self.sequence_index) self.sprite_menu = ScrollMenu(self.controls_frame1, menu_width=7, variable=self.sprite_index) padx = self.horizontal_padx pady = self.horizontal_pady for lbl in labels: lbl.pack(side='left', padx=(15, 0), pady=pady) self.sequence_menu.pack(side='left', padx=padx, pady=pady) self.sprite_menu.pack(side='left', padx=padx, pady=pady) self.write_trace(self.sequence_index, self.sequence_changed) self.write_trace(self.sprite_index, self.sprite_changed) self.change_textures(textures) self.apply_style()
def setup_window(self, *args, **kwargs): ConverterBase.setup_window(self, *args, **kwargs) self.settings_frame = tk.LabelFrame(self, text="Conversion settings") self.from_label = tk.Label(self.settings_frame, text="from") self.src_menu = ScrollMenu( self.settings_frame, options=self.object_types, menu_width=20, sel_index=0, ) self.to_label = tk.Label(self.settings_frame, text="to") self.dst_menu = ScrollMenu( self.settings_frame, options=self.object_types, menu_width=20, sel_index=10, ) self.pack_widgets() self.apply_style()
def __init__(self, *args, **kwargs): kwargs.update(relief='flat', bd=0, highlightthickness=0) data_frame.DataFrame.__init__(self, *args, **kwargs) try: sel_index = self.node.get_index() except Exception: sel_index = -1 # make the widgets self.content = tk.Frame(self, relief='flat', bd=0) self.display_comment() self.title_label = tk.Label( self.content, text=self.gui_name, justify='left', anchor='w', width=self.title_size, disabledforeground=self.text_disabled_color) self.sel_menu = ScrollMenu(self.content, f_widget_parent=self, menu_width=self.widget_width, sel_index=sel_index, max_index=self.desc.get('ENTRIES', 0) - 1, disabled=self.disabled, default_text="<INVALID>", option_getter=self.get_options, callback=self.select_option) if self.gui_name != '': self.title_label.pack(side="left", fill="x") self.content.pack(fill="x", expand=True) self.sel_menu.pack(side="left", fill="x") self.populate() self.pose_fields() self._initialized = True
def __init__(self, *args, **kwargs): kwargs.update(relief='flat', bd=0, highlightthickness=0) data_frame.DataFrame.__init__(self, *args, **kwargs) sel_index = -1 if self.node is None else self.node + 1 # make the widgets self.content = tk.Frame(self, relief='flat', bd=0) self.title_label = tk.Label( self.content, text=self.gui_name, justify='left', anchor='w', width=self.title_size, disabledforeground=self.text_disabled_color) self.sel_menu = ScrollMenu(self.content, f_widget_parent=self, menu_width=self.widget_width, sel_index=sel_index, max_index=0, disabled=self.disabled, default_text="<INVALID>", option_getter=self.get_options, callback=self.select_option) self.sel_menu.bind('<FocusIn>', self.flag_sanity_change) self.sel_menu.arrow_button.bind('<FocusIn>', self.flag_sanity_change) self.sel_menu.bind('<Enter>', self.flag_sanity_change) self.sel_menu.arrow_button.bind('<Enter>', self.flag_sanity_change) self.sel_menu.options_volatile = 'DYN_NAME_PATH' in self.desc if self.gui_name != '': self.title_label.pack(side="left", fill="x") self.content.pack(fill="x", expand=True) self.sel_menu.pack(side="left", fill="x") self.populate() self._initialized = True
class UnionFrame(container_frame.ContainerFrame): '''Used for enumerator nodes. When clicked, creates a dropdown box of all available enumerator options.''' option_cache = None u_node_widgets_by_u_index = () def __init__(self, *args, **kwargs): field_widget.FieldWidget.__init__(self, *args, **kwargs) self.u_node_widgets_by_u_index = {} if self.f_widget_parent is None: self.pack_padx = self.pack_pady = 0 kwargs.update(relief='flat', bd=0, highlightthickness=0, bg=self.default_bg_color) show_frame = bool( kwargs.pop('show_frame', not self.blocks_start_hidden)) if self.is_empty and self.hide_if_blank: show_frame = False tk.Frame.__init__(self, *args, **e_c.fix_kwargs(**kwargs)) self.show = tk.BooleanVar(self) self.show.set(show_frame) max_u_index = len(self.desc['CASE_MAP']) u_index = getattr(self.node, "u_index", None) if u_index is None: u_index = max_u_index toggle_text = '-' if show_frame else '+' self.title = tk.Frame(self, relief='raised') self.show_btn = ttk.Checkbutton(self.title, width=3, text=toggle_text, command=self.toggle_visible, style='ShowButton.TButton') self.title_label = tk.Label(self.title, text=self.gui_name, anchor='w', width=self.title_size, justify='left') self.title_label.font_type = "frame_title" self.sel_menu = ScrollMenu(self.title, f_widget_parent=self, sel_index=u_index, max_index=max_u_index, disabled=self.disabled, callback=self.select_option, option_getter=self.get_options) self.show_btn.pack(side="left") self.title_label.pack(side="left", fill="x", expand=True) self.sel_menu.pack(side="left", fill="x") self.title.pack(fill="x", expand=True) self.content = tk.Frame(self, relief="sunken") # make the default raw bytes union frame self.raw_frame = tk.Frame(self.content, relief="flat", bd=0) self.raw_label = tk.Label(self.raw_frame, text='DataUnion', width=self.title_size, anchor='w', disabledforeground=self.text_disabled_color) self.import_btn = ttk.Button(self.raw_frame, text='Import', command=self.import_node, width=7) self.export_btn = ttk.Button(self.raw_frame, text='Export', command=self.export_node, width=7) self.raw_label.pack(side="left", expand=True, fill='x') for w in (self.export_btn, self.import_btn): w.pack(side="left", padx=(0, 4), pady=2) self.populate() self._initialized = True def apply_style(self, seen=None): container_frame.ContainerFrame.apply_style(self, seen) self.title.config(bd=self.frame_depth, bg=self.frame_bg_color) self.title_label.config(bg=self.frame_bg_color) self.content.config(bd=self.frame_depth) def load_child_node_data(self): desc = self.desc sub_node = None for wid in self.f_widgets: # try and load any existing FieldWidgets with appropriate node data w = self.f_widgets[wid] attr_index = self.f_widget_ids_map_inv.get(wid) if attr_index is None: continue elif self.node: sub_node = self.node.u_node w.load_node_data(self.node, sub_node, attr_index) return False def set_disabled(self, disable=True): disable = disable or not self.editable if self.node is None and not disable: return if bool(disable) != self.disabled and getattr(self, "sel_menu", None): self.sel_menu.set_disabled(disable) container_frame.ContainerFrame.set_disabled(self, disable) def get_options(self, opt_index=None): ''' Returns a list of the option strings sorted by option index. ''' if self.option_cache is None: return self.generate_options(opt_index) if opt_index is None: return self.option_cache elif opt_index == e_c.ACTIVE_ENUM_NAME: opt_index = getattr(self.node, "u_index", None) if opt_index is None: opt_index = len(self.option_cache) - 1 if opt_index is None: opt_index = -1 return self.option_cache.get(opt_index, None) def generate_options(self, opt_index=None): options = {i: c for c, i in self.desc['CASE_MAP'].items()} options[len(options)] = e_c.RAW_BYTES if opt_index is None: self.option_cache = options if self.sel_menu is not None: self.sel_menu.options_menu_sane = False return options return options.get(opt_index, None) def edit_apply(self=None, *, edit_state, undo=True): edit_info = edit_state.edit_info w, node = field_widget.FieldWidget.get_widget_and_node( nodepath=edit_state.nodepath, tag_window=edit_state.tag_window) if undo: node.u_index = edit_info.get('undo_u_index') u_node = edit_state.undo_node else: node.u_index = edit_info.get('redo_u_index') u_node = edit_state.redo_node if node.u_index is None: node.u_node = None node[:] = u_node else: node.u_node = u_node if w is not None: try: if w.desc is not edit_state.desc: return w.needs_flushing = False w.set_edited() w.reload() except Exception: print(format_exc()) def select_option(self, opt_index=None, force=False, reload=True): self.flush() if self.node is None: return node = self.node curr_index = self.sel_menu.sel_index if (opt_index < 0 or opt_index > self.sel_menu.max_index or opt_index is None or force): return undo_u_index = node.u_index undo_node = node.u_node if node.u_index is None: undo_node = node[:] if opt_index == self.sel_menu.max_index: # setting to rawdata self.node.set_active() else: self.node.set_active(opt_index) self.set_edited() # make an edit state if undo_u_index != node.u_index: self.edit_create( undo_u_index=undo_u_index, redo_u_index=node.u_index, redo_node=node[:] if node.u_index is None else node.u_node, undo_node=undo_node) self.sel_menu.sel_index = opt_index if reload: self.reload() def populate(self): try: old_u_node_frames = [] for u_index in self.u_node_widgets_by_u_index: # delete any existing widgets if u_index is not None: old_u_node_frames.append( self.u_node_widgets_by_u_index[u_index]) self.u_node_widgets_by_u_index.clear() self.u_node_widgets_by_u_index[None] = self.raw_frame for w in (self, self.content, self.title_label): w.tooltip_string = self.desc.get('TOOLTIP') self.display_comment(self.content) self.reload() # do things in this order to prevent the window from scrolling up for w in old_u_node_frames: w.destroy() except Exception: print(format_exc()) def reload(self): try: # clear the f_widget_ids list self.f_widget_ids = [] self.f_widget_ids_map = {} self.f_widget_ids_map_inv = {} u_index = u_node = None if self.node is not None: self.raw_label.config(text='DataUnion: %s raw bytes' % self.node.get_size()) sel_index = self.sel_menu.sel_index u_index = self.node.u_index u_node = self.node.u_node new_sel_index = (self.sel_menu.max_index if u_index is None else u_index) if new_sel_index != self.sel_menu.sel_index: self.sel_menu.sel_index = new_sel_index u_desc = self.desc.get(u_index) if hasattr(u_node, 'desc'): u_desc = u_node.desc active_widget = self.u_node_widgets_by_u_index.get(u_index) if active_widget is None: if u_index is not None: widget_cls = self.widget_picker.get_widget(u_desc) kwargs = dict(show_title=False, tag_window=self.tag_window, attr_index=u_index, disabled=self.disabled, f_widget_parent=self, desc=u_desc, show_frame=self.show.get(), dont_padx_fields=True) try: active_widget = widget_cls(self.content, **kwargs) except Exception: print(format_exc()) active_widget = data_frame.NullFrame( self.content, **kwargs) else: active_widget = self.raw_frame self.u_node_widgets_by_u_index[u_index] = active_widget wid = id(active_widget) self.f_widget_ids.append(wid) self.f_widget_ids_map[u_index] = wid self.f_widget_ids_map_inv[wid] = u_index if hasattr(active_widget, "load_node_data"): if active_widget.load_node_data(self.node, u_node, u_index, u_desc): self.populate() return active_widget.reload() active_widget.apply_style() self.build_f_widget_cache() # now that the field widgets are created, position them if self.show.get(): self.pose_fields() except Exception: print(format_exc()) self.sel_menu.update_label() if self.node is None: self.set_disabled(True) else: self.set_children_disabled(not self.node) def pose_fields(self): u_index = None if self.node is None else self.node.u_index w = self.u_node_widgets_by_u_index.get(u_index) for child in self.content.children.values(): if child not in (w, self.comment_frame): child.pack_forget() if w: # by adding a fixed amount of padding, we fix a problem # with difficult to predict padding based on nesting w.pack(fill='x', anchor='nw', padx=self.vertical_padx, pady=self.vertical_pady) self.content.pack(fill='x', anchor='nw', expand=True)
class RefineryActionsWindow(tk.Toplevel, BinillaWidget): app_root = None settings = None renamable = True propagatable = False accept_rename = None accept_settings = None tag_index_ref = None rename_string = None newtype_string = None recursive_rename = None original_name = "" def __init__(self, *args, **kwargs): title = kwargs.pop('title', None) self.renamable = kwargs.pop('renamable', self.renamable) self.settings = settings = kwargs.pop('settings', {}) self.tag_index_ref = kwargs.pop('tag_index_ref', self.tag_index_ref) # Zatarita, added propagatable kwarg for displaying checkbox self.propagatable = kwargs.pop('propagatable', self.propagatable) BinillaWidget.__init__(self, *args, **kwargs) tk.Toplevel.__init__(self, *args, **kwargs) try: self.iconbitmap(e_c.REFINERY_ICON_PATH) except Exception: if not e_c.IS_LNX: print("Could not load window icon.") self.bind('<Escape>', lambda e=None, s=self, *a, **kw: s.destroy()) if self.app_root is None and hasattr(self.master, 'app_root'): self.app_root = self.master.app_root self.accept_rename = settings.get('accept_rename', tk.IntVar(self)) self.accept_settings = settings.get('accept_settings', tk.IntVar(self)) self.rename_string = settings.get('rename_string', tk.StringVar(self)) self.newtype_string = settings.get('newtype_string', tk.StringVar(self)) self.extract_to_dir = settings.get('out_dir', tk.StringVar(self)) self.tagslist_path = settings.get('tagslist_path', tk.StringVar(self)) self.extract_mode = settings.get('extract_mode', tk.StringVar(self)) self.recursive_rename = tk.IntVar(self) self.resizable(1, 0) if title is None: title = self.rename_string.get() if not title: title = "Options" self.title(title) self.original_name = PureWindowsPath(self.rename_string.get()) if self.tag_index_ref is not None: # this ActionsWindow is displaying a single tag. the # original name will have an extension. remove it self.original_name = self.original_name.with_suffix("") self.rename_string.set(str(self.original_name)) self.newtype_string.set("") self.accept_rename.set(0) self.accept_settings.set(0) # frames self.rename_frame = tk.LabelFrame(self, text="Rename to") self.rename_frame_inner0 = tk.Frame(self.rename_frame) self.rename_frame_inner1 = tk.Frame(self.rename_frame) self.tags_list_frame = tk.LabelFrame( self, text="Tags list log(erase to disable logging)") self.extract_to_frame = tk.LabelFrame(self, text="Directory to extract to") self.settings_frame = tk.LabelFrame(self, text="Extract settings") self.button_frame = tk.Frame(self) self.accept_frame = tk.Frame(self.button_frame) self.cancel_frame = tk.Frame(self.button_frame) # rename self.rename_entry = tk.Entry(self.rename_frame_inner0, width=50, textvariable=self.rename_string) self.rename_button = tk.Button(self.rename_frame_inner0, text="Rename", command=self.rename, width=6) self.class_scroll_menu = ScrollMenu(self.rename_frame_inner1, menu_width=35) self.recursive_rename_cbtn = tk.Checkbutton( self.rename_frame_inner1, text="Recursive", variable=self.recursive_rename) if self.tag_index_ref: # populate the class_scroll_menu options opts = sorted([n for n in self.tag_index_ref.class_1.NAME_MAP]) self.class_scroll_menu.set_options(opts) try: self.class_scroll_menu.sel_index = opts.index( self.tag_index_ref.class_1.enum_name) except ValueError: pass # tags list self.tags_list_entry = tk.Entry(self.tags_list_frame, width=50, textvariable=self.tagslist_path) self.browse_tags_list_button = tk.Button(self.tags_list_frame, text="Browse", command=self.tags_list_browse) # extract to dir self.extract_to_entry = tk.Entry(self.extract_to_frame, width=50, textvariable=self.extract_to_dir) self.browse_extract_to_button = tk.Button( self.extract_to_frame, text="Browse", command=self.extract_to_browse) # settings self.recursive_cbtn = tk.Checkbutton(self.settings_frame, text="Recursive extraction", variable=settings.get( "recursive", tk.IntVar(self))) self.overwrite_cbtn = tk.Checkbutton( self.settings_frame, text="Overwrite tags(not recommended)", variable=settings.get("overwrite", tk.IntVar(self))) self.do_printout_cbtn = tk.Checkbutton( self.settings_frame, text="Print extracted tag names", variable=settings.get("do_printout", tk.IntVar(self))) # zatarita added do_propegate_settings to place the check box in frame, and link value to variable self.do_propegate_settings = tk.Checkbutton( self.settings_frame, text="Use these settings for each item", variable=settings.get("propagate_settings", tk.IntVar(self))) # accept/cancel self.accept_button = tk.Button(self.accept_frame, text="Add to queue", command=self.add_to_queue, width=14) self.cancel_button = tk.Button(self.cancel_frame, text="Cancel", command=self.destroy, width=14) self.show_meta_button = tk.Button(self, text="Display metadata", command=self.show_meta) # pack everything # frames if self.renamable: self.rename_frame.pack(padx=4, pady=2, expand=True, fill="x") self.rename_frame_inner0.pack(expand=True, fill="x") self.rename_frame_inner1.pack(expand=True, fill="x") self.tags_list_frame.pack(padx=4, pady=2, expand=True, fill="x") self.extract_to_frame.pack(padx=4, pady=2, expand=True, fill="x") self.settings_frame.pack(padx=4, pady=2, expand=True, fill="x") self.button_frame.pack(pady=2, expand=True, fill="x") self.accept_frame.pack(padx=4, side='left', fill='x', expand=True) self.cancel_frame.pack(padx=4, side='right', fill='x', expand=True) # rename self.rename_entry.pack(padx=4, side='left', fill='x', expand=True) self.rename_button.pack(padx=4, side='left', fill='x') if self.tag_index_ref: self.class_scroll_menu.pack(padx=4, side='left', fill='x') #self.recursive_rename_cbtn.pack(padx=4, side='left', fill='x') # extract to self.extract_to_entry.pack(padx=4, side='left', fill='x', expand=True) self.browse_extract_to_button.pack(padx=4, side='left', fill='x') # tags list self.tags_list_entry.pack(padx=4, side='left', fill='x', expand=True) self.browse_tags_list_button.pack(padx=4, side='left', fill='x') # settings # zatarita, added propagatable check box to top of settings frame. (if enabled) if self.propagatable: self.do_propegate_settings.pack(padx=4, anchor='w') self.recursive_cbtn.pack(padx=4, anchor='w') self.overwrite_cbtn.pack(padx=4, anchor='w') self.do_printout_cbtn.pack(padx=4, anchor='w') # accept/cancel self.accept_button.pack(side='right') self.cancel_button.pack(side='left') if self.tag_index_ref is not None: self.show_meta_button.pack(padx=4, pady=4, expand=True, fill='x') # make the window not show up on the start bar self.transient(self.master) self.wait_visibility() self.lift() self.grab_set() self.apply_style() try: self.update() self.app_root.place_window_relative(self) # I would use focus_set, but it doesn't seem to always work self.accept_button.focus_force() except AttributeError: pass def apply_style(self, seen=None): BinillaWidget.apply_style(self, seen) self.update() w, h = self.winfo_reqwidth(), self.winfo_reqheight() self.geometry("%sx%s" % (w, h)) self.minsize(width=w, height=h) def add_to_queue(self, e=None): self.accept_settings.set(1) self.destroy() def rename(self, e=None): if not self.renamable: return new_name = self.rename_string.get() new_name = str(PureWindowsPath(new_name)).lower().strip(".") if self.tag_index_ref is None and new_name: # directory of tags new_name += "\\" old_class = new_class = None try: old_class = self.tag_index_ref.class_1.enum_name except Exception: pass try: new_class = self.class_scroll_menu.get_option() except Exception: new_class = "" #new_name = os.path.splitext(new_name)[0] self.rename_string.set(new_name) str_len = len(new_name) if str_len > MAX_TAG_NAME_LEN: messagebox.showerror( "Max name length exceeded", ("The max length for a tag is limited to %s characters\n" + "Remove %s characters(excluding extension).") % (MAX_TAG_NAME_LEN, str_len - MAX_TAG_NAME_LEN), parent=self) return elif is_protected_tag(new_name): messagebox.showerror("Invalid name", "The entered string is not a valid filename.", parent=self) return elif not str_len and self.tag_index_ref is not None: messagebox.showerror("Invalid name", "The entered string cannot be empty.", parent=self) return self.accept_rename.set(1) # change the type if applicable if new_class and new_class != old_class: self.newtype_string.set(new_class) self.destroy() def tags_list_browse(self): try: init_dir = os.path.dirname(self.tagslist_path.get()) except Exception: init_dir = None dirpath = asksaveasfilename( initialdir=init_dir, parent=self, title="Select where to save the tag list log", filetypes=(("text log", "*.txt"), ("All", "*"))) if not dirpath: return self.tagslist_path.set(str(Path(dirpath))) def extract_to_browse(self): dirpath = askdirectory(initialdir=self.extract_to_dir.get(), parent=self, title="Select the directory to extract tags to") if not dirpath: return self.extract_to_dir.set(str(Path(dirpath))) def show_meta(self): index_ref = self.tag_index_ref if not index_ref: return try: halo_map = self.settings.get("halo_map") if halo_map is None: print("Could not get map.") return disable_safe_mode = getattr(self.app_root, "disable_safe_mode") disable_tag_cleaning = getattr(self.app_root, "disable_tag_cleaning") meta = halo_map.get_meta( index_ref.id & 0xFFff, True, allow_corrupt=self.settings["allow_corrupt"].get(), disable_safe_mode=disable_safe_mode, disable_tag_cleaning=disable_tag_cleaning) if meta is None: print("Could not get meta.") return meta_tag = meta_tag_def.build() meta_tag.data.tagdata = meta tag_path = index_ref.path meta_tag.filepath = tag_path if index_ref.class_1.enum_name not in BAD_CLASSES: ext = ".%s" % index_ref.class_1.enum_name else: ext = ".INVALID" w = MetaWindow(self.app_root, meta_tag, engine=halo_map.engine, tag_path=tag_path + ext) self.destroy() w.focus_set() except Exception: print(format_exc()) return
def __init__(self, *args, **kwargs): title = kwargs.pop('title', None) self.renamable = kwargs.pop('renamable', self.renamable) self.settings = settings = kwargs.pop('settings', {}) self.tag_index_ref = kwargs.pop('tag_index_ref', self.tag_index_ref) # Zatarita, added propagatable kwarg for displaying checkbox self.propagatable = kwargs.pop('propagatable', self.propagatable) BinillaWidget.__init__(self, *args, **kwargs) tk.Toplevel.__init__(self, *args, **kwargs) try: self.iconbitmap(e_c.REFINERY_ICON_PATH) except Exception: if not e_c.IS_LNX: print("Could not load window icon.") self.bind('<Escape>', lambda e=None, s=self, *a, **kw: s.destroy()) if self.app_root is None and hasattr(self.master, 'app_root'): self.app_root = self.master.app_root self.accept_rename = settings.get('accept_rename', tk.IntVar(self)) self.accept_settings = settings.get('accept_settings', tk.IntVar(self)) self.rename_string = settings.get('rename_string', tk.StringVar(self)) self.newtype_string = settings.get('newtype_string', tk.StringVar(self)) self.extract_to_dir = settings.get('out_dir', tk.StringVar(self)) self.tagslist_path = settings.get('tagslist_path', tk.StringVar(self)) self.extract_mode = settings.get('extract_mode', tk.StringVar(self)) self.recursive_rename = tk.IntVar(self) self.resizable(1, 0) if title is None: title = self.rename_string.get() if not title: title = "Options" self.title(title) self.original_name = PureWindowsPath(self.rename_string.get()) if self.tag_index_ref is not None: # this ActionsWindow is displaying a single tag. the # original name will have an extension. remove it self.original_name = self.original_name.with_suffix("") self.rename_string.set(str(self.original_name)) self.newtype_string.set("") self.accept_rename.set(0) self.accept_settings.set(0) # frames self.rename_frame = tk.LabelFrame(self, text="Rename to") self.rename_frame_inner0 = tk.Frame(self.rename_frame) self.rename_frame_inner1 = tk.Frame(self.rename_frame) self.tags_list_frame = tk.LabelFrame( self, text="Tags list log(erase to disable logging)") self.extract_to_frame = tk.LabelFrame(self, text="Directory to extract to") self.settings_frame = tk.LabelFrame(self, text="Extract settings") self.button_frame = tk.Frame(self) self.accept_frame = tk.Frame(self.button_frame) self.cancel_frame = tk.Frame(self.button_frame) # rename self.rename_entry = tk.Entry(self.rename_frame_inner0, width=50, textvariable=self.rename_string) self.rename_button = tk.Button(self.rename_frame_inner0, text="Rename", command=self.rename, width=6) self.class_scroll_menu = ScrollMenu(self.rename_frame_inner1, menu_width=35) self.recursive_rename_cbtn = tk.Checkbutton( self.rename_frame_inner1, text="Recursive", variable=self.recursive_rename) if self.tag_index_ref: # populate the class_scroll_menu options opts = sorted([n for n in self.tag_index_ref.class_1.NAME_MAP]) self.class_scroll_menu.set_options(opts) try: self.class_scroll_menu.sel_index = opts.index( self.tag_index_ref.class_1.enum_name) except ValueError: pass # tags list self.tags_list_entry = tk.Entry(self.tags_list_frame, width=50, textvariable=self.tagslist_path) self.browse_tags_list_button = tk.Button(self.tags_list_frame, text="Browse", command=self.tags_list_browse) # extract to dir self.extract_to_entry = tk.Entry(self.extract_to_frame, width=50, textvariable=self.extract_to_dir) self.browse_extract_to_button = tk.Button( self.extract_to_frame, text="Browse", command=self.extract_to_browse) # settings self.recursive_cbtn = tk.Checkbutton(self.settings_frame, text="Recursive extraction", variable=settings.get( "recursive", tk.IntVar(self))) self.overwrite_cbtn = tk.Checkbutton( self.settings_frame, text="Overwrite tags(not recommended)", variable=settings.get("overwrite", tk.IntVar(self))) self.do_printout_cbtn = tk.Checkbutton( self.settings_frame, text="Print extracted tag names", variable=settings.get("do_printout", tk.IntVar(self))) # zatarita added do_propegate_settings to place the check box in frame, and link value to variable self.do_propegate_settings = tk.Checkbutton( self.settings_frame, text="Use these settings for each item", variable=settings.get("propagate_settings", tk.IntVar(self))) # accept/cancel self.accept_button = tk.Button(self.accept_frame, text="Add to queue", command=self.add_to_queue, width=14) self.cancel_button = tk.Button(self.cancel_frame, text="Cancel", command=self.destroy, width=14) self.show_meta_button = tk.Button(self, text="Display metadata", command=self.show_meta) # pack everything # frames if self.renamable: self.rename_frame.pack(padx=4, pady=2, expand=True, fill="x") self.rename_frame_inner0.pack(expand=True, fill="x") self.rename_frame_inner1.pack(expand=True, fill="x") self.tags_list_frame.pack(padx=4, pady=2, expand=True, fill="x") self.extract_to_frame.pack(padx=4, pady=2, expand=True, fill="x") self.settings_frame.pack(padx=4, pady=2, expand=True, fill="x") self.button_frame.pack(pady=2, expand=True, fill="x") self.accept_frame.pack(padx=4, side='left', fill='x', expand=True) self.cancel_frame.pack(padx=4, side='right', fill='x', expand=True) # rename self.rename_entry.pack(padx=4, side='left', fill='x', expand=True) self.rename_button.pack(padx=4, side='left', fill='x') if self.tag_index_ref: self.class_scroll_menu.pack(padx=4, side='left', fill='x') #self.recursive_rename_cbtn.pack(padx=4, side='left', fill='x') # extract to self.extract_to_entry.pack(padx=4, side='left', fill='x', expand=True) self.browse_extract_to_button.pack(padx=4, side='left', fill='x') # tags list self.tags_list_entry.pack(padx=4, side='left', fill='x', expand=True) self.browse_tags_list_button.pack(padx=4, side='left', fill='x') # settings # zatarita, added propagatable check box to top of settings frame. (if enabled) if self.propagatable: self.do_propegate_settings.pack(padx=4, anchor='w') self.recursive_cbtn.pack(padx=4, anchor='w') self.overwrite_cbtn.pack(padx=4, anchor='w') self.do_printout_cbtn.pack(padx=4, anchor='w') # accept/cancel self.accept_button.pack(side='right') self.cancel_button.pack(side='left') if self.tag_index_ref is not None: self.show_meta_button.pack(padx=4, pady=4, expand=True, fill='x') # make the window not show up on the start bar self.transient(self.master) self.wait_visibility() self.lift() self.grab_set() self.apply_style() try: self.update() self.app_root.place_window_relative(self) # I would use focus_set, but it doesn't seem to always work self.accept_button.focus_force() except AttributeError: pass
def __init__(self, *args, **kwargs): kwargs.update(relief='flat', bd=0, highlightthickness=0, bg=self.default_bg_color) field_widget.FieldWidget.__init__(self, *args, **kwargs) tk.Frame.__init__(self, *args, **e_c.fix_kwargs(**kwargs)) show_frame = bool( kwargs.pop('show_frame', not self.blocks_start_hidden)) if self.is_empty and self.hide_if_blank: show_frame = False self.show = tk.BooleanVar() self.show.set(show_frame) self.options_sane = False node_len = 0 try: node_len = len(self.node) except Exception: pass self.sel_index = (node_len > 0) - 1 # make the title, element menu, and all the buttons self.controls = tk.Frame(self, relief='raised', bd=self.frame_depth) self.title = title = tk.Frame(self.controls, relief='flat', bd=0) self.buttons = buttons = tk.Frame(self.controls, relief='flat', bd=0) toggle_text = '-' if show_frame else '+' self.title_label = tk.Label( title, text=self.gui_name, justify='left', anchor='w', width=self.title_size, font=self.get_font("frame_title"), disabledforeground=self.text_disabled_color) self.title_label.font_type = "frame_title" self.show_btn = ttk.Checkbutton(title, width=3, text=toggle_text, command=self.toggle_visible, style='ShowButton.TButton') self.sel_menu = ScrollMenu(title, f_widget_parent=self, sel_index=self.sel_index, max_index=node_len - 1, option_getter=self.get_options, callback=self.select_option) self.shift_up_btn = ttk.Button(title, width=7, text='Shift ▲', command=self.shift_entry_up) self.shift_down_btn = ttk.Button(buttons, width=7, text='Shift ▼', command=self.shift_entry_down) self.add_btn = ttk.Button(buttons, width=4, text='Add', command=self.add_entry) self.insert_btn = ttk.Button(buttons, width=6, text='Insert', command=self.insert_entry) self.duplicate_btn = ttk.Button(buttons, width=9, text='Duplicate', command=self.duplicate_entry) self.delete_btn = ttk.Button(buttons, width=6, text='Delete', command=self.delete_entry) self.delete_all_btn = ttk.Button(buttons, width=10, text='Delete all', command=self.delete_all_entries) self.import_btn = ttk.Button(buttons, width=6, text='Import', command=self.import_node) self.export_btn = ttk.Button(buttons, width=6, text='Export', command=self.export_node) # pack the title, menu, and all the buttons for w in (self.shift_down_btn, self.export_btn, self.import_btn, self.delete_all_btn, self.delete_btn, self.duplicate_btn, self.insert_btn, self.add_btn): w.pack(side="right", padx=(0, 4), pady=(2, 2)) self.show_btn.pack(side="left") if self.gui_name != '': self.title_label.pack(side="left", fill="x", expand=True) self.sel_menu.pack(side="left", fill="x", expand=True, padx=(0, 4)) self.shift_up_btn.pack(side="right", padx=(0, 1), pady=(2, 2)) self.title.pack(fill="x", expand=True, padx=0) self.buttons.pack(fill="x", expand=True, padx=0) self.controls.pack(fill="x", expand=True, padx=0) self.populate() self._initialized = True
class ModelCompilerWindow(window_base_class, BinillaWidget): app_root = None tags_dir = '' jms_models = () merged_jms = None mod2_tag = None shader_paths = () shader_types = () _compiling = False _loading = False _saving = False _editing_shader_path = False _jms_tree_iids = () def __init__(self, app_root, *args, **kwargs): if window_base_class == tk.Toplevel: kwargs.update(bd=0, highlightthickness=0, bg=self.default_bg_color) self.app_root = app_root else: self.app_root = self window_base_class.__init__(self, app_root, *args, **kwargs) BinillaWidget.__init__(self, *args, **kwargs) self.title("Gbxmodel compiler") self.resizable(1, 1) self.update() try: self.iconbitmap(e_c.MOZZ_ICON_PATH) except Exception: print("Could not load window icon.") self.superhigh_lod_cutoff = tk.StringVar(self) self.high_lod_cutoff = tk.StringVar(self) self.medium_lod_cutoff = tk.StringVar(self) self.low_lod_cutoff = tk.StringVar(self) self.superlow_lod_cutoff = tk.StringVar(self) self.shader_path_string_var = tk.StringVar(self) tags_dir = getattr(app_root, "tags_dir", "") self.optimize_level = tk.IntVar(self) self.tags_dir = tk.StringVar(self, tags_dir if tags_dir else "") self.jms_dir = tk.StringVar(self) self.gbxmodel_path = tk.StringVar(self) # make the frames self.main_frame = tk.Frame(self) self.jms_info_frame = tk.LabelFrame(self, text="Model info") self.dirs_frame = tk.LabelFrame(self.main_frame, text="Directories") self.buttons_frame = tk.Frame(self.main_frame) self.settings_frame = tk.LabelFrame(self.main_frame, text="Compilation settings") self.jms_dir_frame = tk.LabelFrame(self.dirs_frame, text="Source models folder") self.tags_dir_frame = tk.LabelFrame(self.dirs_frame, text="Tags directory root folder") self.gbxmodel_path_frame = tk.LabelFrame(self.dirs_frame, text="Gbxmodel output path") self.lods_frame = tk.LabelFrame(self.settings_frame, text="LOD cutoffs") self.shaders_frame = tk.LabelFrame(self.settings_frame, text="Shaders") self.optimize_label = tk.Label( self.settings_frame, justify="right", text=("Vertex optimization\n(Set before loading)")) self.optimize_menu = ScrollMenu(self.settings_frame, menu_width=5, options=("None", "Exact", "Loose")) self.optimize_menu.sel_index = 1 self.jms_info_tree = tk.ttk.Treeview(self.jms_info_frame, selectmode='browse', padding=(0, 0), height=4) self.jms_info_vsb = tk.Scrollbar(self.jms_info_frame, orient='vertical', command=self.jms_info_tree.yview) self.jms_info_hsb = tk.Scrollbar(self.jms_info_frame, orient='horizontal', command=self.jms_info_tree.xview) self.jms_info_tree.config(yscrollcommand=self.jms_info_vsb.set, xscrollcommand=self.jms_info_hsb.set) self.shader_names_menu = ScrollMenu( self.shaders_frame, menu_width=10, callback=self.select_shader, option_getter=self.get_shader_names, options_volatile=True) self.shader_types_menu = ScrollMenu(self.shaders_frame, menu_width=20, options=shader_types, callback=self.select_shader_type) self.shader_path_browse_button = tk.Button( self.shaders_frame, text="Browse", width=6, command=self.browse_shader_path) self.shader_path_entry = tk.Entry( self.shaders_frame, textvariable=self.shader_path_string_var) self.write_trace(self.shader_path_string_var, self.shader_path_edited) self.superhigh_lod_label = tk.Label(self.lods_frame, text="Superhigh") self.high_lod_label = tk.Label(self.lods_frame, text="High") self.medium_lod_label = tk.Label(self.lods_frame, text="Medium") self.low_lod_label = tk.Label(self.lods_frame, text="Low") self.superlow_lod_label = tk.Label(self.lods_frame, text="Superlow") self.superhigh_lod_cutoff_entry = tk.Entry( self.lods_frame, textvariable=self.superhigh_lod_cutoff, width=6, justify='right') self.high_lod_cutoff_entry = tk.Entry( self.lods_frame, textvariable=self.high_lod_cutoff, width=6, justify='right') self.medium_lod_cutoff_entry = tk.Entry( self.lods_frame, textvariable=self.medium_lod_cutoff, width=6, justify='right') self.low_lod_cutoff_entry = tk.Entry(self.lods_frame, textvariable=self.low_lod_cutoff, width=6, justify='right') self.superlow_lod_cutoff_entry = tk.Entry( self.lods_frame, textvariable=self.superlow_lod_cutoff, width=6, justify='right') self.jms_dir_entry = tk.Entry(self.jms_dir_frame, textvariable=self.jms_dir, state=tk.DISABLED) self.jms_dir_browse_button = tk.Button(self.jms_dir_frame, text="Browse", command=self.jms_dir_browse) self.tags_dir_entry = tk.Entry(self.tags_dir_frame, textvariable=self.tags_dir, state=tk.DISABLED) self.tags_dir_browse_button = tk.Button(self.tags_dir_frame, text="Browse", command=self.tags_dir_browse) self.gbxmodel_path_entry = tk.Entry(self.gbxmodel_path_frame, textvariable=self.gbxmodel_path, state=tk.DISABLED) self.gbxmodel_path_browse_button = tk.Button( self.gbxmodel_path_frame, text="Browse", command=self.gbxmodel_path_browse) self.load_button = tk.Button(self.buttons_frame, text="Load\nmodels", command=self.load_models) self.save_button = tk.Button(self.buttons_frame, text="Save as JMS", command=self.save_models) self.compile_button = tk.Button(self.buttons_frame, text="Compile\ngbxmodel", command=self.compile_gbxmodel) self.populate_model_info_tree() # pack everything self.main_frame.pack(fill="both", side='left', pady=3, padx=3) self.jms_info_frame.pack(fill="both", side='left', pady=3, padx=3, expand=True) self.dirs_frame.pack(fill="x") self.buttons_frame.pack(fill="x", pady=3, padx=3) self.settings_frame.pack(fill="both", expand=True) self.superhigh_lod_label.grid(sticky='e', row=0, column=0, padx=3, pady=1) self.high_lod_label.grid(sticky='e', row=1, column=0, padx=3, pady=1) self.medium_lod_label.grid(sticky='e', row=2, column=0, padx=3, pady=1) self.low_lod_label.grid(sticky='e', row=3, column=0, padx=3, pady=1) self.superlow_lod_label.grid(sticky='e', row=4, column=0, padx=3, pady=1) self.superhigh_lod_cutoff_entry.grid(sticky='ew', row=0, column=1, padx=3, pady=1) self.high_lod_cutoff_entry.grid(sticky='ew', row=1, column=1, padx=3, pady=1) self.medium_lod_cutoff_entry.grid(sticky='ew', row=2, column=1, padx=3, pady=1) self.low_lod_cutoff_entry.grid(sticky='ew', row=3, column=1, padx=3, pady=1) self.superlow_lod_cutoff_entry.grid(sticky='ew', row=4, column=1, padx=3, pady=1) self.jms_dir_frame.pack(expand=True, fill='x') self.tags_dir_frame.pack(expand=True, fill='x') self.gbxmodel_path_frame.pack(expand=True, fill='x') self.jms_dir_entry.pack(side='left', expand=True, fill='x') self.jms_dir_browse_button.pack(side='left') self.gbxmodel_path_entry.pack(side='left', expand=True, fill='x') self.gbxmodel_path_browse_button.pack(side='left') self.tags_dir_entry.pack(side='left', expand=True, fill='x') self.tags_dir_browse_button.pack(side='left') self.optimize_label.grid(sticky='ne', row=3, column=1, padx=3) self.optimize_menu.grid(sticky='new', row=3, column=2, padx=3, pady=(3, 0)) self.lods_frame.grid(sticky='ne', row=0, column=3, rowspan=4) self.shaders_frame.grid(sticky='nsew', row=0, column=0, columnspan=3, rowspan=3, pady=(0, 3)) self.shader_names_menu.grid(sticky='new', row=0, column=0, padx=3, columnspan=5, pady=2) self.shader_path_browse_button.grid(sticky='ne', row=1, column=4, padx=3, pady=2) self.shader_types_menu.grid(sticky='new', row=1, column=1, padx=3, columnspan=3, pady=2) self.shader_path_entry.grid(sticky='new', row=2, column=0, padx=3, columnspan=5, pady=2) self.jms_info_hsb.pack(side="bottom", fill='x') self.jms_info_vsb.pack(side="right", fill='y') self.jms_info_tree.pack(side='left', fill='both', expand=True) self.load_button.pack(side='left', expand=True, fill='both', padx=3) self.save_button.pack(side='left', expand=True, fill='both', padx=3) self.compile_button.pack(side='right', expand=True, fill='both', padx=3) self.apply_style() if self.app_root is not self: self.transient(self.app_root) def populate_model_info_tree(self): jms_tree = self.jms_info_tree if not jms_tree['columns']: jms_tree['columns'] = ('data', ) jms_tree.heading("#0") jms_tree.heading("data") jms_tree.column("#0", minwidth=100, width=100) jms_tree.column("data", minwidth=50, width=50, stretch=False) for iid in self._jms_tree_iids: jms_tree.delete(iid) self._jms_tree_iids = [] if not self.jms_models or not self.merged_jms: return nodes_iid = jms_tree.insert('', 'end', text="Nodes", tags=('item', ), values=(len(self.merged_jms.nodes), )) self._jms_tree_iids.append(nodes_iid) nodes = self.merged_jms.nodes for node in nodes: iid = jms_tree.insert(nodes_iid, 'end', text=node.name, tags=('item', )) parent_name = child_name = sibling_name = "NONE" if node.parent_index >= 0: parent_name = nodes[node.parent_index].name if node.sibling_index >= 0: sibling_name = nodes[node.sibling_index].name if node.first_child >= 0: child_name = nodes[node.first_child].name jms_tree.insert( iid, 'end', text="Parent", values=(parent_name, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="First child", values=(child_name, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="Next sibling", values=(sibling_name, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="i", values=(node.rot_i, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="j", values=(node.rot_j, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="k", values=(node.rot_k, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="w", values=(node.rot_w, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="x", values=(node.pos_x, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="y", values=(node.pos_y, ), tags=('item', ), ) jms_tree.insert( iid, 'end', text="z", values=(node.pos_z, ), tags=('item', ), ) mats_iid = jms_tree.insert('', 'end', text="Materials", tags=('item', ), values=(len(self.merged_jms.materials), )) self._jms_tree_iids.append(mats_iid) for mat in self.merged_jms.materials: jms_tree.insert(mats_iid, 'end', text=mat.name, tags=('item', ), values=(mat.tiff_path, )) regions = list(sorted(self.merged_jms.regions)) regions_iid = jms_tree.insert('', 'end', text="Regions", tags=('item', ), values=(len(regions), )) self._jms_tree_iids.append(regions_iid) for region in regions: jms_tree.insert( regions_iid, 'end', text=region, tags=('item', ), ) geoms_iid = jms_tree.insert('', 'end', text="Geometries", tags=('item', ), values=(len(self.jms_models), )) self._jms_tree_iids.append(geoms_iid) for jms_model in self.jms_models: iid = jms_tree.insert(geoms_iid, 'end', tags=('item', ), text=jms_model.name) jms_tree.insert(iid, 'end', text="Vertex count", tags=('item', ), values=(len(jms_model.verts), )) jms_tree.insert(iid, 'end', text="Triangle count", tags=('item', ), values=(len(jms_model.tris), )) markers_iid = jms_tree.insert(iid, 'end', text="Markers", tags=('item', ), values=(len(jms_model.markers), )) for marker in jms_model.markers: iid = jms_tree.insert(markers_iid, 'end', tags=('item', ), text=marker.name) perm_name = marker.permutation region_name = regions[marker.region] parent_name = "" if marker.parent >= 0: parent_name = nodes[marker.parent].name jms_tree.insert(iid, 'end', text="Permutation", values=(perm_name, ), tags=('item', )) jms_tree.insert(iid, 'end', text="Region", values=(region_name, ), tags=('item', )) jms_tree.insert(iid, 'end', text="Parent", values=(parent_name, ), tags=('item', )) jms_tree.insert(iid, 'end', text="Radius", values=(marker.radius, ), tags=('item', )) jms_tree.insert(iid, 'end', text="i", values=(marker.rot_i, ), tags=('item', )) jms_tree.insert(iid, 'end', text="j", values=(marker.rot_j, ), tags=('item', )) jms_tree.insert(iid, 'end', text="k", values=(marker.rot_k, ), tags=('item', )) jms_tree.insert(iid, 'end', text="w", values=(marker.rot_w, ), tags=('item', )) jms_tree.insert(iid, 'end', text="x", values=(marker.pos_x, ), tags=('item', )) jms_tree.insert(iid, 'end', text="y", values=(marker.pos_y, ), tags=('item', )) jms_tree.insert(iid, 'end', text="z", values=(marker.pos_z, ), tags=('item', )) def jms_dir_browse(self): if self._compiling or self._loading or self._saving: return tags_dir = self.tags_dir.get() data_dir = path_replace(tags_dir, "tags", "data") jms_dir = self.jms_dir.get() if tags_dir and not jms_dir: jms_dir = data_dir dirpath = askdirectory( initialdir=jms_dir, parent=self, title="Select the folder of models to compile...") if not dirpath: return dirpath = str(Path(dirpath)) if tags_dir and data_dir and os.path.basename( dirpath).lower() == "models": object_dir = os.path.dirname(dirpath) if object_dir and is_in_dir(object_dir, data_dir): tag_path = os.path.join(object_dir, os.path.basename(object_dir)) tag_path = os.path.join(tags_dir, os.path.relpath(tag_path, data_dir)) self.gbxmodel_path.set(tag_path + ".gbxmodel") self.app_root.last_load_dir = os.path.dirname(dirpath) self.jms_dir.set(dirpath) if not self.tags_dir.get(): self.tags_dir.set( Path(path_split(self.app_root.last_load_dir, "data"), "tags")) def tags_dir_browse(self): if self._compiling or self._loading or self._saving: return old_tags_dir = self.tags_dir.get() tags_dir = askdirectory(initialdir=old_tags_dir, parent=self, title="Select the root of the tags directory") if not tags_dir: return tags_dir = path_normalize(tags_dir) mod2_path = self.gbxmodel_path.get() if old_tags_dir and mod2_path and not is_in_dir(mod2_path, tags_dir): # adjust mod2 filepath to be relative to the new tags directory mod2_path = os.path.join(tags_dir, os.path.relpath(mod2_path, old_tags_dir)) self.gbxmodel_path.set(mod2_path) self.app_root.last_load_dir = os.path.dirname(tags_dir) self.tags_dir.set(tags_dir) def gbxmodel_path_browse(self, force=False): if not force and (self._compiling or self._loading or self._saving): return mod2_dir = os.path.dirname(self.gbxmodel_path.get()) if self.tags_dir.get() and not mod2_dir: mod2_dir = self.tags_dir.get() fp = asksaveasfilename(initialdir=mod2_dir, title="Save gbxmodel to...", parent=self, filetypes=(("Gearbox model", "*.gbxmodel"), ('All', '*'))) if not fp: return fp = Path(fp).with_suffix(".gbxmodel") self.app_root.last_load_dir = str(fp.parent) self.gbxmodel_path.set(str(fp)) self.tags_dir.set( path_split(self.app_root.last_load_dir, "tags", after=True)) def apply_style(self, seen=None): BinillaWidget.apply_style(self, seen) self.update() w, h = self.winfo_reqwidth(), self.winfo_reqheight() self.geometry("%sx%s" % (w, h)) self.minsize(width=w, height=h) def get_material(self, index): if not isinstance(self.merged_jms, MergedJmsModel): return None mats = self.merged_jms.materials if index >= len(mats) or index < 0: return None return mats[index] def select_shader(self, shader_index): self._editing_shader_path = True try: self._editing_shader_path = False mat = self.get_material(shader_index) if mat: self.shader_names_menu.sel_index = shader_index self.shader_path_string_var.set(mat.shader_path) self.shader_types_menu.sel_index = shader_type_map.get( mat.shader_type, -1) elif shader_index < 0: self.shader_types_menu.sel_index = -1 self.shader_names_menu.sel_index = -1 self.shader_path_string_var.set("") except Exception: self._editing_shader_path = False raise def select_shader_type(self, shader_type): mat = self.get_material(self.shader_names_menu.sel_index) if mat and shader_type in range(len(shader_types)): mat.shader_type = "shader_" + shader_types[shader_type] def shader_path_edited(self, *a, **kw): if self._editing_shader_path: return self._editing_shader_path = True try: shader_path = self.shader_path_string_var.get().replace('/', '\\') mat = self.get_material(self.shader_names_menu.sel_index) if mat: mat.shader_path = shader_path self._editing_shader_path = False except Exception: self._editing_shader_path = False raise def browse_shader_path(self): if self._compiling or self._loading or self._saving: return tags_dir = self.tags_dir.get() if not tags_dir or not os.path.exists(tags_dir): return shader_dir = os.path.dirname( os.path.join(tags_dir, self.shader_path_string_var.get())) shader_exts = tuple((typ, "*.shader_%s" % typ) for typ in shader_types) fp = asksaveasfilename( initialdir=shader_dir, parent=self, title="Select the shader to use(or where to make one)", filetypes=shader_exts + (('All', '*'), )) fp, ext = os.path.splitext(fp) if fp: if not is_in_dir(fp, tags_dir): print("Specified shader is not located in the tags directory.") return ext = ext.strip(".").lower() self.shader_path_string_var.set(os.path.relpath(fp, tags_dir)) mat = self.get_material(self.shader_names_menu.sel_index) if mat and ext in shader_type_map: self.shader_types_menu.sel_index = shader_type_map[ext] mat.shader_type = ext def get_shader_names(self, opt_index=None): if opt_index == "<ACTIVE>": opt_index = self.shader_names_menu.sel_index if isinstance(opt_index, str): opt_index = -1 if opt_index is not None: return self.merged_jms.materials[opt_index].\ shader_path.split("\\")[-1] shader_names = {} if isinstance(self.merged_jms, MergedJmsModel): i = 0 for mat in self.merged_jms.materials: shader_names[i] = mat.shader_path.split("\\")[-1] i += 1 return shader_names def destroy(self): try: self.app_root.tool_windows.pop(self.window_name, None) except AttributeError: pass window_base_class.destroy(self) def load_models(self): if not self._compiling and not self._loading and not self._saving: self._loading = True try: self._load_models() except Exception: print(format_exc()) try: self.populate_model_info_tree() except Exception: print(format_exc()) self._loading = False def save_models(self): if not self._compiling and not self._loading and not self._saving: self._saving = True try: self._save_models() except Exception: print(format_exc()) self._saving = False def compile_gbxmodel(self): if not self._compiling and not self._loading and not self._saving: self._compiling = True try: self._compile_gbxmodel() except Exception: print(format_exc()) self._compiling = False def _load_models(self): models_dir = self.jms_dir.get() if not models_dir: return start = time.time() print("Locating jms files...") fps = [] for _, __, files in os.walk(models_dir): for fname in files: ext = os.path.splitext(fname)[-1].lower() #if ext in ".jms.obj.dae": if ext in ".jms.obj": fps.append(os.path.join(models_dir, fname)) break if not fps: print(" No valid jms files found in the folder.") return self.mod2_tag = self.merged_jms = None optimize_level = max(0, self.optimize_menu.sel_index) jms_models = self.jms_models = [] print("Loading jms files...") self.app_root.update() for fp in fps: try: print(" %s" % fp.replace('/', '\\').split("\\")[-1]) self.app_root.update() model_name = os.path.basename(fp).split('.')[0] ext = os.path.splitext(fp)[-1].lower() jms_model = None if ext == ".jms": with open(fp, "r") as f: jms_model = read_jms(f.read(), '', model_name) elif ext == ".obj": with open(fp, "r") as f: jms_model = jms_model_from_obj(f.read(), model_name) elif ext == ".dae": jms_model = jms_model_from_dae(fp, model_name) if not jms_model: continue jms_models.append(jms_model) if optimize_level: old_vert_ct = len(jms_model.verts) print(" Optimizing...", end='') jms_model.optimize_geometry(optimize_level == 1) print(" Removed %s verts" % (old_vert_ct - len(jms_model.verts))) print(" Calculating normals...") jms_model.calculate_vertex_normals() except Exception: print(format_exc()) print(" Could not parse jms file.") self.app_root.update() if not jms_models: print(" No valid jms files found.") return first_crc = None for jms_model in jms_models: if first_crc is None: first_crc = jms_model.node_list_checksum elif first_crc != jms_model.node_list_checksum: print(" Warning, not all node list checksums match.") break # make sure the highest lod for each permutation is set as superhigh # this is necessary, as only superhigh jms markers are used jms_models_by_name = {} for jms_model in jms_models: lod_models = jms_models_by_name.setdefault(jms_model.perm_name, [None] * 5) lod_index = { "high": 1, "medium": 2, "low": 3, "superlow": 4 }.get(jms_model.lod_level, 0) lod_models[lod_index] = jms_model for lod_models in jms_models_by_name.values(): for jms_model in lod_models: if jms_model is not None: jms_model.lod_level = "superhigh" break print("Merging jms data...") self.app_root.update() self.merged_jms = merged_jms = MergedJmsModel() errors_occurred = False for jms_model in jms_models: errors = merged_jms.merge_jms_model(jms_model) errors_occurred |= bool(errors) if errors: print(" Errors in '%s'" % jms_model.name) for error in errors: print(" ", error, sep='') self.app_root.update() mod2_path = self.gbxmodel_path.get() tags_dir = self.tags_dir.get().replace('/', '\\') self.shader_names_menu.max_index = len(merged_jms.materials) - 1 shaders_dir = "" if mod2_path: shaders_dir = os.path.join(os.path.dirname(mod2_path), "shaders", '') tags_dir = self.tags_dir.get() has_local_shaders = os.path.exists(shaders_dir) and os.path.exists( tags_dir) if errors_occurred: print(" Errors occurred while loading jms files.") elif os.path.isfile(mod2_path): try: self.mod2_tag = mod2_def.build(filepath=mod2_path) tagdata = self.mod2_tag.data.tagdata self.merged_jms.node_list_checksum = tagdata.node_list_checksum self.superhigh_lod_cutoff.set(str( tagdata.superhigh_lod_cutoff)) self.high_lod_cutoff.set(str(tagdata.high_lod_cutoff)) self.medium_lod_cutoff.set(str(tagdata.medium_lod_cutoff)) self.low_lod_cutoff.set(str(tagdata.low_lod_cutoff)) self.superlow_lod_cutoff.set(str(tagdata.superlow_lod_cutoff)) # get any shaders in the gbxmodel and set the shader_path # and shader_type for any matching materials in the jms shdr_refs = {} for shdr_ref in tagdata.shaders.STEPTREE: shdr_name = shdr_ref.shader.filepath.split( "\\")[-1].lower() shdr_refs.setdefault(shdr_name, []).append(shdr_ref) for mat in merged_jms.materials: shdr_ref = shdr_refs.get(mat.name, [""]).pop(0) if shdr_ref: mat.shader_type = shdr_ref.shader.tag_class.enum_name mat.shader_path = shdr_ref.shader.filepath local_shaders = {} if has_local_shaders and is_in_dir(shaders_dir, tags_dir): # fill in any missing shader paths with ones found nearby for _, __, files in os.walk(shaders_dir): for filename in files: name, ext = os.path.splitext(filename) ext = ext.lower() if ext.startswith(".shader"): local_shaders.setdefault( name.split("\\")[-1].lower(), []).append( os.path.join(shaders_dir, filename)) break for mat in merged_jms.materials: shader_path = local_shaders.get(mat.name, [""]).pop(0) if "shader_" in mat.shader_type or not shader_path: continue # shader type isnt set. Try to detect its location and # type if possible, or set it to a default value if not shader_path = shader_path.lower().replace("/", "\\") name, ext = os.path.splitext(shader_path) mat.shader_path = os.path.relpath(name, tags_dir).strip("\\") mat.shader_type = ext.strip(".") except Exception: print(format_exc()) else: self.superhigh_lod_cutoff.set("0.0") self.high_lod_cutoff.set("0.0") self.medium_lod_cutoff.set("0.0") self.low_lod_cutoff.set("0.0") self.superlow_lod_cutoff.set("0.0") for mat in merged_jms.materials: shader_path = mat.shader_path if mat.shader_type in ("shader", ""): assume_shaders_dir = not shaders_dir if not assume_shaders_dir: try: shader_path = os.path.relpath( os.path.join(shaders_dir, shader_path), tags_dir) shader_path = shader_path.strip("\\") except ValueError: assume_shaders_dir = True mat.shader_type = "shader_model" else: assume_shaders_dir = False if assume_shaders_dir or shader_path.startswith("..\\"): shader_path = "shaders\\" + os.path.basename(shader_path) mat.shader_path = shader_path.lstrip("..\\") if not self.mod2_tag: print( " Existing gbxmodel tag not detected or could not be loaded.\n" " A new gbxmodel tag will be created.") print("Finished loading models. Took %.6f seconds.\n" % (time.time() - start)) self.select_shader(0) def _save_models(self): models_dir = self.jms_dir.get() if not models_dir: return start = time.time() print("Saving jms models...") for jms_model in self.jms_models: if isinstance(jms_model, JmsModel): fname = "%s %s.jms" % (jms_model.perm_name, jms_model.lod_level) if not jms_model.is_random_perm: fname = "~" + fname write_jms(os.path.join(models_dir, fname), jms_model) print("Finished saving models. Took %s seconds.\n" % str(time.time() - start).split('.')[0]) def _compile_gbxmodel(self): if not self.merged_jms: return try: superhigh_lod_cutoff = self.superhigh_lod_cutoff.get().strip(" ") high_lod_cutoff = self.high_lod_cutoff.get().strip(" ") medium_lod_cutoff = self.medium_lod_cutoff.get().strip(" ") low_lod_cutoff = self.low_lod_cutoff.get().strip(" ") superlow_lod_cutoff = self.superlow_lod_cutoff.get().strip(" ") if not superhigh_lod_cutoff: superhigh_lod_cutoff = "0" if not high_lod_cutoff: high_lod_cutoff = "0" if not medium_lod_cutoff: medium_lod_cutoff = "0" if not low_lod_cutoff: low_lod_cutoff = "0" if not superlow_lod_cutoff: superlow_lod_cutoff = "0" superhigh_lod_cutoff = float(superhigh_lod_cutoff) high_lod_cutoff = float(high_lod_cutoff) medium_lod_cutoff = float(medium_lod_cutoff) low_lod_cutoff = float(low_lod_cutoff) superlow_lod_cutoff = float(superlow_lod_cutoff) except ValueError: print("LOD cutoffs are invalid.") return updating = self.mod2_tag is not None if updating: print("Updating existing gbxmodel tag.") mod2_tag = self.mod2_tag else: print("Creating new gbxmodel tag.") mod2_tag = mod2_def.build() while not self.gbxmodel_path.get(): self.gbxmodel_path_browse(True) if not self.gbxmodel_path.get(): if messagebox.askyesno( "Unsaved gbxmodel", "Are you sure you wish to cancel saving?", icon='warning', parent=self): print(" Gbxmodel compilation cancelled.") return mod2_tag.filepath = self.gbxmodel_path.get() self.app_root.update() errors = compile_gbxmodel(mod2_tag, self.merged_jms) if errors: for error in errors: print(error) print("Gbxmodel compilation failed.") return tags_dir = self.tags_dir.get() if tags_dir: data_dir = os.path.join(os.path.dirname(os.path.dirname(tags_dir)), "data", "") for mat in self.merged_jms.materials: try: generate_shader(mat, tags_dir, data_dir) except Exception: print(format_exc()) print("Failed to generate shader tag.") tagdata = mod2_tag.data.tagdata tagdata.superhigh_lod_cutoff = superhigh_lod_cutoff tagdata.high_lod_cutoff = high_lod_cutoff tagdata.medium_lod_cutoff = medium_lod_cutoff tagdata.low_lod_cutoff = low_lod_cutoff tagdata.superlow_lod_cutoff = superlow_lod_cutoff try: mod2_tag.calc_internal_data() mod2_tag.serialize(temp=False, backup=False, calc_pointers=False, int_test=False) print(" Finished") except Exception: print(format_exc()) print(" Could not save compiled gbxmodel.")
def __init__(self, app_root, *args, **kwargs): if window_base_class == tk.Toplevel: kwargs.update(bd=0, highlightthickness=0, bg=self.default_bg_color) self.app_root = app_root else: self.app_root = self window_base_class.__init__(self, app_root, *args, **kwargs) BinillaWidget.__init__(self, *args, **kwargs) self.title("Gbxmodel compiler") self.resizable(1, 1) self.update() try: self.iconbitmap(e_c.MOZZ_ICON_PATH) except Exception: print("Could not load window icon.") self.superhigh_lod_cutoff = tk.StringVar(self) self.high_lod_cutoff = tk.StringVar(self) self.medium_lod_cutoff = tk.StringVar(self) self.low_lod_cutoff = tk.StringVar(self) self.superlow_lod_cutoff = tk.StringVar(self) self.shader_path_string_var = tk.StringVar(self) tags_dir = getattr(app_root, "tags_dir", "") self.optimize_level = tk.IntVar(self) self.tags_dir = tk.StringVar(self, tags_dir if tags_dir else "") self.jms_dir = tk.StringVar(self) self.gbxmodel_path = tk.StringVar(self) # make the frames self.main_frame = tk.Frame(self) self.jms_info_frame = tk.LabelFrame(self, text="Model info") self.dirs_frame = tk.LabelFrame(self.main_frame, text="Directories") self.buttons_frame = tk.Frame(self.main_frame) self.settings_frame = tk.LabelFrame(self.main_frame, text="Compilation settings") self.jms_dir_frame = tk.LabelFrame(self.dirs_frame, text="Source models folder") self.tags_dir_frame = tk.LabelFrame(self.dirs_frame, text="Tags directory root folder") self.gbxmodel_path_frame = tk.LabelFrame(self.dirs_frame, text="Gbxmodel output path") self.lods_frame = tk.LabelFrame(self.settings_frame, text="LOD cutoffs") self.shaders_frame = tk.LabelFrame(self.settings_frame, text="Shaders") self.optimize_label = tk.Label( self.settings_frame, justify="right", text=("Vertex optimization\n(Set before loading)")) self.optimize_menu = ScrollMenu(self.settings_frame, menu_width=5, options=("None", "Exact", "Loose")) self.optimize_menu.sel_index = 1 self.jms_info_tree = tk.ttk.Treeview(self.jms_info_frame, selectmode='browse', padding=(0, 0), height=4) self.jms_info_vsb = tk.Scrollbar(self.jms_info_frame, orient='vertical', command=self.jms_info_tree.yview) self.jms_info_hsb = tk.Scrollbar(self.jms_info_frame, orient='horizontal', command=self.jms_info_tree.xview) self.jms_info_tree.config(yscrollcommand=self.jms_info_vsb.set, xscrollcommand=self.jms_info_hsb.set) self.shader_names_menu = ScrollMenu( self.shaders_frame, menu_width=10, callback=self.select_shader, option_getter=self.get_shader_names, options_volatile=True) self.shader_types_menu = ScrollMenu(self.shaders_frame, menu_width=20, options=shader_types, callback=self.select_shader_type) self.shader_path_browse_button = tk.Button( self.shaders_frame, text="Browse", width=6, command=self.browse_shader_path) self.shader_path_entry = tk.Entry( self.shaders_frame, textvariable=self.shader_path_string_var) self.write_trace(self.shader_path_string_var, self.shader_path_edited) self.superhigh_lod_label = tk.Label(self.lods_frame, text="Superhigh") self.high_lod_label = tk.Label(self.lods_frame, text="High") self.medium_lod_label = tk.Label(self.lods_frame, text="Medium") self.low_lod_label = tk.Label(self.lods_frame, text="Low") self.superlow_lod_label = tk.Label(self.lods_frame, text="Superlow") self.superhigh_lod_cutoff_entry = tk.Entry( self.lods_frame, textvariable=self.superhigh_lod_cutoff, width=6, justify='right') self.high_lod_cutoff_entry = tk.Entry( self.lods_frame, textvariable=self.high_lod_cutoff, width=6, justify='right') self.medium_lod_cutoff_entry = tk.Entry( self.lods_frame, textvariable=self.medium_lod_cutoff, width=6, justify='right') self.low_lod_cutoff_entry = tk.Entry(self.lods_frame, textvariable=self.low_lod_cutoff, width=6, justify='right') self.superlow_lod_cutoff_entry = tk.Entry( self.lods_frame, textvariable=self.superlow_lod_cutoff, width=6, justify='right') self.jms_dir_entry = tk.Entry(self.jms_dir_frame, textvariable=self.jms_dir, state=tk.DISABLED) self.jms_dir_browse_button = tk.Button(self.jms_dir_frame, text="Browse", command=self.jms_dir_browse) self.tags_dir_entry = tk.Entry(self.tags_dir_frame, textvariable=self.tags_dir, state=tk.DISABLED) self.tags_dir_browse_button = tk.Button(self.tags_dir_frame, text="Browse", command=self.tags_dir_browse) self.gbxmodel_path_entry = tk.Entry(self.gbxmodel_path_frame, textvariable=self.gbxmodel_path, state=tk.DISABLED) self.gbxmodel_path_browse_button = tk.Button( self.gbxmodel_path_frame, text="Browse", command=self.gbxmodel_path_browse) self.load_button = tk.Button(self.buttons_frame, text="Load\nmodels", command=self.load_models) self.save_button = tk.Button(self.buttons_frame, text="Save as JMS", command=self.save_models) self.compile_button = tk.Button(self.buttons_frame, text="Compile\ngbxmodel", command=self.compile_gbxmodel) self.populate_model_info_tree() # pack everything self.main_frame.pack(fill="both", side='left', pady=3, padx=3) self.jms_info_frame.pack(fill="both", side='left', pady=3, padx=3, expand=True) self.dirs_frame.pack(fill="x") self.buttons_frame.pack(fill="x", pady=3, padx=3) self.settings_frame.pack(fill="both", expand=True) self.superhigh_lod_label.grid(sticky='e', row=0, column=0, padx=3, pady=1) self.high_lod_label.grid(sticky='e', row=1, column=0, padx=3, pady=1) self.medium_lod_label.grid(sticky='e', row=2, column=0, padx=3, pady=1) self.low_lod_label.grid(sticky='e', row=3, column=0, padx=3, pady=1) self.superlow_lod_label.grid(sticky='e', row=4, column=0, padx=3, pady=1) self.superhigh_lod_cutoff_entry.grid(sticky='ew', row=0, column=1, padx=3, pady=1) self.high_lod_cutoff_entry.grid(sticky='ew', row=1, column=1, padx=3, pady=1) self.medium_lod_cutoff_entry.grid(sticky='ew', row=2, column=1, padx=3, pady=1) self.low_lod_cutoff_entry.grid(sticky='ew', row=3, column=1, padx=3, pady=1) self.superlow_lod_cutoff_entry.grid(sticky='ew', row=4, column=1, padx=3, pady=1) self.jms_dir_frame.pack(expand=True, fill='x') self.tags_dir_frame.pack(expand=True, fill='x') self.gbxmodel_path_frame.pack(expand=True, fill='x') self.jms_dir_entry.pack(side='left', expand=True, fill='x') self.jms_dir_browse_button.pack(side='left') self.gbxmodel_path_entry.pack(side='left', expand=True, fill='x') self.gbxmodel_path_browse_button.pack(side='left') self.tags_dir_entry.pack(side='left', expand=True, fill='x') self.tags_dir_browse_button.pack(side='left') self.optimize_label.grid(sticky='ne', row=3, column=1, padx=3) self.optimize_menu.grid(sticky='new', row=3, column=2, padx=3, pady=(3, 0)) self.lods_frame.grid(sticky='ne', row=0, column=3, rowspan=4) self.shaders_frame.grid(sticky='nsew', row=0, column=0, columnspan=3, rowspan=3, pady=(0, 3)) self.shader_names_menu.grid(sticky='new', row=0, column=0, padx=3, columnspan=5, pady=2) self.shader_path_browse_button.grid(sticky='ne', row=1, column=4, padx=3, pady=2) self.shader_types_menu.grid(sticky='new', row=1, column=1, padx=3, columnspan=3, pady=2) self.shader_path_entry.grid(sticky='new', row=2, column=0, padx=3, columnspan=5, pady=2) self.jms_info_hsb.pack(side="bottom", fill='x') self.jms_info_vsb.pack(side="right", fill='y') self.jms_info_tree.pack(side='left', fill='both', expand=True) self.load_button.pack(side='left', expand=True, fill='both', padx=3) self.save_button.pack(side='left', expand=True, fill='both', padx=3) self.compile_button.pack(side='right', expand=True, fill='both', padx=3) self.apply_style() if self.app_root is not self: self.transient(self.app_root)
class ArrayFrame(container_frame.ContainerFrame): '''Used for array nodes. Displays a single element in the ArrayBlock represented by it, and contains a combobox for selecting which array element is displayed.''' sel_index = -1 sel_menu = None populated = False option_cache = None options_sane = False def __init__(self, *args, **kwargs): kwargs.update(relief='flat', bd=0, highlightthickness=0, bg=self.default_bg_color) field_widget.FieldWidget.__init__(self, *args, **kwargs) tk.Frame.__init__(self, *args, **e_c.fix_kwargs(**kwargs)) show_frame = bool( kwargs.pop('show_frame', not self.blocks_start_hidden)) if self.is_empty and self.hide_if_blank: show_frame = False self.show = tk.BooleanVar() self.show.set(show_frame) self.options_sane = False node_len = 0 try: node_len = len(self.node) except Exception: pass self.sel_index = (node_len > 0) - 1 # make the title, element menu, and all the buttons self.controls = tk.Frame(self, relief='raised', bd=self.frame_depth) self.title = title = tk.Frame(self.controls, relief='flat', bd=0) self.buttons = buttons = tk.Frame(self.controls, relief='flat', bd=0) toggle_text = '-' if show_frame else '+' self.title_label = tk.Label( title, text=self.gui_name, justify='left', anchor='w', width=self.title_size, font=self.get_font("frame_title"), disabledforeground=self.text_disabled_color) self.title_label.font_type = "frame_title" self.show_btn = ttk.Checkbutton(title, width=3, text=toggle_text, command=self.toggle_visible, style='ShowButton.TButton') self.sel_menu = ScrollMenu(title, f_widget_parent=self, sel_index=self.sel_index, max_index=node_len - 1, option_getter=self.get_options, callback=self.select_option) self.shift_up_btn = ttk.Button(title, width=7, text='Shift ▲', command=self.shift_entry_up) self.shift_down_btn = ttk.Button(buttons, width=7, text='Shift ▼', command=self.shift_entry_down) self.add_btn = ttk.Button(buttons, width=4, text='Add', command=self.add_entry) self.insert_btn = ttk.Button(buttons, width=6, text='Insert', command=self.insert_entry) self.duplicate_btn = ttk.Button(buttons, width=9, text='Duplicate', command=self.duplicate_entry) self.delete_btn = ttk.Button(buttons, width=6, text='Delete', command=self.delete_entry) self.delete_all_btn = ttk.Button(buttons, width=10, text='Delete all', command=self.delete_all_entries) self.import_btn = ttk.Button(buttons, width=6, text='Import', command=self.import_node) self.export_btn = ttk.Button(buttons, width=6, text='Export', command=self.export_node) # pack the title, menu, and all the buttons for w in (self.shift_down_btn, self.export_btn, self.import_btn, self.delete_all_btn, self.delete_btn, self.duplicate_btn, self.insert_btn, self.add_btn): w.pack(side="right", padx=(0, 4), pady=(2, 2)) self.show_btn.pack(side="left") if self.gui_name != '': self.title_label.pack(side="left", fill="x", expand=True) self.sel_menu.pack(side="left", fill="x", expand=True, padx=(0, 4)) self.shift_up_btn.pack(side="right", padx=(0, 1), pady=(2, 2)) self.title.pack(fill="x", expand=True, padx=0) self.buttons.pack(fill="x", expand=True, padx=0) self.controls.pack(fill="x", expand=True, padx=0) self.populate() self._initialized = True @property def is_empty(self): if getattr(self, "node", None) is None: return True return len(self.node) == 0 def load_node_data(self, parent, node, attr_index, desc=None): field_widget.FieldWidget.load_node_data(self, parent, node, attr_index, desc) sub_node = attr_index = None if self.node: attr_index = self.sel_index if attr_index in range(len(self.node)): sub_node = self.node[attr_index] else: attr_index = len(self.node) - 1 if attr_index < 0: attr_index = None if self.sel_menu is not None: self.options_sane = self.sel_menu.options_menu_sane = False for wid in self.f_widgets: # there must be only one entry in self.f_widgets w = self.f_widgets[wid] if w.load_node_data(self.node, sub_node, attr_index): return True return False def unload_node_data(self): self.sel_menu.update_label(" ") container_frame.ContainerFrame.unload_node_data(self) def set_disabled(self, disable=True): disable = disable or not self.editable if self.node is None and not disable: return if getattr(self, "sel_menu", None): self.sel_menu.set_disabled(disable) if bool(disable) == self.disabled: pass elif not disable: self.set_all_buttons_disabled(False) self.disable_unusable_buttons() else: new_state = tk.DISABLED if disable else tk.NORMAL for w in (self.shift_up_btn, self.shift_down_btn, self.add_btn, self.insert_btn, self.duplicate_btn, self.delete_btn, self.delete_all_btn): if w: w.config(state=new_state) container_frame.ContainerFrame.set_disabled(self, disable) def apply_style(self, seen=None): container_frame.ContainerFrame.apply_style(self, seen) self.controls.config(bd=self.frame_depth, bg=self.frame_bg_color) self.title.config(bg=self.frame_bg_color) self.title_label.config(bg=self.frame_bg_color) self.buttons.config(bg=self.frame_bg_color) #if self.show.get(): # self.pose_fields() def destroy(self): # These will linger and take up RAM, even if the widget is destroyed. # Need to remove the references manually self.option_cache = None container_frame.ContainerFrame.destroy(self) def export_node(self): try: # pass call to the export_node method of the array entry's widget w = self.f_widgets[self.f_widget_ids[0]] except Exception: return w.export_node() def import_node(self): try: # pass call to the import_node method of the array entry's widget w = self.f_widgets[self.f_widget_ids[0]] except Exception: return w.import_node() def get_options(self, opt_index=None): ''' Returns a list of the option strings sorted by option index. ''' if (self.option_cache is None or not self.options_sane or opt_index is not None): result = self.generate_options(opt_index) if opt_index is not None: return result if opt_index is None: return self.option_cache elif opt_index == e_c.ACTIVE_ENUM_NAME: opt_index = self.sel_index if opt_index < 0: opt_index = -1 return self.option_cache.get(opt_index) def generate_options(self, opt_index=None): # sort the options by value(values are integers) options = {i: n for n, i in self.desc.get('NAME_MAP', {}).items()} if self.node: node, desc = self.node, self.desc sub_desc = desc['SUB_STRUCT'] def_struct_name = sub_desc['NAME'] if self.use_gui_names and 'GUI_NAME' in sub_desc: def_struct_name = sub_desc['GUI_NAME'] options_to_generate = range(len(node)) if opt_index is not None: options_to_generate = ((opt_index, ) if opt_index in options_to_generate else ()) for i in options_to_generate: if i in options: continue sub_node = node[i] if not hasattr(sub_node, 'desc'): continue sub_desc = sub_node.desc sub_struct_name = sub_desc.get('GUI_NAME', sub_desc['NAME']) if sub_struct_name == def_struct_name: continue options[i] = sub_struct_name if opt_index is None: self.options_sane = True self.option_cache = options if self.sel_menu is not None: self.sel_menu.options_menu_sane = False self.sel_menu.max_index = len(node) - 1 return options return options.get(opt_index, None) def set_shift_up_disabled(self, disable=True): ''' Disables the move up button if disable is True. Enables it if not. ''' if disable: self.shift_up_btn.config(state="disabled") else: self.shift_up_btn.config(state="normal") def set_shift_down_disabled(self, disable=True): ''' Disables the move down button if disable is True. Enables it if not. ''' if disable: self.shift_down_btn.config(state="disabled") else: self.shift_down_btn.config(state="normal") def set_add_disabled(self, disable=True): '''Disables the add button if disable is True. Enables it if not.''' if disable: self.add_btn.config(state="disabled") else: self.add_btn.config(state="normal") def set_insert_disabled(self, disable=True): '''Disables the insert button if disable is True. Enables it if not.''' if disable: self.insert_btn.config(state="disabled") else: self.insert_btn.config(state="normal") def set_duplicate_disabled(self, disable=True): ''' Disables the duplicate button if disable is True. Enables it if not. ''' if disable: self.duplicate_btn.config(state="disabled") else: self.duplicate_btn.config(state="normal") def set_delete_disabled(self, disable=True): '''Disables the delete button if disable is True. Enables it if not.''' if disable: self.delete_btn.config(state="disabled") else: self.delete_btn.config(state="normal") def set_delete_all_disabled(self, disable=True): ''' Disables the delete_all button if disable is True. Enables it if not. ''' if disable: self.delete_all_btn.config(state="disabled") else: self.delete_all_btn.config(state="normal") def edit_apply(self=None, *, edit_state, undo=True): state = edit_state edit_type = state.edit_type i = state.attr_index undo_node = state.undo_node redo_node = state.redo_node edit_info = state.edit_info sel_index = edit_info.get('sel_index', 0) w, node = field_widget.FieldWidget.get_widget_and_node( nodepath=state.nodepath, tag_window=state.tag_window) if edit_type == 'shift_up': node[i], node[i - 1] = node[i - 1], node[i] elif edit_type == 'shift_down': node[i], node[i + 1] = node[i + 1], node[i] elif edit_type in ('add', 'insert', 'duplicate'): if undo: sel_index = None node.pop(i) else: node.insert(i, redo_node) elif edit_type == 'delete': if undo: node.insert(i, undo_node) else: sel_index = None node.pop(i) elif edit_type == 'delete_all': if undo: node[:] = undo_node else: del node[:] sel_index = None else: raise TypeError('Unknown edit_state type') if w is not None: try: if w.desc is not state.desc: return if sel_index is None: pass elif edit_type in ('add', 'insert', 'duplicate', 'delete'): w.sel_index = sel_index elif edit_type in ('shift_up', 'shift_down'): w.sel_index = sel_index if undo: pass elif 'down' in edit_type: w.sel_index += 1 else: w.sel_index -= 1 max_index = len(node) - 1 w.sel_menu.max_index = max_index w.options_sane = w.sel_menu.options_menu_sane = False if w.sel_index < 0: w.select_option(0, force=True) elif w.sel_index > max_index: w.select_option(max_index, force=True) else: w.select_option(w.sel_index, force=True) w.needs_flushing = False w.set_edited() except Exception: print(format_exc()) def edit_create(self, **kwargs): # add own stuff kwargs.setdefault("sel_index", self.sel_index) field_widget.FieldWidget.edit_create(self, **kwargs) def shift_entry_up(self): if not hasattr(self.node, '__len__') or len(self.node) < 2: return node = self.node index = self.sel_index if index <= 0: return self.set_edited() # do this first so the TagWindow detects that # the title needs to be updated with an asterisk self.edit_create(edit_type='shift_up', attr_index=index) node[index], node[index - 1] = node[index - 1], node[index] self.sel_index = self.sel_menu.sel_index = index - 1 self.options_sane = self.sel_menu.options_menu_sane = False self.sel_menu.update_label() def shift_entry_down(self): if not hasattr(self.node, '__len__') or len(self.node) < 2: return node = self.node index = self.sel_index if index >= len(node) - 1: return self.set_edited() # do this first so the TagWindow detects that # the title needs to be updated with an asterisk self.edit_create(edit_type='shift_down', attr_index=index) node[index], node[index + 1] = node[index + 1], node[index] self.sel_index = self.sel_menu.sel_index = index + 1 self.options_sane = self.sel_menu.options_menu_sane = False self.sel_menu.update_label() def add_entry(self): if not hasattr(self.node, '__len__'): return field_max = self.field_max if field_max is not None and len(self.node) >= field_max: if self.enforce_max: return attr_index = len(self.node) self.set_edited() # do this first so the TagWindow detects that # the title needs to be updated with an asterisk self.node.append() self.edit_create(edit_type='add', attr_index=attr_index, redo_node=self.node[attr_index], sel_index=attr_index) self.options_sane = self.sel_menu.options_menu_sane = False self.set_all_buttons_disabled(self.disabled) self.disable_unusable_buttons() self.select_option(len(self.node) - 1, True) def insert_entry(self): if not hasattr(self.node, '__len__'): return field_max = self.field_max if field_max is not None and len(self.node) >= field_max: if self.enforce_max: return attr_index = self.sel_index = max(self.sel_index, 0) self.set_edited() # do this first so the TagWindow detects that # the title needs to be updated with an asterisk self.node.insert(attr_index) self.edit_create(edit_type='insert', attr_index=attr_index, redo_node=self.node[attr_index], sel_index=attr_index) self.options_sane = self.sel_menu.options_menu_sane = False self.set_all_buttons_disabled(self.disabled) self.disable_unusable_buttons() self.select_option(attr_index, True) # select the new entry def duplicate_entry(self): if not hasattr(self.node, '__len__') or len(self.node) < 1: return field_max = self.field_max if field_max is not None and len(self.node) >= field_max: if self.enforce_max: return self.sel_index = self.sel_menu.sel_index = max(self.sel_index, 0) self.set_edited() # do this first so the TagWindow detects that # the title needs to be updated with an asterisk new_subnode = deepcopy(self.node[self.sel_index]) attr_index = len(self.node) self.edit_create(edit_type='duplicate', attr_index=attr_index, redo_node=new_subnode, sel_index=attr_index) self.node.append(new_subnode) self.options_sane = self.sel_menu.options_menu_sane = False self.set_all_buttons_disabled(self.disabled) self.disable_unusable_buttons() self.select_option(attr_index, True) def delete_entry(self): if not hasattr(self.node, '__len__') or len(self.node) == 0: return field_min = self.field_min if field_min is None: field_min = 0 if len(self.node) <= field_min: if self.enforce_min: return if not len(self.node): self.sel_menu.disable() return attr_index = max(self.sel_index, 0) self.set_edited() # do this first so the TagWindow detects that # the title needs to be updated with an asterisk self.edit_create(edit_type='delete', undo_node=self.node[attr_index], attr_index=attr_index, sel_index=attr_index) del self.node[attr_index] attr_index = max(-1, min(len(self.node) - 1, attr_index)) self.options_sane = self.sel_menu.options_menu_sane = False self.select_option(attr_index, True) self.set_all_buttons_disabled(self.disabled) self.disable_unusable_buttons() def delete_all_entries(self): if not hasattr(self.node, '__len__') or len(self.node) == 0: return field_min = self.field_min if field_min is None: field_min = 0 if len(self.node) <= field_min: if self.enforce_min: return if not len(self.node): self.sel_menu.disable() return self.set_edited() # do this first so the TagWindow detects that # the title needs to be updated with an asterisk self.edit_create(edit_type='delete_all', undo_node=tuple(self.node[:])) del self.node[:] self.options_sane = self.sel_menu.options_menu_sane = False self.set_all_buttons_disabled(self.disabled) self.disable_unusable_buttons() self.select_option(self.sel_index, True) def set_all_buttons_disabled(self, disable=False): for btn in (self.add_btn, self.insert_btn, self.duplicate_btn, self.delete_btn, self.delete_all_btn, self.import_btn, self.export_btn, self.shift_up_btn, self.shift_down_btn): if disable: btn.config(state=tk.DISABLED) else: btn.config(state=tk.NORMAL) def disable_unusable_buttons(self): no_node = not hasattr(self.node, '__len__') if no_node or len(self.node) < 2: self.set_shift_up_disabled() self.set_shift_down_disabled() if no_node or (isinstance(self.desc.get('SIZE'), int) and self.enforce_min): self.set_add_disabled() self.set_insert_disabled() self.set_duplicate_disabled() self.set_delete_disabled() self.set_delete_all_disabled() return field_max = self.field_max field_min = self.field_min if field_min is None or field_min < 0: field_min = 0 enforce_min = self.enforce_min or field_min == 0 enforce_max = self.enforce_max if field_max is not None and len( self.node) >= field_max and enforce_max: self.set_add_disabled() self.set_insert_disabled() self.set_duplicate_disabled() if len(self.node) <= field_min and (enforce_min or not self.node): self.set_delete_disabled() self.set_delete_all_disabled() if not self.node: self.set_export_disabled() self.set_import_disabled() self.set_duplicate_disabled() def populate(self): node = self.node desc = self.desc sub_node = None sub_desc = desc['SUB_STRUCT'] if node and self.sel_index in range(len(node)): sub_node = node[self.sel_index] sub_desc = desc['SUB_STRUCT'] if hasattr(sub_node, 'desc'): sub_desc = sub_node.desc if self.content in (None, self): self.content = tk.Frame(self, relief="sunken", bd=self.frame_depth, bg=self.default_bg_color) self.sel_menu.default_text = sub_desc.get('GUI_NAME', sub_desc.get('NAME', "")) self.sel_menu.update_label() self.disable_unusable_buttons() rebuild = not bool(self.f_widgets) if hasattr(node, '__len__') and len(node) == 0: # disabling existing widgets self.sel_index = -1 self.sel_menu.max_index = -1 if self.f_widgets: self.unload_child_node_data() else: for w in self.f_widgets: if getattr(w, "desc", None) != sub_desc: rebuild = True break if rebuild: # destroy existing widgets and make new ones self.populated = False self.f_widget_ids = [] self.f_widget_ids_map = {} self.f_widget_ids_map_inv = {} # destroy any child widgets of the content for c in list(self.f_widgets.values()): c.destroy() for w in (self, self.content, self.title, self.title_label, self.controls, self.buttons): w.tooltip_string = self.desc.get('TOOLTIP') self.display_comment(self.content) widget_cls = self.widget_picker.get_widget(sub_desc) try: widget = widget_cls(self.content, node=sub_node, parent=node, show_title=False, dont_padx_fields=True, attr_index=self.sel_index, tag_window=self.tag_window, f_widget_parent=self, disabled=self.disabled) except Exception: print(format_exc()) widget = data_frame.NullFrame(self.content, node=sub_node, parent=node, show_title=False, dont_padx_fields=True, attr_index=self.sel_index, tag_window=self.tag_window, f_widget_parent=self, disabled=self.disabled) wid = id(widget) self.f_widget_ids.append(wid) self.f_widget_ids_map[self.sel_index] = wid self.f_widget_ids_map_inv[wid] = self.sel_index self.populated = True self.build_f_widget_cache() # now that the field widgets are created, position them if self.show.get(): self.pose_fields() if self.node is None: self.set_disabled(True) else: self.set_children_disabled(not self.node) def reload(self): '''Resupplies the nodes to the widgets which display them.''' try: node = self.node if self.node else () desc = self.desc is_empty = len(node) == 0 field_max = self.field_max field_min = self.field_min if field_min is None: field_min = 0 self.set_all_buttons_disabled(self.disabled) self.disable_unusable_buttons() if is_empty: self.sel_menu.sel_index = -1 self.sel_menu.max_index = -1 # if there is no index to select, destroy the content if self.sel_index != -1: self.sel_index = -1 self.unload_child_node_data() else: self.f_widget_ids_map = {} self.f_widget_ids_map_inv = {} self.sel_menu.sel_index = self.sel_index self.sel_menu.max_index = len(node) - 1 sub_node = None sub_desc = desc['SUB_STRUCT'] if node and self.sel_index in range(len(node)): sub_node = node[self.sel_index] sub_desc = desc['SUB_STRUCT'] if hasattr(sub_node, 'desc'): sub_desc = sub_node.desc for wid in self.f_widget_ids: w = self.f_widgets[wid] wid = id(w) if node and self.sel_index not in range(len(node)): # current selection index is invalid. call select_option # to reset it to some valid option. Don't reload though, # as we will be either reloading or repopulating below. self.select_option(force=True, reload=False) self.f_widget_ids_map[self.sel_index] = wid self.f_widget_ids_map_inv[wid] = self.sel_index # if the descriptors are different, gotta repopulate! if w.load_node_data(node, sub_node, self.sel_index, sub_desc): self.populate() self.apply_style() return w.reload() if w.desc.get("PORTABLE", True) and self.node: self.set_import_disabled(False) self.set_export_disabled(False) else: self.set_import_disabled() self.set_export_disabled() self.sel_menu.update_label() if self.node is None: self.set_disabled(True) else: self.set_children_disabled(not self.node) except Exception: print(format_exc()) def pose_fields(self): # there should only be one wid in here, but for # the sake of consistancy we'll loop over them. for wid in self.f_widget_ids: w = self.f_widgets[wid] # by adding a fixed amount of padding, we fix a problem # with difficult to predict padding based on nesting w.pack(fill='x', side='top', expand=True, padx=self.vertical_padx, pady=self.vertical_pady) # if there are no children in the content, we need to # pack in SOMETHING, update the idletasks, and then # destroy that something to resize the content frame if not self.f_widgets: f = tk.Frame(self.content, width=0, height=0, bd=0) f.pack() self.content.update_idletasks() f.destroy() self.content.pack(fill='both', side='top', anchor='nw', expand=True) def select_option(self, opt_index=None, force=False, reload=True): node = self.node if self.node else () curr_index = self.sel_index if opt_index is None: opt_index = curr_index if opt_index < 0: opt_index = 0 if opt_index == curr_index and not force: return elif not node: opt_index = -1 elif opt_index not in range(len(node)): opt_index = len(node) - 1 # flush any lingering changes self.flush() self.sel_index = opt_index self.sel_menu.sel_index = opt_index self.sel_menu.max_index = len(node) - 1 if reload: self.reload() self.sel_menu.update_label() @property def visible_field_count(self): # array frames only display one item at a time return 1
class HaloBitmapDisplayFrame(BitmapDisplayFrame): # these mappings have the 2nd and 3rd faces swapped on pc for some reason cubemap_cross_mapping = ( (-1, 1), ( 2, 4, 0, 5), (-1, 3), ) def __init__(self, master, bitmap_tag=None, *args, **kwargs): self.bitmap_tag = bitmap_tag textures = kwargs.get('textures', ()) BitmapDisplayFrame.__init__(self, master, *args, **kwargs) self.sequence_index = tk.IntVar(self) self.sprite_index = tk.IntVar(self) labels = [] labels.append(tk.Label(self.controls_frame0, text="Sequence index")) labels.append(tk.Label(self.controls_frame1, text="Sprite index")) for lbl in labels: lbl.config(width=15, anchor='w') self.sequence_menu = ScrollMenu(self.controls_frame0, menu_width=7, variable=self.sequence_index) self.sprite_menu = ScrollMenu(self.controls_frame1, menu_width=7, variable=self.sprite_index) padx = self.horizontal_padx pady = self.horizontal_pady for lbl in labels: lbl.pack(side='left', padx=(15, 0), pady=pady) self.sequence_menu.pack(side='left', padx=padx, pady=pady) self.sprite_menu.pack(side='left', padx=padx, pady=pady) self.write_trace(self.sequence_index, self.sequence_changed) self.write_trace(self.sprite_index, self.sprite_changed) self.change_textures(textures) self.apply_style() def sequence_changed(self, *args): tag = self.bitmap_tag if tag is None: self.sprite_menu.set_options(()) self.sequence_menu.set_options(("None", )) self.sprite_menu.sel_index = -1 self.sequence_menu.sel_index = 0 return sequence_i = self.sequence_index.get() - 1 options = () sequences = tag.data.tagdata.sequences.STEPTREE if sequence_i in range(len(sequences)): sequence = sequences[sequence_i] options = range(len(sequence.sprites.STEPTREE)) if not options: options = range(sequence.bitmap_count) self.sprite_menu.set_options(options) self.sprite_menu.sel_index = (self.sprite_menu.max_index >= 0) - 1 def sprite_changed(self, *args): self.image_canvas.delete(SPRITE_RECTANGLE_TAG) self.image_canvas.delete(SPRITE_CENTER_TAG) tag = self.bitmap_tag if tag is None: self.sprite_menu.set_options(()) self.sequence_menu.set_options(("None", )) self.sprite_menu.sel_index = -1 self.sequence_menu.sel_index = 0 return data = tag.data.tagdata sequences = data.sequences.STEPTREE bitmaps = data.bitmaps.STEPTREE sequence_i = self.sequence_index.get() - 1 if sequence_i not in range(len(sequences)): return sequence = sequences[sequence_i] sprite_i = self.sprite_index.get() sprites = sequence.sprites.STEPTREE bitmap_index = sequence.first_bitmap_index + sprite_i x0, y0, x1, y1 = 0, 0, 1, 1 rx, ry = 0.5, 0.5 if sprite_i in range(len(sprites)): sprite = sprites[sprite_i] if sprite.bitmap_index not in range(len(bitmaps)): return bitmap_index = sprite.bitmap_index x0, y0, x1, y1 = sprite.left_side, sprite.top_side,\ sprite.right_side, sprite.bottom_side rx = x0 + sprite.registration_point_x ry = y0 + sprite.registration_point_y elif bitmap_index not in range(len(bitmaps)): return if bitmap_index != self.bitmap_index.get(): self.bitmap_menu.sel_index = bitmap_index bitmap = bitmaps[bitmap_index] mip = self.mipmap_index.get() w, h = max(bitmap.width>>mip, 1), max(bitmap.height>>mip, 1) x0, y0 = int(round(x0 * w)), int(round(y0 * h)) x1, y1 = int(round(x1 * w)), int(round(y1 * h)) rx, ry = int(round(rx * w)), int(round(ry * h)) rx0, rx1 = rx - 1, rx + 1 ry0, ry1 = ry - 1, ry + 1 if x1 < x0: x0, x1 = x1, x0 if y1 < y0: y0, y1 = y1, y0 if ry1 < ry0: ry0, ry1 = ry1, ry0 x0 -= 1; y0 -= 1 rx0 -= 1; ry0 -= 1 self.image_canvas.create_rectangle( (x0, y0, x1, y1), fill=None, dash=(2, 1), tags=SPRITE_RECTANGLE_TAG, outline=self.bitmap_canvas_outline_color) self.image_canvas.create_rectangle( (rx0, ry0, rx1, ry1), fill=None, tags=SPRITE_CENTER_TAG, outline=self.bitmap_canvas_bg_color) def change_textures(self, textures): BitmapDisplayFrame.change_textures(self, textures) tag = self.bitmap_tag if tag is None: return data = tag.data.tagdata options = {i+1: str(i) for i in range(len(data.sequences.STEPTREE))} options[0] = "None" if not (hasattr(self, "sprite_menu") and hasattr(self, "sequence_menu")): return self.sequence_menu.set_options(options) self.sequence_menu.sel_index = (self.sequence_menu.max_index >= 0) - 1
def __init__(self, *args, **kwargs): field_widget.FieldWidget.__init__(self, *args, **kwargs) self.u_node_widgets_by_u_index = {} if self.f_widget_parent is None: self.pack_padx = self.pack_pady = 0 kwargs.update(relief='flat', bd=0, highlightthickness=0, bg=self.default_bg_color) show_frame = bool( kwargs.pop('show_frame', not self.blocks_start_hidden)) if self.is_empty and self.hide_if_blank: show_frame = False tk.Frame.__init__(self, *args, **e_c.fix_kwargs(**kwargs)) self.show = tk.BooleanVar(self) self.show.set(show_frame) max_u_index = len(self.desc['CASE_MAP']) u_index = getattr(self.node, "u_index", None) if u_index is None: u_index = max_u_index toggle_text = '-' if show_frame else '+' self.title = tk.Frame(self, relief='raised') self.show_btn = ttk.Checkbutton(self.title, width=3, text=toggle_text, command=self.toggle_visible, style='ShowButton.TButton') self.title_label = tk.Label(self.title, text=self.gui_name, anchor='w', width=self.title_size, justify='left') self.title_label.font_type = "frame_title" self.sel_menu = ScrollMenu(self.title, f_widget_parent=self, sel_index=u_index, max_index=max_u_index, disabled=self.disabled, callback=self.select_option, option_getter=self.get_options) self.show_btn.pack(side="left") self.title_label.pack(side="left", fill="x", expand=True) self.sel_menu.pack(side="left", fill="x") self.title.pack(fill="x", expand=True) self.content = tk.Frame(self, relief="sunken") # make the default raw bytes union frame self.raw_frame = tk.Frame(self.content, relief="flat", bd=0) self.raw_label = tk.Label(self.raw_frame, text='DataUnion', width=self.title_size, anchor='w', disabledforeground=self.text_disabled_color) self.import_btn = ttk.Button(self.raw_frame, text='Import', command=self.import_node, width=7) self.export_btn = ttk.Button(self.raw_frame, text='Export', command=self.export_node, width=7) self.raw_label.pack(side="left", expand=True, fill='x') for w in (self.export_btn, self.import_btn): w.pack(side="left", padx=(0, 4), pady=2) self.populate() self._initialized = True
class DynamicEnumFrame(EnumFrame): options_sane = False def __init__(self, *args, **kwargs): kwargs.update(relief='flat', bd=0, highlightthickness=0) data_frame.DataFrame.__init__(self, *args, **kwargs) sel_index = -1 if self.node is None else self.node + 1 # make the widgets self.content = tk.Frame(self, relief='flat', bd=0) self.title_label = tk.Label( self.content, text=self.gui_name, justify='left', anchor='w', width=self.title_size, disabledforeground=self.text_disabled_color) self.sel_menu = ScrollMenu(self.content, f_widget_parent=self, menu_width=self.widget_width, sel_index=sel_index, max_index=0, disabled=self.disabled, default_text="<INVALID>", option_getter=self.get_options, callback=self.select_option) self.sel_menu.bind('<FocusIn>', self.flag_sanity_change) self.sel_menu.arrow_button.bind('<FocusIn>', self.flag_sanity_change) self.sel_menu.bind('<Enter>', self.flag_sanity_change) self.sel_menu.arrow_button.bind('<Enter>', self.flag_sanity_change) self.sel_menu.options_volatile = 'DYN_NAME_PATH' in self.desc if self.gui_name != '': self.title_label.pack(side="left", fill="x") self.content.pack(fill="x", expand=True) self.sel_menu.pack(side="left", fill="x") self.populate() self._initialized = True def edit_apply(self=None, *, edit_state, undo=True): state = edit_state attr_index = state.attr_index w_parent, parent = field_widget.FieldWidget.get_widget_and_node( nodepath=state.nodepath, tag_window=state.tag_window) if undo: parent[attr_index] = state.undo_node else: parent[attr_index] = state.redo_node if w_parent is not None: try: w = w_parent.f_widgets[w_parent.f_widget_ids_map[attr_index]] if w.desc is not state.desc: return w.sel_menu.sel_index = parent[attr_index] + 1 w.needs_flushing = False w.sel_menu.update_label() w.set_edited() except Exception: print(format_exc()) def generate_options(self, opt_index=None): desc = self.desc options = {0: "-1. NONE"} dyn_name_path = desc.get('DYN_NAME_PATH') if self.node is None: if opt_index is None: return options return None elif not dyn_name_path: print("Missing DYN_NAME_PATH path in dynamic enumerator.") print(self.parent.get_root().def_id, self.name) if opt_index is None: return options return None try: p_out, p_in = dyn_name_path.split('[DYN_I]') # We are ALWAYS going to go to the parent, so we need to slice if p_out.startswith('..'): p_out = p_out.split('.', 1)[-1] array = self.parent.get_neighbor(p_out) options_to_generate = range(len(array)) if opt_index is not None: options_to_generate = ((opt_index - 1, ) if opt_index - 1 in options_to_generate else ()) for i in options_to_generate: name = array[i].get_neighbor(p_in) if isinstance(name, list): name = repr(name).strip("[").strip("]") else: name = str(name) options[i + 1] = '%s. %s' % (i, name.split('\n')[0]) option_count = len(array) + 1 except Exception: print(format_exc()) option_count = 1 if opt_index is None: self.option_cache = options self.options_sane = True if self.sel_menu is not None: self.sel_menu.options_menu_sane = False self.sel_menu.max_index = option_count - 1 return options return options.get(opt_index, None) def reload(self): try: self.options_sane = False if self.disabled == self.sel_menu.disabled: pass elif self.disabled: self.sel_menu.disable() else: self.sel_menu.enable() self.generate_options() if self.node is not None: self.sel_menu.sel_index = self.node + 1 self.sel_menu.update_label() except Exception: print(format_exc()) populate = reload def select_option(self, opt_index=None): if None in (self.parent, self.node): return # make an edit state if self.node != opt_index - 1: self.set_edited() self.edit_create(undo_node=self.node, redo_node=opt_index - 1) self.sel_menu.sel_index = opt_index # since the node value is actually signed and can be -1, we'll # set entry 0 to be a node value of -1 and all other values # are one less than the entry index they are located in. self.node = self.parent[self.attr_index] = opt_index - 1 self.sel_menu.update_label() def flag_sanity_change(self, e=None): self.options_sane = self.sel_menu.options_menu_sane = ( not self.sel_menu.options_volatile) if not self.options_sane: self.option_cache = None
class BitmapDisplayFrame(BinillaWidget, tk.Frame): app_root = None root_frame_id = None bitmap_index = None # index of the bitmap being displayed mipmap_index = None # the mip level to display of that bitmap channel_index = None # how to display the bitmap: # 0=RGB or L, 1=A, 2=ARGB or AL, 3=R, 4=G, 5=B depth_index = None # since 3d bitmaps must be viewed in 2d slices, # this is the depth of the slice to display. cube_display_index = None # mode to display the cubemap in: # 0 == horizontal, 1 == vertical, # 2 == linear strip prev_bitmap_index = None prev_mipmap_index = None prev_channel_index = None prev_depth_index = None prev_cube_display_index = None changing_settings = False curr_depth = 0 depth_canvas = None depth_canvas_id = None depth_canvas_image_id = None default_bitmap_mapping = ((0, ), ) cubemap_cross_mapping = ( (-1, 2), (1, 4, 0, 5), (-1, 3), ) cubemap_strip_mapping = ((0, 1, 2, 3, 4, 5), ) _image_handlers = None image_canvas_ids = () # does NOT include the depth_canvas_id textures = () # List of textures ready to be loaded into arbytmap. # Structure is as follows: [ (tex_block0, tex_info0), # (tex_block1, tex_info1), # (tex_block2, tex_info2), ... ] temp_root = os.path.join(tempfile.gettempdir(), "arbytmaps") temp_dir = '' def __init__(self, master, *args, **kwargs): BinillaWidget.__init__(self) self.temp_root = kwargs.pop('temp_root', self.temp_root) textures = kwargs.pop('textures', ()) app_root = kwargs.pop('app_root', ()) self.image_canvas_ids = [] self.textures = [] self._image_handlers = {} temp_name = str(int(random.random() * (1 << 32))) self.temp_dir = os.path.join(self.temp_root, temp_name) kwargs.update(relief='flat', bd=self.frame_depth, bg=self.default_bg_color) tk.Frame.__init__(self, master, *args, **kwargs) self.bitmap_index = tk.IntVar(self) self.mipmap_index = tk.IntVar(self) self.depth_index = tk.IntVar(self) self.channel_index = tk.IntVar(self) self.cube_display_index = tk.IntVar(self) self.root_canvas = tk.Canvas(self, highlightthickness=0) self.root_frame = tk.Frame(self.root_canvas, highlightthickness=0) # create the root_canvas and the root_frame within the canvas self.controls_frame0 = tk.Frame(self.root_frame, highlightthickness=0) self.controls_frame1 = tk.Frame(self.root_frame, highlightthickness=0) self.controls_frame2 = tk.Frame(self.root_frame, highlightthickness=0) self.image_root_frame = tk.Frame(self.root_frame, highlightthickness=0) self.image_canvas = tk.Canvas(self.image_root_frame, highlightthickness=0, bg=self.bitmap_canvas_bg_color) self.depth_canvas = tk.Canvas(self.image_canvas, highlightthickness=0, bg=self.bitmap_canvas_bg_color) self.bitmap_menu = ScrollMenu(self.controls_frame0, menu_width=7, variable=self.bitmap_index, can_scroll=True) self.mipmap_menu = ScrollMenu(self.controls_frame1, menu_width=7, variable=self.mipmap_index, can_scroll=True) self.depth_menu = ScrollMenu(self.controls_frame2, menu_width=7, variable=self.depth_index, can_scroll=True) self.channel_menu = ScrollMenu(self.controls_frame0, menu_width=9, variable=self.channel_index, can_scroll=True) self.cube_display_menu = ScrollMenu(self.controls_frame1, menu_width=9, variable=self.cube_display_index, options=("cross", "linear"), can_scroll=True) self.save_button = ttk.Button(self.controls_frame2, width=11, text="Browse", command=self.save_as) self.depth_menu.default_text = self.mipmap_menu.default_text =\ self.bitmap_menu.default_text =\ self.channel_menu.default_text =\ self.cube_display_menu.default_text = "" labels = [] labels.append(tk.Label(self.controls_frame0, text="Bitmap index")) labels.append(tk.Label(self.controls_frame1, text="Mipmap level")) labels.append(tk.Label(self.controls_frame2, text="Depth level")) labels.append(tk.Label(self.controls_frame0, text="Channels")) labels.append(tk.Label(self.controls_frame1, text="Cubemap display")) labels.append(tk.Label(self.controls_frame2, text="Save to file")) for lbl in labels: lbl.config(width=15, anchor='w', bg=self.default_bg_color, fg=self.text_normal_color, disabledforeground=self.text_disabled_color) self.hsb = tk.Scrollbar(self, orient="horizontal", command=self.root_canvas.xview) self.vsb = tk.Scrollbar(self, orient="vertical", command=self.root_canvas.yview) self.root_canvas.config(xscrollcommand=self.hsb.set, xscrollincrement=1, yscrollcommand=self.vsb.set, yscrollincrement=1) for w in [ self.root_frame, self.root_canvas, self.image_canvas, self.controls_frame0, self.controls_frame1, self.controls_frame2 ] + labels: if e_c.IS_LNX: w.bind('<Shift-4>', self.mousewheel_scroll_x) w.bind('<Shift-5>', self.mousewheel_scroll_x) w.bind('<4>', self.mousewheel_scroll_y) w.bind('<5>', self.mousewheel_scroll_y) else: w.bind('<Shift-MouseWheel>', self.mousewheel_scroll_x) w.bind('<MouseWheel>', self.mousewheel_scroll_y) # pack everything # pack in this order so scrollbars aren't shrunk self.root_frame_id = self.root_canvas.create_window( (0, 0), anchor="nw", window=self.root_frame) self.hsb.pack(side='bottom', fill='x', anchor='nw') self.vsb.pack(side='right', fill='y', anchor='nw') self.root_canvas.pack(fill='both', anchor='nw', expand=True) self.controls_frame0.pack(side='top', fill='x', anchor='nw') self.controls_frame1.pack(side='top', fill='x', anchor='nw') self.controls_frame2.pack(side='top', fill='x', anchor='nw') self.image_root_frame.pack(fill='both', anchor='nw', expand=True) self.image_canvas.pack(fill='both', side='right', anchor='nw', expand=True) padx = self.horizontal_padx pady = self.horizontal_pady for lbl in labels[:3]: lbl.pack(side='left', padx=(25, 0), pady=pady) self.bitmap_menu.pack(side='left', padx=padx, pady=pady) self.mipmap_menu.pack(side='left', padx=padx, pady=pady) self.depth_menu.pack(side='left', padx=padx, pady=pady) for lbl in labels[3:]: lbl.pack(side='left', padx=(15, 0), pady=pady) self.save_button.pack(side='left', padx=padx, pady=pady) self.channel_menu.pack(side='left', padx=padx, pady=pady) self.cube_display_menu.pack(side='left', padx=padx, pady=pady) self.change_textures(textures) self.write_trace(self.bitmap_index, self.settings_changed) self.write_trace(self.mipmap_index, self.settings_changed) self.write_trace(self.depth_index, self.settings_changed) self.write_trace(self.cube_display_index, self.settings_changed) self.write_trace(self.channel_index, self.settings_changed) self.apply_style() def destroy(self): try: self.clear_canvas() self.clear_depth_canvas() except Exception: pass try: del self.textures[:] except Exception: pass try: del self._image_handlers[:] except Exception: pass self.image_canvas_ids = self._image_handlers = None self.textures = None tk.Frame.destroy(self) self.delete_all_traces() self.delete_all_widget_refs() gc.collect() @property def active_image_handler(self): b = self.bitmap_index.get() if b not in range(len(self.textures)) or not import_arbytmap(): return None elif b not in self._image_handlers: # make a new PhotoImageHandler if one doesnt exist already self._image_handlers[b] = PhotoImageHandler( self.textures[b][0], self.textures[b][1], self.temp_dir) return self._image_handlers[b] @property def should_update(self): return (self.prev_bitmap_index != self.bitmap_index.get() or self.prev_mipmap_index != self.mipmap_index.get() or self.prev_channel_index != self.channel_index.get()) def mousewheel_scroll_x(self, e): # prevent scrolling if the root_canvas.bbox width >= canvas width bbox = self.root_canvas.bbox(tk.ALL) if not bbox or (self.root_canvas.winfo_width() >= bbox[2] - bbox[0]): return delta = getattr(self.app_root, "scroll_increment_x", 20) self.root_canvas.xview_scroll(int(get_mouse_delta(e) * delta), "units") def mousewheel_scroll_y(self, e): # prevent scrolling if the root_canvas.bbox height >= canvas height bbox = self.root_canvas.bbox(tk.ALL) if not bbox or (self.root_canvas.winfo_height() >= bbox[3] - bbox[1]): return delta = getattr(self.app_root, "scroll_increment_y", 20) self.root_canvas.yview_scroll(int(get_mouse_delta(e) * delta), "units") def update_scroll_regions(self): if not self.image_canvas_ids and not self.depth_canvas_id: return rf = self.root_frame region = self.image_canvas.bbox(tk.ALL) # bbox region isn't actually x, y, w, h, but if the # origin is at (0,0) we can treat it like that if region is None: x, y, w, h = (0, 0, 0, 0) else: x, y, w, h = region self.image_canvas.config(scrollregion=(x, y, w, h)) rf.update_idletasks() max_w = w total_h = h for widget in self.root_frame.children.values(): if widget is not self.image_root_frame: max_w = max(widget.winfo_reqwidth(), max_w) total_h += widget.winfo_reqheight() self.root_canvas.itemconfigure(self.root_frame_id, width=max_w, height=total_h) self.root_canvas.config(scrollregion=(0, 0, max_w, total_h)) def save_as(self, e=None, initial_dir=None): handler = self.active_image_handler if handler is None: return None fp = asksaveasfilename(initialdir=initial_dir, defaultextension='.dds', title="Save bitmap as...", parent=self, filetypes=(("DirectDraw surface", "*.dds"), ('Portable network graphics', '*.png'), ('Truevision graphics adapter', '*.tga'), ('Raw pixel data', '*.bin'))) fp, ext = os.path.splitext(fp) if not fp: return elif not ext: ext = ".dds" mip_levels = "all" if ext.lower() == ".dds" else self.mipmap_index.get( ) handler.arby.save_to_file(output_path=fp, ext=ext, overwrite=True, mip_levels=mip_levels, bitmap_indexes="all", keep_alpha=handler.channels.get("A"), swizzle_mode=False, channel_mapping=handler.channel_mapping, target_big_endian=False, tile_mode=False) def clear_depth_canvas(self): self.depth_canvas.delete(tk.ALL) self.prev_depth_index = None self.prev_cube_display_index = None self.depth_canvas_image_id = None def clear_canvas(self): self.image_canvas.delete(tk.ALL) self.clear_depth_canvas() self.depth_canvas_id = None self.image_canvas_ids = [] def hide_depth_canvas(self): if self.depth_canvas_id is not None: self.image_canvas.delete(self.depth_canvas_id) self.depth_canvas_id = None def show_depth_canvas(self): self.depth_canvas_id = self.image_canvas.create_window( (0, 0), anchor="nw", window=self.depth_canvas) def change_textures(self, textures): assert hasattr(textures, '__iter__') for tex in textures: assert len(tex) == 2 assert isinstance(tex[1], dict) if self._image_handlers: del self._image_handlers if self.textures: del self.textures[:] self.textures = list(textures) self._image_handlers = {} self.bitmap_index.set(0) self.mipmap_index.set(0) self.channel_index.set(0) self.depth_index.set(0) self.cube_display_index.set(0) self.bitmap_menu.set_options(range(len(textures))) self.prev_bitmap_index = None self.prev_mipmap_index = None self.prev_channel_index = None self.prev_depth_index = None self.prev_cube_display_index = None self.update_bitmap(force=True) def get_images(self): image_handler = self.active_image_handler if not image_handler: return images = image_handler.get_images(mip_levels=self.mipmap_index.get()) return tuple(images[i] for i in sorted(images.keys())) def settings_changed(self, *args, force=False): handler = self.active_image_handler force = False if not handler: return elif self.changing_settings: return elif self.prev_bitmap_index != self.bitmap_index.get(): force = True elif self.prev_mipmap_index != self.mipmap_index.get(): force = True elif self.prev_channel_index != self.channel_index.get(): force = True elif self.prev_depth_index != self.depth_index.get(): pass elif self.prev_cube_display_index != self.cube_display_index.get(): force = True else: return self.changing_settings = True new_mip_options = range(handler.max_mipmap + 1) if self.mipmap_index.get() not in new_mip_options: self.mipmap_index.set(0) max_depth = handler.mip_depth(self.mipmap_index.get()) self.mipmap_menu.set_options(new_mip_options) self.depth_menu.set_options(range(max_depth)) if self.depth_menu.sel_index > max_depth - 1: self.depth_menu.sel_index = max_depth - 1 channel_count = handler.channel_count if channel_count <= 2: opts = ("Luminance", "Alpha", "AL") else: opts = ("RGB", "Alpha", "ARGB", "Red", "Green", "Blue") self.channel_menu.set_options(opts) try: handler.set_channel_mode(self.channel_index.get()) self.update_bitmap(force=force) self.changing_settings = False except Exception: self.changing_settings = False raise def update_bitmap(self, *args, force=False): handler = self.active_image_handler if handler is None: return None tex_type = handler.tex_type if tex_type == "2D": self._display_2d_bitmap(force) elif tex_type == "3D": self._display_3d_bitmap(force) elif tex_type == "CUBE": self._display_cubemap(force) self.prev_bitmap_index = self.bitmap_index.get() self.prev_mipmap_index = self.mipmap_index.get() self.prev_channel_index = self.channel_index.get() self.prev_depth_index = self.depth_index.get() self.prev_cube_display_index = self.cube_display_index.get() def _display_cubemap(self, force=False, bitmap_mapping=None): mapping_type = self.cube_display_index.get() if bitmap_mapping is None: if mapping_type == 0: bitmap_mapping = self.cubemap_cross_mapping else: bitmap_mapping = self.cubemap_strip_mapping self._display_2d_bitmap(force, bitmap_mapping) def _display_2d_bitmap(self, force=False, bitmap_mapping=None): images = self.get_images() if not images or not (self.should_update or force): return w = max(image.width() for image in images) h = max(image.height() for image in images) if bitmap_mapping is None: bitmap_mapping = self.default_bitmap_mapping self.clear_canvas() # place the bitmaps on the canvas y = 0 for line in bitmap_mapping: max_column_ct = max(0, len(line)) x = 0 for image_index in line: if image_index in range(len(images)): # place the cube face on the canvas self.image_canvas_ids.append( self.image_canvas.create_image( (x, y), anchor="nw", image=images[image_index], tags=("BITMAP", "2D_BITMAP"))) x += w y += h self.update_scroll_regions() def _display_3d_bitmap(self, force=False): if self.should_update or self.depth_canvas_image_id is None or force: self.clear_canvas() self.show_depth_canvas() handler = self.active_image_handler images = self.get_images() if not (images and handler): return m = self.mipmap_index.get() w, h = images[0].width(), handler.mip_height(m) self.curr_depth = handler.mip_depth(m) # place the bitmap on the canvas self.depth_canvas_image_id = self.depth_canvas.create_image( (0, 0), anchor="nw", image=images[0], tags=("BITMAP", "3D_BITMAP")) self.image_canvas.itemconfig(self.depth_canvas_id, width=w, height=h) self.depth_canvas.config(scrollregion="0 0 %s %s" % (w, h)) self.update_scroll_regions() self.depth_canvas.coords( self.depth_canvas_image_id, (0, -self.depth_index.get() * self.curr_depth))
def __init__(self, master, *args, **kwargs): BinillaWidget.__init__(self) self.temp_root = kwargs.pop('temp_root', self.temp_root) textures = kwargs.pop('textures', ()) app_root = kwargs.pop('app_root', ()) self.image_canvas_ids = [] self.textures = [] self._image_handlers = {} temp_name = str(int(random.random() * (1 << 32))) self.temp_dir = os.path.join(self.temp_root, temp_name) kwargs.update(relief='flat', bd=self.frame_depth, bg=self.default_bg_color) tk.Frame.__init__(self, master, *args, **kwargs) self.bitmap_index = tk.IntVar(self) self.mipmap_index = tk.IntVar(self) self.depth_index = tk.IntVar(self) self.channel_index = tk.IntVar(self) self.cube_display_index = tk.IntVar(self) self.root_canvas = tk.Canvas(self, highlightthickness=0) self.root_frame = tk.Frame(self.root_canvas, highlightthickness=0) # create the root_canvas and the root_frame within the canvas self.controls_frame0 = tk.Frame(self.root_frame, highlightthickness=0) self.controls_frame1 = tk.Frame(self.root_frame, highlightthickness=0) self.controls_frame2 = tk.Frame(self.root_frame, highlightthickness=0) self.image_root_frame = tk.Frame(self.root_frame, highlightthickness=0) self.image_canvas = tk.Canvas(self.image_root_frame, highlightthickness=0, bg=self.bitmap_canvas_bg_color) self.depth_canvas = tk.Canvas(self.image_canvas, highlightthickness=0, bg=self.bitmap_canvas_bg_color) self.bitmap_menu = ScrollMenu(self.controls_frame0, menu_width=7, variable=self.bitmap_index, can_scroll=True) self.mipmap_menu = ScrollMenu(self.controls_frame1, menu_width=7, variable=self.mipmap_index, can_scroll=True) self.depth_menu = ScrollMenu(self.controls_frame2, menu_width=7, variable=self.depth_index, can_scroll=True) self.channel_menu = ScrollMenu(self.controls_frame0, menu_width=9, variable=self.channel_index, can_scroll=True) self.cube_display_menu = ScrollMenu(self.controls_frame1, menu_width=9, variable=self.cube_display_index, options=("cross", "linear"), can_scroll=True) self.save_button = ttk.Button(self.controls_frame2, width=11, text="Browse", command=self.save_as) self.depth_menu.default_text = self.mipmap_menu.default_text =\ self.bitmap_menu.default_text =\ self.channel_menu.default_text =\ self.cube_display_menu.default_text = "" labels = [] labels.append(tk.Label(self.controls_frame0, text="Bitmap index")) labels.append(tk.Label(self.controls_frame1, text="Mipmap level")) labels.append(tk.Label(self.controls_frame2, text="Depth level")) labels.append(tk.Label(self.controls_frame0, text="Channels")) labels.append(tk.Label(self.controls_frame1, text="Cubemap display")) labels.append(tk.Label(self.controls_frame2, text="Save to file")) for lbl in labels: lbl.config(width=15, anchor='w', bg=self.default_bg_color, fg=self.text_normal_color, disabledforeground=self.text_disabled_color) self.hsb = tk.Scrollbar(self, orient="horizontal", command=self.root_canvas.xview) self.vsb = tk.Scrollbar(self, orient="vertical", command=self.root_canvas.yview) self.root_canvas.config(xscrollcommand=self.hsb.set, xscrollincrement=1, yscrollcommand=self.vsb.set, yscrollincrement=1) for w in [ self.root_frame, self.root_canvas, self.image_canvas, self.controls_frame0, self.controls_frame1, self.controls_frame2 ] + labels: if e_c.IS_LNX: w.bind('<Shift-4>', self.mousewheel_scroll_x) w.bind('<Shift-5>', self.mousewheel_scroll_x) w.bind('<4>', self.mousewheel_scroll_y) w.bind('<5>', self.mousewheel_scroll_y) else: w.bind('<Shift-MouseWheel>', self.mousewheel_scroll_x) w.bind('<MouseWheel>', self.mousewheel_scroll_y) # pack everything # pack in this order so scrollbars aren't shrunk self.root_frame_id = self.root_canvas.create_window( (0, 0), anchor="nw", window=self.root_frame) self.hsb.pack(side='bottom', fill='x', anchor='nw') self.vsb.pack(side='right', fill='y', anchor='nw') self.root_canvas.pack(fill='both', anchor='nw', expand=True) self.controls_frame0.pack(side='top', fill='x', anchor='nw') self.controls_frame1.pack(side='top', fill='x', anchor='nw') self.controls_frame2.pack(side='top', fill='x', anchor='nw') self.image_root_frame.pack(fill='both', anchor='nw', expand=True) self.image_canvas.pack(fill='both', side='right', anchor='nw', expand=True) padx = self.horizontal_padx pady = self.horizontal_pady for lbl in labels[:3]: lbl.pack(side='left', padx=(25, 0), pady=pady) self.bitmap_menu.pack(side='left', padx=padx, pady=pady) self.mipmap_menu.pack(side='left', padx=padx, pady=pady) self.depth_menu.pack(side='left', padx=padx, pady=pady) for lbl in labels[3:]: lbl.pack(side='left', padx=(15, 0), pady=pady) self.save_button.pack(side='left', padx=padx, pady=pady) self.channel_menu.pack(side='left', padx=padx, pady=pady) self.cube_display_menu.pack(side='left', padx=padx, pady=pady) self.change_textures(textures) self.write_trace(self.bitmap_index, self.settings_changed) self.write_trace(self.mipmap_index, self.settings_changed) self.write_trace(self.depth_index, self.settings_changed) self.write_trace(self.cube_display_index, self.settings_changed) self.write_trace(self.channel_index, self.settings_changed) self.apply_style()
def __init__(self, *args, **kwargs): self.settings = settings = kwargs.pop('settings', {}) BinillaWidget.__init__(self, *args, **kwargs) tk.Toplevel.__init__(self, *args, **kwargs) try: self.iconbitmap(e_c.REFINERY_ICON_PATH) except Exception: if not e_c.IS_LNX: print("Could not load window icon.") self.geometry("550x350") self.minsize(width=450, height=350) self.resizable(1, 1) self.title("Settings") self.tabs = ttk.Notebook(self) self.dirs_frame = tk.Frame(self.tabs) self.extract_frame = tk.Frame(self.tabs) self.data_extract_frame = tk.Frame(self.tabs) self.tag_fixup_frame = tk.Frame(self.tabs) self.deprotect_frame = tk.Frame(self.tabs) self.heuristics_frame = tk.Frame(self.tabs) self.fonts_frame = tk.Frame(self.tabs) self.other_frame = tk.Frame(self.tabs) self.tabs.add(self.dirs_frame, text="Directories") self.tabs.add(self.extract_frame, text="Extraction") self.tabs.add(self.data_extract_frame, text="Data extraction") self.tabs.add(self.tag_fixup_frame, text="Tag fixup") self.tabs.add(self.deprotect_frame, text="Deprotection") self.tabs.add(self.heuristics_frame, text="Heuristics") self.tabs.add(self.fonts_frame, text="Fonts") self.tabs.add(self.other_frame, text="Other") font_families = tuple(sorted(tkinter.font.families())) self.tags_dir_frame = tk.LabelFrame( self.dirs_frame, text="Default tags extraction folder") self.data_dir_frame = tk.LabelFrame( self.dirs_frame, text="Default data extraction folder") self.tags_list_frame = tk.LabelFrame( self.dirs_frame, text="Tags list log (erase to disable logging)") for attr in ("overwrite", "recursive", "rename_scnr_dups", "decode_adpcm", "bitmap_extract_keep_alpha", "generate_comp_verts", "generate_uncomp_verts", "force_lower_case_paths", "fix_tag_classes", "autoload_resources", "extract_yelo_cheape", "use_minimum_priorities", "use_heuristics", "rename_cached_tags", "show_all_fields", "show_structure_meta", "edit_all_fields", "allow_corrupt", "valid_tag_paths_are_accurate", "limit_tag_path_lengths", "scrape_tag_paths_from_scripts", "shallow_ui_widget_nesting", "fix_tag_index_offset", "use_tag_index_for_script_names", "do_printout", "print_heuristic_name_changes", "use_scenario_names_for_script_names", "skip_seen_tags_during_queue_processing", "disable_safe_mode", "disable_tag_cleaning",): object.__setattr__(self, attr, settings.get(attr, tk.IntVar(self))) for attr in ("bitmap_extract_format", "globals_overwrite_mode", "tags_dir", "data_dir", "tagslist_path"): object.__setattr__(self, attr, settings.get(attr, tk.StringVar(self))) # tags directory self.tags_dir_entry = tk.Entry( self.tags_dir_frame, state='disabled', textvariable=self.tags_dir) self.tags_dir_browse_button = tk.Button( self.tags_dir_frame, text="Browse", command=self.tags_dir_browse, width=6) # data directory self.data_dir_entry = tk.Entry( self.data_dir_frame, state='disabled', textvariable=self.data_dir) self.data_dir_browse_button = tk.Button( self.data_dir_frame, text="Browse", command=self.data_dir_browse, width=6) # tags list self.tags_list_entry = tk.Entry( self.tags_list_frame, textvariable=self.tagslist_path) self.browse_tags_list_button = tk.Button( self.tags_list_frame, text="Browse", command=self.tags_list_browse, width=6) self.rename_scnr_dups_cbtn = tk.Checkbutton( self.tag_fixup_frame, text=( "Rename duplicate camera points, cutscene\n"+ "flags, and recorded animations in scenario"), variable=self.rename_scnr_dups, justify="left") self.generate_comp_verts_cbtn = tk.Checkbutton( self.tag_fixup_frame, text="Generate compressed lightmap vertices", variable=self.generate_comp_verts) self.generate_uncomp_verts_cbtn = tk.Checkbutton( self.tag_fixup_frame, text="Generate uncompressed lightmap vertices", variable=self.generate_uncomp_verts) self.dont_touch_frame = tk.LabelFrame( self.tag_fixup_frame, text="ONLY CHECK THESE IF YOU ARE NOT DEALING WITH PROTECTED MAPS") self.disable_safe_mode_cbtn = tk.Checkbutton( self.dont_touch_frame, variable=self.disable_safe_mode, justify="left", text="Disable safe-mode") self.disable_tag_cleaning_cbtn = tk.Checkbutton( self.dont_touch_frame, variable=self.disable_tag_cleaning, justify="left", text="Disable cleaning errors from tags when reading them.") self.overwrite_cbtn = tk.Checkbutton( self.extract_frame, text="Overwrite files(not recommended)", variable=self.overwrite) self.recursive_cbtn = tk.Checkbutton( self.extract_frame, text="Recursive extraction", variable=self.recursive) self.do_printout_cbtn = tk.Checkbutton( self.extract_frame, text="Print extracted file names", variable=self.do_printout) self.force_lower_case_paths_cbtn = tk.Checkbutton( self.extract_frame, text="Force all tag paths to lowercase", variable=self.force_lower_case_paths) self.skip_seen_tags_during_queue_processing_cbtn = tk.Checkbutton( self.extract_frame, text="During processing, skip any tags that were already extracted", variable=self.skip_seen_tags_during_queue_processing) self.globals_overwrite_mode_frame = tk.LabelFrame( self.extract_frame, relief="flat", text="When to overwrite an existing globals.globals") sel_index = self.globals_overwrite_mode.get() if sel_index not in range(len(globals_overwrite_gui_names)): sel_index = 0 self.globals_overwrite_mode_menu = ScrollMenu( self.globals_overwrite_mode_frame, sel_index=sel_index, variable=self.globals_overwrite_mode, menu_width=50, options=globals_overwrite_gui_names) self.decode_adpcm_cbtn = tk.Checkbutton( self.data_extract_frame, variable=self.decode_adpcm, text="Decode Xbox audio") self.bitmap_extract_frame = tk.LabelFrame( self.data_extract_frame, relief="flat", text="Bitmap extraction format") self.bitmap_extract_keep_alpha_cbtn = tk.Checkbutton( self.bitmap_extract_frame, variable=self.bitmap_extract_keep_alpha, text="Preserve alpha when extracting to PNG") self.use_tag_index_for_script_names_cbtn = tk.Checkbutton( self.data_extract_frame, variable=self.use_tag_index_for_script_names, text=("When extracting scripts, redirect tag references to\n" "what the tag is currently named(guarantees scripts\n" "point to a valid tag, even if you rename them)"), justify="left") self.use_scenario_names_for_script_names_cbtn = tk.Checkbutton( self.data_extract_frame, variable=self.use_scenario_names_for_script_names, text=("When extracting scripts, extract names for encounters,\n" "command lists, scripts, cutscene titles/camera points/flags,\n" "trigger volumes, recorded animations, ai conversations,\n" "object names, device groups, and player starting profiles\n" "from the scenarios reflexives, rather than script strings."), justify="left") try: sel_index = bitmap_file_formats.index(self.bitmap_extract_format.get()) except Exception: sel_index = 0 self.bitmap_extract_format_menu = ScrollMenu( self.bitmap_extract_frame, str_variable=self.bitmap_extract_format, menu_width=10, options=bitmap_file_formats, sel_index=sel_index) self.fix_tag_classes_cbtn = tk.Checkbutton( self.deprotect_frame, text="Fix tag classes", variable=self.fix_tag_classes) self.use_heuristics_cbtn = tk.Checkbutton( self.deprotect_frame, text="Use heuristic deprotection methods", variable=self.use_heuristics) self.scrape_tag_paths_from_scripts_cbtn = tk.Checkbutton( self.deprotect_frame, text="Scrape tag paths from scenario scripts", variable=self.scrape_tag_paths_from_scripts) self.rename_cached_tags_cbtn = tk.Checkbutton( self.deprotect_frame, text=("Rename cached tags using tag paths in\n" "bitmaps/loc/sounds resource maps"), variable=self.rename_cached_tags, justify="left") self.limit_tag_path_lengths_cbtn = tk.Checkbutton( self.deprotect_frame, text="Limit tag paths to 254 characters (tool.exe limitation)", variable=self.limit_tag_path_lengths) self.fix_tag_index_offset_cbtn = tk.Checkbutton( self.deprotect_frame, text=("Fix tag index offset when saving\n" "WARNING: Can corrupt certain maps"), variable=self.fix_tag_index_offset, justify='left') self.valid_tag_paths_are_accurate_cbtn = tk.Checkbutton( self.heuristics_frame, text="Do not rename non-protected tag paths", variable=self.valid_tag_paths_are_accurate) self.shallow_ui_widget_nesting_cbtn = tk.Checkbutton( self.heuristics_frame, text="Use shallow ui_widget_definition nesting", variable=self.shallow_ui_widget_nesting) self.use_fast_heuristics_cbtn = tk.Checkbutton( self.heuristics_frame, text="Use fast heuristics", variable=self.use_minimum_priorities) self.print_heuristic_progress_cbtn = tk.Checkbutton( self.heuristics_frame, text=("Print heuristic tag path changes"), variable=self.print_heuristic_name_changes, justify='left') font_frame_widgets = {} for font_type in sorted(self.font_settings): if font_type not in ("default", "treeview", "console", "heading", "heading_small", "frame_title",): continue if font_type == "console": font_type_name_text = "Map info" elif font_type == "treeview": font_type_name_text = "Map contents / Extraction queue" elif font_type == "heading_small": font_type_name_text = "Map contents columns / Settings tabs" else: font_type_name_text = font_type.replace("_", " ").capitalize() font_frame = tk.LabelFrame( self.fonts_frame, text=font_type_name_text) font_info = self.font_settings[font_type] try: sel_index = font_families.index(font_info.family) except Exception: sel_index = -1 family_var = tk.StringVar(self) size_var = tk.StringVar(self, str(font_info.size)) weight_var = tk.IntVar(self, int(font_info.weight == "bold")) self.write_trace(family_var, lambda *a, v=family_var, t=font_type: self.update_font_family(v, t)) self.write_trace(size_var, lambda *a, v=size_var, t=font_type: self.update_font_size(v, t)) self.write_trace(weight_var, lambda *a, v=weight_var, t=font_type: self.update_font_weight(v, t)) family_label = tk.Label(font_frame, text="Family ") family_menu = ScrollMenu( font_frame, str_variable=family_var, menu_width=20, options=font_families, sel_index=sel_index) weight_btn = tk.Checkbutton(font_frame, text="bold", variable=weight_var) size_label = tk.Label(font_frame, text="Size ") size_menu = tk.Spinbox( font_frame, width=3, state="readonly", textvariable=size_var, from_=0, to=200) font_frame_widgets[font_type_name_text] = ( font_frame, (family_label, family_menu, weight_btn, size_label, size_menu)) self.apply_fonts_btn = tk.Button( self.fonts_frame, text="Apply changes", command=self.apply_fonts) self.autoload_resources_cbtn = tk.Checkbutton( self.other_frame, text=("Load resource maps automatically\n" + "when loading a non-resource map"), variable=self.autoload_resources, justify="left") self.extract_yelo_cheape_cbtn = tk.Checkbutton( self.other_frame, variable=self.extract_yelo_cheape, text="Extract cheape.map when extracting from yelo maps") self.show_all_fields_cbtn = tk.Checkbutton( self.other_frame, variable=self.show_all_fields, text="Show hidden fields when viewing metadata") self.show_structure_meta_cbtn = tk.Checkbutton( self.other_frame, variable=self.show_structure_meta, text="Show hidden meta structure fields when viewing metadata") self.edit_all_fields_cbtn = tk.Checkbutton( self.other_frame, variable=self.edit_all_fields, text="Allow editing all fields when viewing metadata") self.allow_corrupt_cbtn = tk.Checkbutton( self.other_frame, variable=self.allow_corrupt, text="Allow previewing corrupt tags") # pack everything self.tabs.pack(fill="both", expand=True) for w in (self.tags_dir_frame, self.data_dir_frame, self.tags_list_frame): w.pack(padx=4, pady=2, fill="x") for w in (self.overwrite_cbtn, self.recursive_cbtn, self.do_printout_cbtn, self.force_lower_case_paths_cbtn, self.skip_seen_tags_during_queue_processing_cbtn, self.globals_overwrite_mode_frame): w.pack(padx=4, anchor='w') for w in (self.bitmap_extract_keep_alpha_cbtn, self.bitmap_extract_format_menu, self.globals_overwrite_mode_menu): w.pack(padx=16, anchor='w') for w in (self.decode_adpcm_cbtn, self.bitmap_extract_frame, self.use_tag_index_for_script_names_cbtn, self.use_scenario_names_for_script_names_cbtn): w.pack(padx=4, anchor='w') for w in (self.rename_scnr_dups_cbtn, self.generate_uncomp_verts_cbtn, self.generate_comp_verts_cbtn): w.pack(padx=4, anchor='w') self.dont_touch_frame.pack(padx=4, anchor='w', expand=True, fill="both") for w in (self.disable_safe_mode_cbtn, self.disable_tag_cleaning_cbtn): w.pack(padx=4, anchor='w') for w in (self.fix_tag_classes_cbtn, self.use_heuristics_cbtn, self.fix_tag_index_offset_cbtn, self.rename_cached_tags_cbtn, self.limit_tag_path_lengths_cbtn, self.scrape_tag_paths_from_scripts_cbtn, ): w.pack(padx=4, anchor='w') for w in (self.print_heuristic_progress_cbtn, self.valid_tag_paths_are_accurate_cbtn, self.shallow_ui_widget_nesting_cbtn, self.use_fast_heuristics_cbtn, ): w.pack(padx=4, anchor='w') for w in (self.autoload_resources_cbtn, self.extract_yelo_cheape_cbtn, self.show_all_fields_cbtn, self.show_structure_meta_cbtn, self.edit_all_fields_cbtn, self.allow_corrupt_cbtn, ): w.pack(padx=4, anchor='w') for k in sorted(font_frame_widgets): font_frame, font_widgets = font_frame_widgets[k] font_frame.pack(padx=4, anchor='w', expand=True, fill="x") for w in font_widgets: w.pack(padx=(0, 4), pady=2, side='left', fill='both', expand=isinstance(w, ScrollMenu)) self.apply_fonts_btn.pack(padx=4, anchor='w', expand=True, fill="both") for w1, w2 in ((self.tags_dir_entry, self.tags_dir_browse_button), (self.data_dir_entry, self.data_dir_browse_button), (self.tags_list_entry, self.browse_tags_list_button)): w1.pack(padx=(4, 0), pady=2, side='left', expand=True, fill='x') w2.pack(padx=(0, 4), pady=2, side='left') # make the window not show up on the start bar self.transient(self.master) self.apply_style()
class EnumFrame(data_frame.DataFrame): '''Used for enumerator nodes. When clicked, creates a dropdown box of all available enumerator options.''' option_cache = None sel_menu = None def __init__(self, *args, **kwargs): kwargs.update(relief='flat', bd=0, highlightthickness=0) data_frame.DataFrame.__init__(self, *args, **kwargs) try: sel_index = self.node.get_index() except Exception: sel_index = -1 # make the widgets self.content = tk.Frame(self, relief='flat', bd=0) self.display_comment() self.title_label = tk.Label( self.content, text=self.gui_name, justify='left', anchor='w', width=self.title_size, disabledforeground=self.text_disabled_color) self.sel_menu = ScrollMenu(self.content, f_widget_parent=self, menu_width=self.widget_width, sel_index=sel_index, max_index=self.desc.get('ENTRIES', 0) - 1, disabled=self.disabled, default_text="<INVALID>", option_getter=self.get_options, callback=self.select_option) if self.gui_name != '': self.title_label.pack(side="left", fill="x") self.content.pack(fill="x", expand=True) self.sel_menu.pack(side="left", fill="x") self.populate() self.pose_fields() self._initialized = True @property def widget_width(self): desc = self.desc width = desc.get('WIDGET_WIDTH', self.enum_menu_width) if width <= 0: for s in self.get_options().values(): width = max(width, len(s)) return width def apply_style(self, seen=None): self.sel_menu.menu_width = self.widget_width data_frame.DataFrame.apply_style(self, seen) def unload_node_data(self): field_widget.FieldWidget.unload_node_data(self) self.sel_menu.update_label(" ") def set_disabled(self, disable=True): disable = disable or not self.editable if self.node is None and not disable: return if bool(disable) != self.disabled and getattr(self, "sel_menu", None): self.sel_menu.set_disabled(disable) data_frame.DataFrame.set_disabled(self, disable) def flush(self): pass def edit_apply(self=None, *, edit_state, undo=True): state = edit_state w, node = field_widget.FieldWidget.get_widget_and_node( nodepath=state.nodepath, tag_window=state.tag_window) if undo: node.data = state.undo_node else: node.data = state.redo_node if w is not None: try: if w.desc is not state.desc: return try: w.sel_menu.sel_index = node.get_index() except Exception: # option doesnt exist, so make the displayed one blank w.sel_menu.sel_index = -1 w.needs_flushing = False w.sel_menu.update_label() w.set_edited() except Exception: print(format_exc()) def edit_create(self, **kwargs): # add own stuff kwargs.update(sel_index=self.sel_menu.sel_index, max_index=self.sel_menu.max_index) field_widget.FieldWidget.edit_create(self, **kwargs) def get_options(self, opt_index=None): ''' Returns a list of the option strings sorted by option index. ''' if self.option_cache is None or opt_index is not None: return self.generate_options(opt_index) if opt_index is None: return self.option_cache elif opt_index == e_c.ACTIVE_ENUM_NAME: opt_index = self.sel_menu.sel_index return self.option_cache.get(opt_index, None) def generate_options(self, opt_index=None): desc = self.desc options = {} option_count = desc.get('ENTRIES', 0) options_to_generate = range(option_count) if opt_index is not None: options_to_generate = ((opt_index, ) if opt_index in options_to_generate else ()) # sort the options by value(values are integers) use_gui_names = self.use_gui_names for i in options_to_generate: opt = desc[i] if use_gui_names and 'GUI_NAME' in opt: options[i] = opt['GUI_NAME'] else: options[i] = opt.get('NAME', '<UNNAMED %s>' % i)\ .replace('_', ' ') if opt_index is None: self.options_sane = True self.option_cache = options if self.sel_menu is not None: self.sel_menu.options_menu_sane = False self.sel_menu.max_index = option_count - 1 return options return options.get(opt_index, None) def reload(self): try: if self.disabled == self.sel_menu.disabled: pass elif self.disabled: self.sel_menu.disable() else: self.sel_menu.enable() for w in (self, self.content, self.sel_menu, self.title_label): w.tooltip_string = self.desc.get('TOOLTIP') options = self.get_options() option_count = len(options) if not option_count: self.sel_menu.sel_index = -1 self.sel_menu.max_index = -1 return try: curr_index = self.node.get_index() except Exception: curr_index = -1 self.sel_menu.sel_index = curr_index self.sel_menu.max_index = option_count - 1 self.sel_menu.update_label() except Exception: print(format_exc()) populate = reload def pose_fields(self): pass def select_option(self, opt_index=None): if None in (self.parent, self.node): return node = self.node curr_index = self.sel_menu.sel_index if (opt_index is None or opt_index < 0 or opt_index > self.sel_menu.max_index): print("Invalid option index '%s'" % opt_index) return self.sel_menu.sel_index = opt_index undo_node = self.node.data self.node.set_to(opt_index) # make an edit state if undo_node != self.node.data: self.set_edited() self.edit_create(undo_node=undo_node, redo_node=self.node.data) self.sel_menu.update_label()
def __init__(self, app_root, *args, **kwargs): if window_base_class == tk.Toplevel: kwargs.update(bd=0, highlightthickness=0, bg=self.default_bg_color) self.app_root = app_root else: self.app_root = self window_base_class.__init__(self, app_root, *args, **kwargs) BinillaWidget.__init__(self, *args, **kwargs) self.title("Sound compiler") self.resizable(1, 1) self.update() try: self.iconbitmap(e_c.MOZZ_ICON_PATH) except Exception: print("Could not load window icon.") self.wav_dir = tk.StringVar(self) self.sound_path = tk.StringVar(self) self.generate_mouth_data = tk.IntVar(self, 0) self.split_into_smaller_chunks = tk.IntVar(self, 1) self.compression = tk.IntVar(self, constants.COMPRESSION_PCM_16_LE) self.sample_rate = tk.IntVar(self, constants.SAMPLE_RATE_22K) self.encoding = tk.IntVar(self, constants.ENCODING_MONO) self.update_mode = tk.IntVar(self, constants.SOUND_COMPILE_MODE_PRESERVE) self.adpcm_lookahead = tk.IntVar(self, 3) self.adpcm_noise_shaping = tk.IntVar(self, adpcm.NOISE_SHAPING_OFF) self.chunk_size_string = tk.StringVar(self, str(self.chunk_size)) self.chunk_size_string.trace( "w", lambda *a, s=self: s.set_chunk_size()) # make the frames self.main_frame = tk.Frame(self) self.wav_info_frame = tk.LabelFrame( self, text="Import info") self.dirs_frame = tk.LabelFrame( self.main_frame, text="Directories") self.buttons_frame = tk.Frame(self.main_frame) self.settings_frame = tk.Frame( self.main_frame) self.wav_dir_frame = tk.LabelFrame( self.dirs_frame, text="Wav files folder") self.sound_path_frame = tk.LabelFrame( self.dirs_frame, text="Sound output path") self.update_mode_frame = tk.LabelFrame( self.main_frame, text="What to do with existing sound tag") self.processing_frame = tk.LabelFrame( self.settings_frame, text="Format settings") self.adpcm_frame = tk.LabelFrame( self.settings_frame, text="ADPCM settings") self.misc_frame = tk.LabelFrame( self.settings_frame, text="Misc settings") self.compile_mode_replace_rbtn = tk.Radiobutton( self.update_mode_frame, anchor="w", variable=self.update_mode, value=constants.SOUND_COMPILE_MODE_NEW, text="Erase everything(create from scratch)") self.compile_mode_preserve_rbtn = tk.Radiobutton( self.update_mode_frame, anchor="w", variable=self.update_mode, value=constants.SOUND_COMPILE_MODE_PRESERVE, text="Preserve tag values(skip fractions and such)") self.compile_mode_additive_rbtn = tk.Radiobutton( self.update_mode_frame, anchor="w", variable=self.update_mode, value=constants.SOUND_COMPILE_MODE_ADDITIVE, text="Erase nothing(only add/update permutations)") self.compression_menu = ScrollMenu( self.processing_frame, variable=self.compression, menu_width=10, options=tuple( compression_names[const] for const in compression_menu_values ) ) self.sample_rate_menu = ScrollMenu( self.processing_frame, variable=self.sample_rate, menu_width=5, options=tuple( sample_rate_names[const] for const in sample_rate_menu_values ) ) self.encoding_menu = ScrollMenu( self.processing_frame, variable=self.encoding, menu_width=5, options=tuple( encoding_names[const] for const in encoding_menu_values ) ) self.adpcm_lookahead_label = tk.Label( self.adpcm_frame, text="Lookahead prediction �") self.adpcm_lookahead_menu = ScrollMenu( self.adpcm_frame, variable=self.adpcm_lookahead, menu_width=17, options=adpcm_lookahead_names ) self.adpcm_noise_shaping_label = tk.Label( self.adpcm_frame, text="Noise shaping �") self.adpcm_noise_shaping_menu = ScrollMenu( self.adpcm_frame, variable=self.adpcm_noise_shaping, menu_width=17, options=adpcm_noise_shaping_names ) self.adpcm_lookahead_label.tooltip_string = self.adpcm_lookahead_menu.tooltip_string = ( "Number of samples to look ahead when determining minimum\n" "ADPCM error. Higher numbers are typically better." ) self.adpcm_noise_shaping_label.tooltip_string = self.adpcm_noise_shaping_menu.tooltip_string = ( "The type of noise shaping algorithm to apply to the sound.\n" "Noise shaping is a form of dithering, but for audio.\n" "Results will vary, so always test after turning this on." ) self.compression_menu.sel_index = 0 self.sample_rate_menu.sel_index = 0 self.encoding_menu.sel_index = 0 self.generate_mouth_data_cbtn = tk.Checkbutton( self.misc_frame, variable=self.generate_mouth_data, anchor="w", text="Generate mouth data �") self.split_into_smaller_chunks_cbtn = tk.Checkbutton( self.misc_frame, variable=self.split_into_smaller_chunks, anchor="w", text="Split into chunks �") self.chunk_size_label = tk.Label( self.misc_frame, text="Chunk size �") self.chunk_size_spinbox = tk.Spinbox( self.misc_frame, from_=self.min_chunk_size, to=self.max_chunk_size, increment=1024*4, textvariable=self.chunk_size_string, justify="right") self.generate_mouth_data_cbtn.tooltip_string = ( "Whether or not to generate mouth data for this sound.\n" "Mouth data animates a characters mouth to the sound." ) self.split_into_smaller_chunks_cbtn.tooltip_string = ( "Whether or not to split long sounds into pieces.\n" "Long sounds may not play properly ingame, and this\n" "setting is recommended if the sound is over a few seconds." ) self.chunk_size_label.tooltip_string = self.chunk_size_spinbox.tooltip_string = ( 'The number of samples per chunk to split long sounds into.\n' 'NOTE 1:\tThis is for mono. A value of 2 equals 1 stereo sample.\n' 'NOTE 2:\tThis will be rounded down to a multiple of 64 for ADPCM.' ) self._pr_info_tree = tk.ttk.Treeview( self.wav_info_frame, selectmode='browse', padding=(0, 0), height=4) self.wav_info_vsb = tk.Scrollbar( self.wav_info_frame, orient='vertical', command=self._pr_info_tree.yview) self.wav_info_hsb = tk.Scrollbar( self.wav_info_frame, orient='horizontal', command=self._pr_info_tree.xview) self._pr_info_tree.config(yscrollcommand=self.wav_info_vsb.set, xscrollcommand=self.wav_info_hsb.set) self.wav_dir_entry = tk.Entry( self.wav_dir_frame, textvariable=self.wav_dir, state=tk.DISABLED) self.wav_dir_browse_button = tk.Button( self.wav_dir_frame, text="Browse", command=self.wav_dir_browse) self.sound_path_entry = tk.Entry( self.sound_path_frame, textvariable=self.sound_path, state=tk.DISABLED) self.sound_path_browse_button = tk.Button( self.sound_path_frame, text="Browse", command=self.sound_path_browse) self.load_button = tk.Button( self.buttons_frame, text="Load wav files", command=self.load_wav_files) self.compile_button = tk.Button( self.buttons_frame, text="Compile sound", command=self.compile_sound) self.populate_wav_info_tree() # pack everything self.main_frame.pack(fill="both", side='left', pady=3, padx=3) self.wav_info_frame.pack(fill="both", side='left', pady=3, padx=3, expand=True) self.dirs_frame.pack(fill="x") self.buttons_frame.pack(fill="x", pady=3, padx=3) self.update_mode_frame.pack(fill='both') self.settings_frame.pack(fill="both") self.wav_dir_frame.pack(fill='x') self.sound_path_frame.pack(fill='x') self.wav_dir_entry.pack(side='left', fill='x', expand=True) self.wav_dir_browse_button.pack(side='left') self.sound_path_entry.pack(side='left', fill='x', expand=True) self.sound_path_browse_button.pack(side='left') self.wav_info_hsb.pack(side="bottom", fill='x') self.wav_info_vsb.pack(side="right", fill='y') self._pr_info_tree.pack(side='left', fill='both', expand=True) self.load_button.pack(side='left', fill='both', padx=3, expand=True) self.compile_button.pack(side='right', fill='both', padx=3, expand=True) for w in (self.processing_frame, self.adpcm_frame, self.misc_frame): w.pack(expand=True, fill='both') for w in (self.compile_mode_replace_rbtn, self.compile_mode_preserve_rbtn, self.compile_mode_additive_rbtn,): w.pack(expand=True, fill='both') for w in (self.compression_menu, self.sample_rate_menu, self.encoding_menu): w.pack(expand=True, side='left', fill='both') i = 0 for w, lbl in ( (self.adpcm_lookahead_menu, self.adpcm_lookahead_label), (self.adpcm_noise_shaping_menu, self.adpcm_noise_shaping_label) ): lbl.grid(row=i, column=0, sticky="w", padx=(17, 0)) w.grid(row=i, column=2, sticky="news", padx=(10, 0)) i += 1 self.generate_mouth_data_cbtn.grid( row=0, column=0, sticky="news", padx=(17, 0)) self.split_into_smaller_chunks_cbtn.grid( row=0, column=1, sticky="news", padx=(17, 0)) self.chunk_size_label.grid( row=1, column=0, sticky="w", padx=(17, 0)) self.chunk_size_spinbox.grid( row=1, column=1, sticky="news", padx=(10, 10)) self.apply_style() if self.app_root is not self: self.transient(self.app_root)
class ObjectConverter(ConverterBase, window_base_class): object_types = ( "vehicle", "biped", "weapon", "equipment", "garbage", "device_machine", "device_control", "device_light_fixture", "projectile", "sound_scenery", "scenery", ) def __init__(self, app_root, *args, **kwargs): if isinstance(self, tk.Toplevel): kwargs.update(bd=0, highlightthickness=0, bg=self.default_bg_color) window_base_class.__init__(self, app_root, *args, **kwargs) ConverterBase.__init__(self, app_root, *args, **kwargs) kwargs["title"] = "Object to object converter" self.setup_window(*args, **kwargs) @property def src_ext(self): try: return self.object_types[self.src_menu.sel_index] except Exception: return "" @property def dst_ext(self): try: return self.object_types[self.dst_menu.sel_index] except Exception: return "" @property def src_exts(self): return self.object_types def setup_window(self, *args, **kwargs): ConverterBase.setup_window(self, *args, **kwargs) self.settings_frame = tk.LabelFrame(self, text="Conversion settings") self.from_label = tk.Label(self.settings_frame, text="from") self.src_menu = ScrollMenu( self.settings_frame, options=self.object_types, menu_width=20, sel_index=0, ) self.to_label = tk.Label(self.settings_frame, text="to") self.dst_menu = ScrollMenu( self.settings_frame, options=self.object_types, menu_width=20, sel_index=10, ) self.pack_widgets() self.apply_style() def pack_widgets(self): ConverterBase.pack_widgets(self) # pack everything self.settings_frame.pack(fill='both', anchor='nw') self.from_label.pack(padx=(10, 0), pady=10, anchor='w', side='left') self.src_menu.pack(padx=(10, 0), pady=10, fill='x', anchor='w', side='left') self.to_label.pack(padx=(10, 0), pady=10, anchor='w', side='left') self.dst_menu.pack(padx=(10, 0), pady=10, fill='x', anchor='w', side='left') def destroy(self): ConverterBase.destroy(self) window_base_class.destroy(self) def tag_path_browse(self): curr_tag_path = self.tag_path.get() ConverterBase.tag_path_browse(self) if curr_tag_path == self.tag_path.get(): return try: ext = Path(curr_tag_path).suffix[1:].lower() self.src_menu.sel_index = self.object_types.index(ext) except Exception: pass def convert(self, tag_path): return obje_to_obje(tag_path, self.src_ext, self.dst_ext)