def show(self, e=None): # The first time we're shown, decode the text. # That way we don't need to do it on startup. if self.text is not None: parsed_text = tkMarkdown.convert(self.text) self.textbox.set_text(parsed_text) self.text = None self.deiconify() self.update_idletasks() utils.center_win(self, TK_ROOT)
def set_corridors(config: Dict[Tuple[str, int], CorrDesc]): """Set the corridor data based on the passed in config.""" CORRIDOR_DATA.clear() CORRIDOR_DATA.update(config) default_icon = img.icon('clean/portal_door') corridor_conf = COMPILE_CFG['CorridorNames'] for group, length in CORRIDOR_COUNTS.items(): selector = CORRIDOR[group] for item in selector.item_list: if item.name == '<NONE>': continue # No customisation for this. ind = int(item.name) data = config[group, ind] corridor_conf['{}_{}_name'.format(group, ind)] = data.name corridor_conf['{}_{}_desc'.format(group, ind)] = data.desc corridor_conf['{}_{}_icon'.format(group, ind)] = data.icon # Note: default corridor description desc = data.name or _('Corridor') item.longName = item.shortName = item.context_lbl = item.name + ': ' + desc if data.icon: item.large_icon = img.png( 'corr/' + data.icon, resize_to=selectorWin.ICON_SIZE_LRG, error=default_icon, ) item.icon = img.png( 'corr/' + data.icon, resize_to=selectorWin.ICON_SIZE, error=default_icon, ) else: item.icon = item.large_icon = default_icon if data.desc: item.desc = tkMarkdown.convert(data.desc) else: item.desc = CORRIDOR_DESC selector.set_disp() COMPILE_CFG.save_check()
def __init__( self, name: str, short_name: str, long_name: str = None, icon=None, large_icon=None, authors: list = None, desc: Union[tkMarkdown.MarkdownData, str] = '', group: str = None, sort_key: str = None, attributes: dict = None, snd_sample: str = None, ): self.name = name self.shortName = short_name self.group = group or '' self.longName = long_name or short_name self.sort_key = sort_key if len(self.longName) > 20: self.context_lbl = self.shortName else: self.context_lbl = self.longName if icon is not None: self.icon = get_icon(icon, ICON_SIZE, err_icon) else: self.icon = img.color_square(img.PETI_ITEM_BG, ICON_SIZE) self.large_icon = get_icon(large_icon, ICON_SIZE_LRG, err_icon_lrg) if isinstance(desc, str): self.desc = tkMarkdown.convert(desc) else: self.desc = desc self.snd_sample = snd_sample self.authors = authors or [] self.attrs = attributes or {} self.button = None # type: ttk.Button self.win = None # type: Toplevel self.win_x = None # type: int self.win_y = None # type: int
def __init__(self, title: str, text: str): super().__init__(TK_ROOT) self.withdraw() self.title(title) self.transient(master=TK_ROOT) self.resizable(width=True, height=True) tk_tools.set_window_icon(self) # Hide when the exit button is pressed, or Escape # on the keyboard. self.protocol("WM_DELETE_WINDOW", self.withdraw) self.bind("<Escape>", self.withdraw) frame = tk.Frame(self, background='white') frame.grid(row=0, column=0, sticky='nsew') self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.textbox = tkRichText(frame, width=80, height=24) self.textbox.configure(background='white', relief='flat') self.textbox.grid(row=0, column=0, sticky='nsew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) scrollbox = HidingScroll( frame, orient='vertical', command=self.textbox.yview, ) scrollbox.grid(row=0, column=1, sticky='ns') self.textbox['yscrollcommand'] = scrollbox.set parsed_text = tkMarkdown.convert(text) self.textbox.set_text(parsed_text) ttk.Button( frame, text=_('Close'), command=self.withdraw, ).grid( row=1, column=0, )
def __init__( self, name, short_name, long_name=None, icon=None, large_icon=None, authors=None, desc='', group='', sort_key=None, attributes=None, snd_sample=None, ): self.name = name self.shortName = short_name self.group = group or '' self.longName = long_name or short_name self.sort_key = sort_key if len(self.longName) > 20: self._context_lbl = self.shortName else: self._context_lbl = self.longName if icon is not None: self.icon = get_icon(icon, ICON_SIZE, err_icon) else: self.icon = img.color_square(img.PETI_ITEM_BG, ICON_SIZE) self.large_icon = get_icon(large_icon, ICON_SIZE_LRG, err_icon_lrg) if isinstance(desc, str): self.desc = tkMarkdown.convert(desc) else: self.desc = desc self.snd_sample = snd_sample self.authors = authors or [] self.attrs = attributes or {} self.button = None self._selector = None self._context_ind = None
from BEE2_config import ConfigFile, GEN_OPTS from packageLoader import CORRIDOR_COUNTS, CorrDesc from tooltip import add_tooltip, set_tooltip import selectorWin import SubPane import img # The size of PeTI screenshots PETI_WIDTH = 555 PETI_HEIGHT = 312 CORRIDOR = {} # type: Dict[str, selectorWin.selWin] CORRIDOR_DATA = {} # type: Dict[Tuple[str, int], CorrDesc] CORRIDOR_DESC = tkMarkdown.convert('') COMPILE_DEFAULTS = { 'Screenshot': { 'Type': 'AUTO', 'Loc': '', }, 'General': { 'spawn_elev': 'True', 'player_model': 'PETI', 'force_final_light': '0', 'use_voice_priority': '1', 'packfile_dump_dir': '', 'packfile_dump_enable': '0', }, 'Corridor': {
def __init__( self, tk, lst, *, # Make all keyword-only for readability has_none=True, has_def=True, has_snd_sample=False, modal=False, # i18n: 'None' item description none_desc=_('Do not add anything.'), none_attrs: dict = EmptyMapping, title='BEE2', desc='', readonly_desc='', callback: Callable[..., None] = None, callback_params=(), attributes=()): """Create a window object. Read from .selected_id to get the currently-chosen Item name, or None if the <none> Item is selected. Args: - tk: Must be a Toplevel window, either the tk() root or another window if needed. - lst: A list of Item objects, defining the visible items. - If has_none is True, a <none> item will be added to the beginning of the list. - If has_def is True, the 'Reset to Default' button will appear, which resets to the suggested item. - If has_snd_sample is True, a '>' button will appear next to names to play the associated audio sample for the item. - none_desc holds an optional description for the <none> Item, which can be used to describe what it results in. - title is the title of the selector window. - callback is a function to be called whenever the selected item changes. - callback_params is a list of additional values which will be passed to the callback function. The first arguement to the callback is always the selected item ID. - full_context controls if the short or long names are used for the context menu. - attributes is a list of AttrDef tuples. Each tuple should contain an ID, display text, and default value. If the values are True or False a check/cross will be displayed, otherwise they're a string. - desc is descriptive text to display on the window, and in the widget tooltip. - readonly_desc will be displayed on the widget tooltip when readonly. - modal: If True, the window will block others while open. """ self.noneItem = Item( name='NONE', short_name='', icon='BEE2/none_96.png', desc=tkMarkdown.convert(none_desc), attributes=dict(none_attrs), ) # The textbox on the parent window. self.display = None # type: tk_tools.ReadOnlyEntry # Variable associated with self.display. self.disp_label = StringVar() # The '...' button to open our window. self.disp_btn = None # type: ttk.Button # ID of the currently chosen item self.chosen_id = None # Callback function, and positional arugments to pass if callback is not None: self.callback = callback self.callback_params = list(callback_params) else: self.callback = None self.callback_params = () # Item object for the currently suggested item. self.suggested = None # Should we have the 'reset to default' button? self.has_def = has_def self.description = desc self.readonly_description = readonly_desc if has_none: self.item_list = [self.noneItem] + lst else: self.item_list = lst try: self.selected = self.item_list[0] # type: Item except IndexError: LOGGER.error('No items for window "{}"!', title) # We crash without items, forcefully add the None item in so at # least this works. self.item_list = [self.noneItem] self.selected = self.noneItem self.orig_selected = self.selected self.parent = tk self._readonly = False self.modal = modal self.win = Toplevel(tk) self.win.withdraw() self.win.title("BEE2 - " + title) self.win.transient(master=tk) # Allow resizing in X and Y. self.win.resizable(True, True) tk_tools.set_window_icon(self.win) # Run our quit command when the exit button is pressed, or Escape # on the keyboard. self.win.protocol("WM_DELETE_WINDOW", self.exit) self.win.bind("<Escape>", self.exit) # Allow navigating with arrow keys. self.win.bind("<KeyPress>", self.key_navigate) # A map from group name -> header widget self.group_widgets = {} # A map from folded name -> display name self.group_names = {} self.grouped_items = defaultdict(list) # A list of folded group names in the display order. self.group_order = [] # The maximum number of items that fits per row (set in flow_items) self.item_width = 1 if desc: self.desc_label = ttk.Label( self.win, text=desc, justify=LEFT, anchor=W, width=5, # Keep a small width, so this doesn't affect the # initial window size. ) self.desc_label.grid(row=0, column=0, sticky='EW') # PanedWindow allows resizing the two areas independently. self.pane_win = PanedWindow( self.win, orient=HORIZONTAL, sashpad=2, # Padding above/below panes sashwidth=3, # Width of border sashrelief=RAISED, # Raise the border between panes ) self.pane_win.grid(row=1, column=0, sticky="NSEW") self.win.columnconfigure(0, weight=1) self.win.rowconfigure(1, weight=1) self.wid = {} shim = ttk.Frame(self.pane_win, relief="sunken") shim.rowconfigure(0, weight=1) shim.columnconfigure(0, weight=1) # We need to use a canvas to allow scrolling. self.wid_canvas = Canvas(shim, highlightthickness=0) self.wid_canvas.grid(row=0, column=0, sticky="NSEW") # Add another frame inside to place labels on. self.pal_frame = ttk.Frame(self.wid_canvas) self.wid_canvas.create_window(1, 1, window=self.pal_frame, anchor="nw") self.wid_scroll = tk_tools.HidingScroll( shim, orient=VERTICAL, command=self.wid_canvas.yview, ) self.wid_scroll.grid(row=0, column=1, sticky="NS") self.wid_canvas['yscrollcommand'] = self.wid_scroll.set utils.add_mousewheel(self.wid_canvas, self.win) if utils.MAC: # Labelframe doesn't look good here on OSX self.sugg_lbl = ttk.Label( self.pal_frame, # Draw lines with box drawing characters text="\u250E\u2500" + _("Suggested") + "\u2500\u2512", ) else: self.sugg_lbl = ttk.LabelFrame( self.pal_frame, text=_("Suggested"), labelanchor=N, height=50, ) # Holds all the widgets which provide info for the current item. self.prop_frm = ttk.Frame(self.pane_win, borderwidth=4, relief='raised') self.prop_frm.columnconfigure(1, weight=1) # Border around the selected item icon. width, height = img.tuple_size(ICON_SIZE_LRG) self.prop_icon_frm = ttk.Frame( self.prop_frm, borderwidth=4, relief='raised', width=width, height=height, ) self.prop_icon_frm.grid(row=0, column=0, columnspan=4) self.prop_icon = ttk.Label( self.prop_icon_frm, image=img.color_square(img.PETI_ITEM_BG, ICON_SIZE_LRG), ) self.prop_icon.grid(row=0, column=0) name_frame = ttk.Frame(self.prop_frm) self.prop_name = ttk.Label( name_frame, text="Item", justify=CENTER, width=-10, font=("Helvetica", 12, "bold"), ) name_frame.grid(row=1, column=0, columnspan=4) name_frame.columnconfigure(0, weight=1) self.prop_name.grid(row=0, column=0) # For music items, add a '>' button to play sound samples if has_snd_sample and sound.initiallised: self.samp_button = samp_button = ttk.Button( name_frame, text=BTN_PLAY, width=1, ) samp_button.grid(row=0, column=1) add_tooltip( samp_button, _("Play a sample of this item."), ) def set_samp_play(): samp_button['text'] = BTN_PLAY def set_samp_stop(): samp_button['text'] = BTN_STOP self.sampler = sound.SamplePlayer( stop_callback=set_samp_play, start_callback=set_samp_stop, ) samp_button['command'] = self.sampler.play_sample utils.bind_leftclick(self.prop_icon, self.sampler.play_sample) samp_button.state(('disabled', )) else: self.sampler = None self.prop_author = ttk.Label(self.prop_frm, text="Author") self.prop_author.grid(row=2, column=0, columnspan=4) self.prop_desc_frm = ttk.Frame(self.prop_frm, relief="sunken") self.prop_desc_frm.grid(row=4, column=0, columnspan=4, sticky="NSEW") self.prop_desc_frm.rowconfigure(0, weight=1) self.prop_desc_frm.columnconfigure(0, weight=1) self.prop_frm.rowconfigure(4, weight=1) self.prop_desc = tkRichText( self.prop_desc_frm, width=40, height=4, font="TkSmallCaptionFont", ) self.prop_desc.grid( row=0, column=0, padx=(2, 0), pady=2, sticky='NSEW', ) self.prop_scroll = tk_tools.HidingScroll( self.prop_desc_frm, orient=VERTICAL, command=self.prop_desc.yview, ) self.prop_scroll.grid( row=0, column=1, sticky="NS", padx=(0, 2), pady=2, ) self.prop_desc['yscrollcommand'] = self.prop_scroll.set ttk.Button( self.prop_frm, text=_("OK"), command=self.save, ).grid( row=6, column=0, padx=(8, 8), ) if self.has_def: self.prop_reset = ttk.Button( self.prop_frm, text=_("Reset to Default"), command=self.sel_suggested, ) self.prop_reset.grid( row=6, column=1, sticky='EW', ) ttk.Button( self.prop_frm, text=_("Cancel"), command=self.exit, ).grid( row=6, column=2, padx=(8, 8), ) self.win.option_add('*tearOff', False) self.context_menu = Menu(self.win) self.norm_font = tk_font.nametofont('TkMenuFont') # Make a font for showing suggested items in the context menu self.sugg_font = self.norm_font.copy() self.sugg_font['weight'] = tk_font.BOLD # Make a font for previewing the suggested item self.mouseover_font = self.norm_font.copy() self.mouseover_font['slant'] = tk_font.ITALIC self.context_var = IntVar() # The headers for the context menu self.context_menus = {} # Sort alphabetically, preferring a sort key if present. self.item_list.sort(key=lambda it: it.sort_key or it.longName) for ind, item in enumerate(self.item_list): # type: int, Item if item == self.noneItem: item.button = ttk.Button( self.pal_frame, image=item.icon, ) item.context_lbl = '<None>' else: item.button = ttk.Button( self.pal_frame, text=item.shortName, image=item.icon, compound='top', ) group_key = item.group.casefold() self.grouped_items[group_key].append(item) if group_key not in self.group_names: # If the item is groupless, use 'Other' for the header. self.group_names[group_key] = item.group or _('Other') if not item.group: # Ungrouped items appear directly in the menu. menu = self.context_menu else: try: menu = self.context_menus[group_key] except KeyError: self.context_menus[group_key] = menu = Menu( self.context_menu, ) menu.add_radiobutton( label=item.context_lbl, command=functools.partial(self.sel_item_id, item.name), var=self.context_var, value=ind, ) item.win = self.win @utils.bind_leftclick(item.button) def click_item(event=None, *, _item=item): """Handle clicking on the item. If it's already selected, save and close the window. """ # We need to capture the item in a default, since it's # the same variable in different iterations if _item is self.selected: self.save() else: self.sel_item(_item) # Convert to a normal dictionary, after adding all items. self.grouped_items = dict(self.grouped_items) # Figure out the order for the groups - alphabetical. # Note - empty string should sort to the beginning! self.group_order[:] = sorted(self.grouped_items.keys()) for index, (key, menu) in enumerate( sorted(self.context_menus.items(), key=itemgetter(0)), # We start with the ungrouped items, so increase the index # appropriately. start=len(self.grouped_items.get('', ()))): self.context_menu.add_cascade( menu=menu, label=self.group_names[key], ) # Set a custom attribute to keep track of the menu's index. menu.index = index for group_key, text in self.group_names.items(): self.group_widgets[group_key] = GroupHeader( self, text, ) self.group_widgets[group_key].should_show = True self.flow_items(None) self.wid_canvas.bind("<Configure>", self.flow_items) self.pane_win.add(shim) self.pane_win.add(self.prop_frm) # Force a minimum size for the two parts self.pane_win.paneconfigure(shim, minsize=100, stretch='always') self.prop_frm.update_idletasks() # Update reqwidth() self.pane_win.paneconfigure( self.prop_frm, minsize=200, stretch='never', ) if attributes: attr_frame = ttk.Frame(self.prop_frm) attr_frame.grid( row=5, column=0, columnspan=3, sticky=EW, ) self.attr = {} # Add in all the attribute labels for index, attr in enumerate(attributes): desc_label = ttk.Label( attr_frame, text=attr.desc, ) self.attr[attr.id] = val_label = ttk.Label(attr_frame, ) val_label.default = attr.default val_label.type = attr.type if attr.type is AttrTypes.BOOL: # It's a tick/cross label val_label['image'] = (ICON_CHECK if attr.default else ICON_CROSS, ) elif attr.type is AttrTypes.COLOR: # A small colour swatch. val_label.configure(relief=RAISED, ) # Show the color value when hovered. add_tooltip(val_label) # Position in a 2-wide grid desc_label.grid( row=index // 2, column=(index % 2) * 2, sticky=E, ) val_label.grid( row=index // 2, column=(index % 2) * 2 + 1, sticky=W, ) else: self.attr = None