def add_tabs(): """Add the tabs to the notebook.""" notebook = UI['tabs'] # Save the current tab index so we can restore it after. try: current_tab = notebook.index(notebook.select()) except TclError: # .index() will fail if the voice is empty, current_tab = None # in that case abandon remembering the tab. # Add or remove tabs so only the correct mode is visible. for name, tab in sorted(TABS.items()): notebook.add(tab) # For the special tabs, we use a special image to make # sure they are well-distinguished from the other groups if tab.nb_type is TabTypes.MID: notebook.tab( tab, compound='image', image=img.png('icons/mid_quote'), ) if tab.nb_type is TabTypes.RESPONSE: notebook.tab( tab, compound=RIGHT, image=img.png('icons/resp_quote'), #Note: 'response' tab name, should be short. text=_('Resp')) else: notebook.tab(tab, text=tab.nb_text) if current_tab is not None: notebook.select(current_tab)
def add_tabs(): """Add the tabs to the notebook.""" notebook = UI['tabs'] # Save the current tab index so we can restore it after. try: current_tab = notebook.index(notebook.select()) except TclError: # .index() will fail if the voice is empty, current_tab = None # in that case abandon remembering the tab. # Add or remove tabs so only the correct mode is visible. for name, tab in sorted(TABS.items()): notebook.add(tab) # For the special tabs, we use a special image to make # sure they are well-distinguished from the other groups if tab.nb_type is TabTypes.MID: notebook.tab( tab, compound='image', image=img.png('icons/mid_quote'), ) if tab.nb_type is TabTypes.RESPONSE: notebook.tab( tab, compound=RIGHT, image=img.png('icons/resp_quote'), text='Resp' ) else: notebook.tab(tab, text=tab.nb_text) if current_tab is not None: notebook.select(current_tab)
def __init__( self, name: str, icon: str, group: str=None, group_icon: str=None, ) -> None: self.name = name self.dnd_icon = img.png('items/clean/{}.png'.format(icon)) self.dnd_group = group if group_icon: self.dnd_group_icon = img.png('items/clean/{}.png'.format(group_icon))
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 make_help_menu(parent: tk.Menu): """Create the application 'Help' menu.""" # Using this name displays this correctly in OS X help = tk.Menu(parent, name='help') parent.add_cascade(menu=help, label=_('Help')) invis_icon = img.invis_square(16) icons = { icon: img.png('icons/' + icon.value, resize_to=16, error=invis_icon) for icon in ResIcon if icon is not ResIcon.NONE } icons[ResIcon.NONE] = invis_icon credits = Dialog( title=_('BEE2 Credits'), text=CREDITS_TEXT, ) for res in WEB_RESOURCES: if res is SEPERATOR: help.add_separator() else: help.add_command( label=res.name, command=functools.partial(webbrowser.open, res.url), compound='left', image=icons[res.icon], ) help.add_separator() help.add_command( label=_('Credits...'), command=credits.show, )
def style_changed(new_style: Style) -> None: """Update the icons for the selected signage.""" for sign in Signage.all(): if sign.hidden: # Don't bother with making the icon. continue for potential_style in new_style.bases: try: icon = sign.styles[potential_style.id.upper()].icon break except KeyError: pass else: LOGGER.warning( 'No valid <{}> style for "{}" signage!', new_style.id, sign.id, ) try: icon = sign.styles['BEE2_CLEAN'].icon except KeyError: sign.dnd_icon = img.img_error continue if icon: sign.dnd_icon = img.png(icon, resize_to=(64, 64)) else: LOGGER.warning( 'No icon for "{}" signage in <{}> style!', sign.id, new_style.id, ) sign.dnd_icon = img.img_error drag_man.refresh_icons()
def __init__( self, name: str, short_name: str, long_name: str=None, icon=None, authors: list=None, desc=(('line', ''),), 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 None: self.icon = img.png( 'BEE2/blank_96', error=err_icon, resize_to=ICON_SIZE, ) self.ico_file = 'BEE2/blank_96' else: self.icon = img.png( icon, error=err_icon, resize_to=ICON_SIZE, ) self.ico_file = icon 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 refresh(e=None): notebook = UI['tabs'] is_coop = coop_selected # Save the current tab index so we can restore it after. try: current_tab = notebook.index(notebook.select()) except TclError: # .index() will fail if the voice is empty, current_tab = None # in that case abandon remembering the tab. # Add or remove tabs so only the correct mode is visible. for name, tab in sorted(TABS_SP.items()): if is_coop: notebook.forget(tab) else: notebook.add(tab) if tab.nb_is_mid: # For the midpoint tabs, we use a special image to make # sure they are well-distinguished from the other groups. notebook.tab( tab, compound='image', image=img.png('icons/mid_quote'), ) else: notebook.tab(tab, text=tab.nb_text) for name, tab in sorted(TABS_COOP.items()): if is_coop: notebook.add(tab) if tab.nb_is_mid: notebook.tab( tab, compound='image', image=img.png('icons/mid_quote'), ) else: notebook.tab(tab, text=tab.nb_text) else: notebook.forget(tab) if current_tab is not None: notebook.select(current_tab)
def __init__( self, man: Manager, parent: tkinter.Misc, is_source: bool, label: str, ) -> None: """Internal only, use Manager.slot().""" self.man = man self.is_source = is_source self._contents: Optional[ItemT] = None self._pos_type: Optional[str] = None self._lbl = tkinter.Label( parent, image=man._img_blank, ) utils.bind_leftclick(self._lbl, self._evt_start) self._lbl.bind(utils.EVENTS['LEFT_SHIFT'], self._evt_fastdrag) self._lbl.bind('<Enter>', self._evt_hover_enter) self._lbl.bind('<Leave>', self._evt_hover_exit) config_event = self._evt_configure utils.bind_rightclick(self._lbl, config_event) if label: self._text_lbl = tkinter.Label( self._lbl, text=label, font=('Helvetica', -12), relief='ridge', bg=img.PETI_ITEM_BG_HEX, ) else: self._text_lbl = None if man.config_icon: self._info_btn = tkinter.Label( self._lbl, image=img.png('icons/gear'), relief='ridge', ) @utils.bind_leftclick(self._info_btn) def info_button_click(e): """Trigger the callback whenever the gear button was pressed.""" config_event(e) # Cancel the event sequence, so it doesn't travel up to the main # window and hide the window again. return 'break' # Rightclick does the same as the main icon. utils.bind_rightclick(self._info_btn, config_event) else: self._info_btn = None
def get_icon(icon, size, err_icon): if icon is None: # Unset. return None elif icon == '<black>': return img.color_square(Vec(), size) else: return img.png( icon, error=err_icon, resize_to=size, algo=img.Image.LANCZOS, )
def refresh(e=None): notebook = UI['tabs'] is_coop = coop_selected # Save the current tab index so we can restore it after. current_tab = notebook.index(notebook.select()) # Add or remove tabs so only the correct mode is visible. for name, tab in sorted(TABS_SP.items()): if is_coop: notebook.forget(tab) else: notebook.add(tab) if tab.nb_is_mid: # For the midpoint tabs, we use a special image to make # sure they are well-distinguished from the other groups. notebook.tab( tab, compound='image', image=img.png('icons/mid_quote'), ) else: notebook.tab(tab, text=tab.nb_text) for name, tab in sorted(TABS_COOP.items()): if is_coop: notebook.add(tab) if tab.nb_is_mid: notebook.tab( tab, compound='image', image=img.png('icons/mid_quote'), ) else: notebook.tab(tab, text=tab.nb_text) else: notebook.forget(tab) notebook.select(current_tab)
def __init__( self, name: str, short_name: str, long_name: str=None, icon=None, authors: list=None, desc=(('line', ''),), group: str=None, attributes: dict=None, ): self.name = name self.shortName = short_name self.group = group self.longName = long_name or short_name if len(self.longName) > 20: self.context_lbl = self.shortName else: self.context_lbl = self.longName if icon is None: self.icon = img.png( 'BEE2/blank_96', error=err_icon, resize_to=ICON_SIZE, ) self.ico_file = 'BEE2/blank_96' else: self.icon = img.png( icon, error=err_icon, resize_to=ICON_SIZE, ) self.ico_file = icon self.desc = desc self.authors = authors or [] self.attrs = attributes or {} self.button = None self.win = None
def __init__( self, name, short_name, long_name=None, icon=None, authors=None, desc=(('line', ''),), group=None, ): self.name = name self.shortName = short_name self.group = group self.longName = long_name or short_name if len(self.longName) > 20: self.context_lbl = self.shortName else: self.context_lbl = self.longName if icon is None: self.icon = img.png( 'BEE2/blank_96', error=err_icon, resize_to=ICON_SIZE, ) self.ico_file = 'BEE2/blank_96' else: self.icon = img.png( icon, error=err_icon, resize_to=ICON_SIZE, ) self.ico_file = icon self.desc = desc self.authors = [] if authors is None else authors self.button = None self.win = None
def make_pane(tool_frame): """Initialise when part of the BEE2.""" global window window = SubPane.SubPane( TK_ROOT, options=GEN_OPTS, title=_('Compile Options'), name='compiler', resize_x=True, resize_y=False, tool_frame=tool_frame, tool_img=img.png('icons/win_compiler'), tool_col=4, ) window.columnconfigure(0, weight=1) window.rowconfigure(0, weight=1) make_widgets()
def set_text(self, text_data: Union[str, tkMarkdown.MarkdownData]): """Write the rich-text into the textbox. text_data should either be a string, or the data returned from tkMarkdown.convert(). """ # Remove all previous link commands for tag, (command_id, func) in self.link_commands.items(): self.tag_unbind(tag, '<Button-1>', funcid=command_id) self.link_commands.clear() self['state'] = "normal" self.delete(1.0, END) # Basic mode, insert just blocks of text. if isinstance(text_data, str): super().insert("end", text_data) return for block_type, block_data in text_data.blocks: if block_type is tkMarkdown.BlockTags.TEXT: super().insert('end', *block_data) elif block_type is tkMarkdown.BlockTags.IMAGE: super().insert('end', '\n') self.image_create('end', image=img.png(block_data)) super().insert('end', '\n') else: raise ValueError('Unknown block {!r}?'.format(block_type)) for url, link_id in text_data.links.items(): func = self.make_link_callback(url) self.link_commands[link_id] = self.tag_bind( link_id, '<Button-1>', self.make_link_callback(url), ), func self['state'] = "disabled"
from richTextBox import tkRichText from tooltip import add_tooltip import sound import utils import tk_tools from typing import Callable LOGGER = utils.getLogger(__name__) ICON_SIZE = 96 # Size of the selector win icons ITEM_WIDTH = ICON_SIZE + (32 if utils.MAC else 16) ITEM_HEIGHT = ICON_SIZE + 51 # The larger error icon used if an image is not found err_icon = img.png('BEE2/error_96', resize_to=ICON_SIZE) # The two icons used for boolean item attributes ICON_CHECK = img.png('icons/check') ICON_CROSS = img.png('icons/cross') # Arrows used to indicate the state of the group - collapsed or expanded GRP_COLL = '◁' GRP_COLL_HOVER = '◀' GRP_EXP = '▽' GRP_EXP_HOVER = '▼' BTN_PLAY = '▶' BTN_STOP = '■'
def load_item_data(): """Refresh the window to use the selected item's data.""" global version_lookup item_data = selected_item.data for ind, pos in enumerate(SUBITEM_POS[selected_item.num_sub]): if pos == -1: wid['subitem', ind]['image'] = png.png('BEE2/alpha_64') else: wid['subitem', ind]['image'] = selected_item.get_icon(pos) wid['subitem', ind]['relief'] = 'flat' wid['subitem', pos_for_item()]['relief'] = 'raised' wid['author']['text'] = ', '.join(item_data['auth']) wid['name']['text'] = selected_sub_item.name wid['ent_count']['text'] = item_data['ent'] wid['desc'].set_text( get_description( global_last=selected_item.item.glob_desc_last, glob_desc=selected_item.item.glob_desc, style_desc=item_data['desc'] ) ) if itemPropWin.can_edit(selected_item.properties()): wid['changedefaults'].state(['!disabled']) else: wid['changedefaults'].state(['disabled']) if selected_item.is_wip and selected_item.is_dep: wid['wip_dep']['text'] = 'WIP, Deprecated Item!' elif selected_item.is_wip: wid['wip_dep']['text'] = 'WIP Item!' elif selected_item.is_dep: wid['wip_dep']['text'] = 'Deprecated Item!' else: wid['wip_dep']['text'] = '' version_lookup, version_names = selected_item.get_version_names() if len(version_names) <= 1: # There aren't any alternates to choose from, disable the box wid['variant'].state(['disabled']) # We want to display WIP / Dep tags still, so users know. if selected_item.is_wip and selected_item.is_dep: wid['variant']['values'] = ['[WIP] [DEP] No Alts!'] elif selected_item.is_wip: wid['variant']['values'] = ['[WIP] No Alt Versions!'] elif selected_item.is_dep: wid['variant']['values'] = ['[DEP] No Alt Versions!'] else: wid['variant']['values'] = ['No Alternate Versions!'] wid['variant'].current(0) else: wid['variant'].state(['!disabled']) wid['variant']['values'] = version_names wid['variant'].current(version_lookup.index(selected_item.selected_ver)) if selected_item.url is None: wid['moreinfo'].state(['disabled']) else: wid['moreinfo'].state(['!disabled']) wid['moreinfo'].tooltip_text = selected_item.url editor_data = item_data['editor'] has_inputs = False has_polarity = False has_outputs = False for inp_list in editor_data.find_all("Exporting", "Inputs"): for inp in inp_list: if inp.name == "connection_standard": has_inputs = True elif inp.name == "connection_tbeam_polarity": has_polarity = True for out_list in editor_data.find_all("Exporting", "Outputs"): for out in out_list: if out.name == "connection_standard": has_outputs = True break has_timer = any(editor_data.find_all("Properties", "TimerDelay")) editor_bit = next(editor_data.find_all("Editor")) rot_type = editor_bit["MovementHandle", "HANDLE_NONE"].casefold() facing_type = editor_bit["InvalidSurface", ""].casefold() surf_wall = "wall" in facing_type surf_floor = "floor" in facing_type surf_ceil = "ceiling" in facing_type is_embed = any(editor_data.find_all("Exporting", "EmbeddedVoxels")) if has_inputs: if has_polarity: set_sprite(SPR.INPUT, 'in_polarity') else: set_sprite(SPR.INPUT, 'in_norm') else: set_sprite(SPR.INPUT, 'in_none') if has_outputs: if has_timer: set_sprite(SPR.OUTPUT, 'out_tim') else: set_sprite(SPR.OUTPUT, 'out_norm') else: set_sprite(SPR.OUTPUT, 'out_none') set_sprite( SPR.ROTATION, ROT_TYPES.get( rot_type.casefold(), 'rot_none', ) ) if is_embed: set_sprite(SPR.COLLISION, 'space_embed') else: set_sprite(SPR.COLLISION, 'space_none') face_spr = "surf" if not surf_wall: face_spr += "_wall" if not surf_floor: face_spr += "_floor" if not surf_ceil: face_spr += "_ceil" if face_spr == "surf": # This doesn't seem right - this item won't be placeable at all... LOGGER.warning( "Item <{}> disallows all orientations. Is this right?", selected_item.id, ) face_spr += "_none" set_sprite(SPR.FACING, face_spr) # Now some special overrides for paint and cube dropper items.. if selected_item.id == "ITEM_CUBE": set_sprite(SPR.FACING, 'surf_ceil') set_sprite(SPR.INPUT, 'in_norm') set_sprite(SPR.COLLISION, 'space_embed') set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, 'rot_36') wid['sprite', SPR.ROTATION].tooltip_text += '\n(Reflection Cube only)' if editor_data['ItemClass', ''].casefold() == "itempaintsplat": # Reflection or normal gel.. set_sprite(SPR.FACING, 'surf_wall_ceil') set_sprite(SPR.INPUT, 'in_norm') set_sprite(SPR.COLLISION, 'space_none') set_sprite(SPR.OUTPUT, 'out_none') set_sprite(SPR.ROTATION, 'rot_paint')
import srctools.logger from srctools.filesys import FileSystemChain import tkMarkdown import sound import utils import tk_tools LOGGER = srctools.logger.get_logger(__name__) ICON_SIZE = 96 # Size of the selector win icons ICON_SIZE_LRG = (256, 192) # Size of the larger icon shown in description. ITEM_WIDTH = ICON_SIZE + (32 if utils.MAC else 16) ITEM_HEIGHT = ICON_SIZE + 51 # The larger error icons used if an image is not found err_icon = img.png('BEE2/error_96', resize_to=ICON_SIZE) err_icon_lrg = img.png('BEE2/error_96', resize_to=ICON_SIZE_LRG) # The two icons used for boolean item attributes ICON_CHECK = img.png('icons/check') ICON_CROSS = img.png('icons/cross') # Arrows used to indicate the state of the group - collapsed or expanded GRP_COLL = '◁' GRP_COLL_HOVER = '◀' GRP_EXP = '▽' GRP_EXP_HOVER = '▼' BTN_PLAY = '▶' BTN_STOP = '■'
def init_widgets(): """Initiallise all the window components.""" global prop_window, moreinfo_win prop_window = Toplevel(TK_ROOT) prop_window.overrideredirect(1) prop_window.resizable(False, False) prop_window.transient(master=TK_ROOT) prop_window.attributes('-topmost', 1) prop_window.relX = 0 prop_window.relY = 0 prop_window.withdraw() # starts hidden f = ttk.Frame(prop_window, relief="raised", borderwidth="4") f.grid(row=0, column=0) ttk.Label( f, text="Properties:", anchor="center", ).grid( row=0, column=0, columnspan=3, sticky="EW", ) wid['name'] = ttk.Label(f, text="", anchor="center") wid['name'].grid(row=1, column=0, columnspan=3, sticky="EW") wid['ent_count'] = ttk.Label( f, text="2", anchor="e", compound="left", image=png.spr('gear_ent'), ) wid['ent_count'].grid(row=0, column=2, rowspan=2, sticky=E) wid['author'] = ttk.Label(f, text="", anchor="center", relief="sunken") wid['author'].grid(row=2, column=0, columnspan=3, sticky="EW") sub_frame = ttk.Frame(f, borderwidth=4, relief="sunken") sub_frame.grid(column=0, columnspan=3, row=3) for i, _ in enumerate(wid['subitem']): wid['subitem'][i] = ttk.Label( sub_frame, image=png.png('BEE2/alpha_64'), ) wid['subitem'][i].grid(row=0, column=i) wid['subitem'][i].bind('<Button-1>', functools.partial(sub_sel, i)) wid['subitem'][i].bind('<Button-3>', functools.partial(sub_open, i)) wid['wip_dep'] = ttk.Label(f, text='', anchor="nw") wid['wip_dep'].grid(row=4, column=0, sticky="NW") ttk.Label(f, text="Description:", anchor="sw").grid( row=4, column=0, sticky="SW", ) spr_frame = ttk.Frame(f, borderwidth=4, relief="sunken") spr_frame.grid(column=1, columnspan=2, row=4, sticky=W) # sprites: inputs, outputs, rotation handle, occupied/embed state, # desiredFacing for i in range(5): spr = png.spr('ap_grey') wid['sprite'][i] = ttk.Label(spr_frame, image=spr, relief="raised") wid['sprite'][i].grid(row=0, column=i) desc_frame = ttk.Frame(f, borderwidth=4, relief="sunken") desc_frame.grid(row=5, column=0, columnspan=3, sticky="EW") wid['desc'] = tkRichText(desc_frame, width=40, height=8, font=None) wid['desc'].grid(row=0, column=0, sticky="EW") desc_scroll = ttk.Scrollbar( desc_frame, orient=VERTICAL, command=wid['desc'].yview, ) wid['desc']['yscrollcommand'] = desc_scroll.set desc_scroll.grid(row=0, column=1, sticky="NS") def show_more_info(): url = selected_item.url if url is not None: try: webbrowser.open(url, new=OPEN_IN_TAB, autoraise=True) except webbrowser.Error: if messagebox.askyesno( icon="error", title="BEE2 - Error", message='Failed to open a web browser. Do you wish for ' 'the URL to be copied to the clipboard ' 'instead?', detail='"{!s}"'.format(url), parent=prop_window ): print("Saving " + url + "to clipboard!") TK_ROOT.clipboard_clear() TK_ROOT.clipboard_append(url) # Either the webbrowser or the messagebox could cause the # properties to move behind the main window, so hide it # so it doesn't appear there. hide_context(None) wid['moreinfo'] = ttk.Button(f, text="More Info>>", command=show_more_info) wid['moreinfo'].grid(row=6, column=2, sticky=E) wid['moreinfo'].bind('<Enter>', more_info_show_url) wid['moreinfo'].bind('<Leave>', more_info_hide_url) moreinfo_win = Toplevel(TK_ROOT) moreinfo_win.withdraw() moreinfo_win.transient(master=TK_ROOT) moreinfo_win.overrideredirect(1) moreinfo_win.resizable(False, False) wid['moreinfo_context'] = ttk.Label( moreinfo_win, text='', relief="groove", font="TkSmallCaptionFont", padding=(5, 2), ) wid['moreinfo_context'].grid(row=0, column=0) menu_info = Menu(wid['moreinfo']) menu_info.add_command(label='', state='disabled') def show_item_props(): snd.fx('expand') itemPropWin.show_window( selected_item.get_properties(), wid['changedefaults'], selected_sub_item.name, ) wid['changedefaults'] = ttk.Button( f, text="Change Defaults...", command=show_item_props, ) wid['changedefaults'].grid(row=6, column=1) wid['variant'] = ttk.Combobox(f, values=['VERSION'], exportselection=0) wid['variant'].state(['readonly']) # Prevent directly typing in values wid['variant'].bind('<<ComboboxSelected>>', set_item_version) wid['variant'].current(0) wid['variant'].grid(row=6, column=0, sticky=W) itemPropWin.init(hide_item_props)
def make_comp_widgets(frame: ttk.Frame): """Create widgets for the compiler settings pane. These are generally things that are aesthetic, and to do with the file and compilation process. """ frame.columnconfigure(0, weight=1) thumb_frame = ttk.LabelFrame( frame, text=_('Thumbnail'), labelanchor=N, ) thumb_frame.grid(row=0, column=0, sticky=EW) thumb_frame.columnconfigure(0, weight=1) UI['thumb_auto'] = ttk.Radiobutton( thumb_frame, text=_('Auto'), value='AUTO', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_peti'] = ttk.Radiobutton( thumb_frame, text=_('PeTI'), value='PETI', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_custom'] = ttk.Radiobutton( thumb_frame, text=_('Custom:'), value='CUST', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_label'] = ttk.Label( thumb_frame, anchor=CENTER, cursor=utils.CURSORS['link'], ) UI['thumb_label'].bind( utils.EVENTS['LEFT'], find_screenshot, ) UI['thumb_cleanup'] = ttk.Checkbutton( thumb_frame, text=_('Cleanup old screenshots'), variable=cleanup_screenshot, command=make_setter('Screenshot', 'del_old', cleanup_screenshot), ) UI['thumb_auto'].grid(row=0, column=0, sticky='W') UI['thumb_peti'].grid(row=0, column=1, sticky='W') UI['thumb_custom'].grid(row=1, column=0, columnspan=2, sticky='NEW') UI['thumb_cleanup'].grid(row=3, columnspan=2, sticky='W') add_tooltip( UI['thumb_auto'], _("Override the map image to use a screenshot automatically taken " "from the beginning of a chamber. Press F5 to take a new " "screenshot. If the map has not been previewed recently " "(within the last few hours), the default PeTI screenshot " "will be used instead.") ) add_tooltip( UI['thumb_peti'], _("Use the normal editor view for the map preview image.") ) custom_tooltip = _( "Use a custom image for the map preview image. Click the " "screenshot to select.\n" "Images will be converted to JPEGs if needed." ) add_tooltip( UI['thumb_custom'], custom_tooltip, ) add_tooltip( UI['thumb_label'], custom_tooltip, ) add_tooltip( UI['thumb_cleanup'], _('Automatically delete unused Automatic screenshots. ' 'Disable if you want to keep things in "portal2/screenshots". ') ) if chosen_thumb.get() == 'CUST': # Show this if the user has set it before UI['thumb_label'].grid(row=2, column=0, columnspan=2, sticky='EW') set_screenshot() # Load the last saved screenshot vrad_frame = ttk.LabelFrame( frame, text=_('Lighting:'), labelanchor=N, ) vrad_frame.grid(row=1, column=0, sticky=EW) UI['light_fast'] = ttk.Radiobutton( vrad_frame, text=_('Fast'), value=0, variable=vrad_light_type, command=make_setter('General', 'vrad_force_full', vrad_light_type), ) UI['light_fast'].grid(row=0, column=0) UI['light_full'] = ttk.Radiobutton( vrad_frame, text=_('Full'), value=1, variable=vrad_light_type, command=make_setter('General', 'vrad_force_full', vrad_light_type), ) UI['light_full'].grid(row=0, column=1) add_tooltip( UI['light_fast'], _("Compile with lower-quality, fast lighting. This speeds " "up compile times, but does not appear as good. Some " "shadows may appear wrong.\n" "When publishing, this is ignored.") ) add_tooltip( UI['light_full'], _("Compile with high-quality lighting. This looks correct, " "but takes longer to compute. Use if you're arranging lights. " "When publishing, this is always used.") ) packfile_enable = ttk.Checkbutton( frame, text=_('Dump packed files to:'), variable=packfile_dump_enable, command=set_pack_dump_enabled, ) packfile_frame = ttk.LabelFrame( frame, labelwidget=packfile_enable, ) packfile_frame.grid(row=2, column=0, sticky=EW) UI['packfile_filefield'] = packfile_filefield = FileField( packfile_frame, is_dir=True, loc=COMPILE_CFG.get_val('General', 'packfile_dump_dir', ''), callback=set_pack_dump_dir, ) packfile_filefield.grid(row=0, column=0, sticky=EW) packfile_frame.columnconfigure(0, weight=1) ttk.Frame(packfile_frame).grid(row=1) set_pack_dump_enabled() add_tooltip( packfile_enable, _("When compiling, dump all files which were packed into the map. Useful" " if you're intending to edit maps in Hammer.") ) count_frame = ttk.LabelFrame( frame, text=_('Last Compile:'), labelanchor=N, ) count_frame.grid(row=7, column=0, sticky=EW) count_frame.columnconfigure(0, weight=1) count_frame.columnconfigure(2, weight=1) ttk.Label( count_frame, text=_('Entity'), anchor=N, ).grid(row=0, column=0, columnspan=3, sticky=EW) UI['count_entity'] = ttk.Progressbar( count_frame, maximum=100, variable=count_entity, length=120, ) UI['count_entity'].grid( row=1, column=0, columnspan=3, sticky=EW, padx=5, ) ttk.Label( count_frame, text=_('Overlay'), anchor=CENTER, ).grid(row=2, column=0, sticky=EW) UI['count_overlay'] = ttk.Progressbar( count_frame, maximum=100, variable=count_overlay, length=50, ) UI['count_overlay'].grid(row=3, column=0, sticky=EW, padx=5) UI['refresh_counts'] = SubPane.make_tool_button( count_frame, img.png('icons/tool_sub', resize_to=16), refresh_counts, ) UI['refresh_counts'].grid(row=3, column=1) add_tooltip( UI['refresh_counts'], _("Refresh the compile progress bars. Press after a compile has been " "performed to show the new values."), ) ttk.Label( count_frame, text=_('Brush'), anchor=CENTER, ).grid(row=2, column=2, sticky=EW) UI['count_brush'] = ttk.Progressbar( count_frame, maximum=100, variable=count_brush, length=50, ) UI['count_brush'].grid(row=3, column=2, sticky=EW, padx=5) for wid_name in ('count_overlay', 'count_entity', 'count_brush'): # Add in tooltip logic to the widgets. add_tooltip(UI[wid_name]) refresh_counts(reload=False)
def make_pane(tool_frame): """Create the styleVar pane. """ global window window = SubPane( TK_ROOT, options=BEE2_config.GEN_OPTS, title=_('Style/Item Properties'), name='style', resize_y=True, tool_frame=tool_frame, tool_img=img.png('icons/win_stylevar'), tool_col=3, ) UI['nbook'] = nbook = ttk.Notebook(window) nbook.grid(row=0, column=0, sticky=NSEW) window.rowconfigure(0, weight=1) window.columnconfigure(0, weight=1) nbook.enable_traversal() stylevar_frame = ttk.Frame(nbook) stylevar_frame.rowconfigure(0, weight=1) stylevar_frame.columnconfigure(0, weight=1) nbook.add(stylevar_frame, text=_('Styles')) UI['style_can'] = Canvas(stylevar_frame, highlightthickness=0) # need to use a canvas to allow scrolling UI['style_can'].grid(sticky='NSEW') window.rowconfigure(0, weight=1) UI['style_scroll'] = ttk.Scrollbar( stylevar_frame, orient=VERTICAL, command=UI['style_can'].yview, ) UI['style_scroll'].grid(column=1, row=0, rowspan=2, sticky="NS") UI['style_can']['yscrollcommand'] = UI['style_scroll'].set utils.add_mousewheel(UI['style_can'], stylevar_frame) canvas_frame = ttk.Frame(UI['style_can']) frame_all = ttk.Labelframe(canvas_frame, text=_("All:")) frame_all.grid(row=0, sticky='EW') frm_chosen = ttk.Labelframe(canvas_frame, text=_("Selected Style:")) frm_chosen.grid(row=1, sticky='EW') ttk.Separator( canvas_frame, orient=HORIZONTAL, ).grid(row=2, sticky='EW', pady=(10, 5)) frm_other = ttk.Labelframe(canvas_frame, text=_("Other Styles:")) frm_other.grid(row=3, sticky='EW') UI['stylevar_chosen_none'] = ttk.Label( frm_chosen, text=_('No Options!'), font='TkMenuFont', justify='center', ) UI['stylevar_other_none'] = ttk.Label( frm_other, text=_('None!'), font='TkMenuFont', justify='center', ) all_pos = 0 for all_pos, var in enumerate(styleOptions): # Add the special stylevars which apply to all styles tk_vars[var.id] = int_var = IntVar(value=var.default) checkbox_all[var.id] = ttk.Checkbutton( frame_all, variable=int_var, text=var.name, ) checkbox_all[var.id].grid(row=all_pos, column=0, sticky="W", padx=3) # Special case - this needs to refresh the filter when swapping, # so the items disappear or reappear. if var.id == 'UnlockDefault': def cmd(): update_filter() checkbox_all[var.id]['command'] = cmd tooltip.add_tooltip( checkbox_all[var.id], make_desc(var, is_hardcoded=True), ) for var in VAR_LIST: tk_vars[var.id] = IntVar(value=var.enabled) args = { 'variable': tk_vars[var.id], 'text': var.name, } desc = make_desc(var) if var.applies_to_all(): # Available in all styles - put with the hardcoded variables. all_pos += 1 checkbox_all[var.id] = check = ttk.Checkbutton(frame_all, **args) check.grid(row=all_pos, column=0, sticky="W", padx=3) tooltip.add_tooltip(check, desc) else: # Swap between checkboxes depending on style. checkbox_chosen[var.id] = ttk.Checkbutton(frm_chosen, **args) checkbox_other[var.id] = ttk.Checkbutton(frm_other, **args) tooltip.add_tooltip( checkbox_chosen[var.id], desc, ) tooltip.add_tooltip( checkbox_other[var.id], desc, ) UI['style_can'].create_window(0, 0, window=canvas_frame, anchor="nw") UI['style_can'].update_idletasks() UI['style_can'].config( scrollregion=UI['style_can'].bbox(ALL), width=canvas_frame.winfo_reqwidth(), ) if utils.USE_SIZEGRIP: ttk.Sizegrip( window, cursor=utils.CURSORS['stretch_vert'], ).grid(row=1, column=0) UI['style_can'].bind('<Configure>', flow_stylevar) item_config_frame = ttk.Frame(nbook) nbook.add(item_config_frame, text=_('Items')) itemconfig.make_pane(item_config_frame)
def load_item_data(): """Refresh the window to use the selected item's data.""" global version_lookup item_data = selected_item.data for ind, pos in enumerate(SUBITEM_POS[selected_item.num_sub]): if pos == -1: wid['subitem'][ind]['image'] = png.png('BEE2/alpha_64') else: wid['subitem'][ind]['image'] = selected_item.get_icon(pos) wid['subitem'][ind]['relief'] = 'flat' wid['subitem'][pos_for_item()]['relief'] = 'raised' wid['author']['text'] = ', '.join(item_data['auth']) wid['name']['text'] = selected_sub_item.name wid['ent_count']['text'] = item_data['ent'] wid['desc'].set_text( get_description(global_last=selected_item.item.glob_desc_last, glob_desc=selected_item.item.glob_desc, style_desc=item_data['desc'])) if itemPropWin.can_edit(selected_item.properties()): wid['changedefaults'].state(['!disabled']) else: wid['changedefaults'].state(['disabled']) if selected_item.is_wip and selected_item.is_dep: wid['wip_dep']['text'] = 'WIP, Deprecated Item!' elif selected_item.is_wip: wid['wip_dep']['text'] = 'WIP Item!' elif selected_item.is_dep: wid['wip_dep']['text'] = 'Deprecated Item!' else: wid['wip_dep']['text'] = '' version_lookup, version_names = selected_item.get_version_names() if len(version_names) <= 1: # There aren't any alternates to choose from, disable the box wid['variant'].state(['disabled']) # We want to display WIP / Dep tags still, so users know. if selected_item.is_wip and selected_item.is_dep: wid['variant']['values'] = ['[WIP] [DEP] No Alts!'] elif selected_item.is_wip: wid['variant']['values'] = ['[WIP] No Alt Versions!'] elif selected_item.is_dep: wid['variant']['values'] = ['[DEP] No Alt Versions!'] else: wid['variant']['values'] = ['No Alternate Versions!'] wid['variant'].current(0) else: wid['variant'].state(['!disabled']) wid['variant']['values'] = version_names wid['variant'].current(version_lookup.index( selected_item.selected_ver)) if selected_item.url is None: wid['moreinfo'].state(['disabled']) else: wid['moreinfo'].state(['!disabled']) wid['moreinfo'].tooltip_text = selected_item.url editor_data = item_data['editor'] has_inputs = False has_polarity = False has_outputs = False for inp_list in editor_data.find_all("Exporting", "Inputs"): for inp in inp_list: if inp.name == "CONNECTION_STANDARD": has_inputs = True elif inp.name == "CONNECTION_TBEAM_POLARITY": has_polarity = True for out_list in editor_data.find_all("Exporting", "Outputs"): for out in out_list: if out.name == "CONNECTION_STANDARD": has_outputs = True break has_timer = any(editor_data.find_all("Properties", "TimerDelay")) editor_bit = next(editor_data.find_all("Editor")) rot_type = editor_bit["MovementHandle", "HANDLE_NONE"].casefold() facing_type = editor_bit["InvalidSurface", ""].casefold() surf_wall = "wall" in facing_type surf_floor = "floor" in facing_type surf_ceil = "ceiling" in facing_type is_embed = any(editor_data.find_all("Exporting", "EmbeddedVoxels")) if has_inputs: if has_polarity: wid['sprite'][0]['image'] = png.spr('in_polarity') else: wid['sprite'][0]['image'] = png.spr('in_norm') else: wid['sprite'][0]['image'] = png.spr('in_none') if has_outputs: if has_timer: wid['sprite'][1]['image'] = png.spr('out_tim') else: wid['sprite'][1]['image'] = png.spr('out_norm') else: wid['sprite'][1]['image'] = png.spr('out_none') wid['sprite'][2]['image'] = png.spr( ROT_TYPES.get( rot_type.casefold(), 'rot_none', )) if is_embed: wid['sprite'][3]['image'] = png.spr('space_embed') else: wid['sprite'][3]['image'] = png.spr('space_none') face_spr = "surf" if not surf_wall: face_spr += "_wall" if not surf_floor: face_spr += "_floor" if not surf_ceil: face_spr += "_ceil" if face_spr == "surf": face_spr += "_none" wid['sprite'][4]['image'] = png.spr(face_spr)
from BEE2_config import ConfigFile from srctools import Property from tk_tools import TK_ROOT LOGGER = utils.getLogger(__name__) voice_item = None UI = {} TABS = {} QUOTE_FONT = font.nametofont('TkHeadingFont').copy() QUOTE_FONT['weight'] = 'bold' SP_IMG = img.png('icons/quote_sp') COOP_IMG = img.png('icons/quote_coop') # Friendly names given to certain response channels. RESPONSE_NAMES = { 'death_goo': _('Death - Toxic Goo'), 'death_turret': _('Death - Turrets'), 'death_crush': _('Death - Crusher'), 'death_laserfield': _('Death - LaserField'), } config = config_mid = config_resp = None # type: ConfigFile class TabTypes(Enum): NORM = 0
def load_item_data(): """Refresh the window to use the selected item's data.""" global version_lookup item_data = selected_item.data for ind, pos in enumerate(SUBITEM_POS[selected_item.num_sub]): if pos == -1: wid['subitem'][ind]['image'] = png.png('BEE2/alpha_64') else: wid['subitem'][ind]['image'] = selected_item.get_icon(pos) wid['subitem'][ind]['relief'] = 'flat' wid['subitem'][pos_for_item()]['relief'] = 'raised' wid['author']['text'] = ', '.join(item_data['auth']) wid['name']['text'] = selected_sub_item.name wid['ent_count']['text'] = item_data['ent'] wid['desc'].set_text( get_description( global_last=selected_item.item.glob_desc_last, glob_desc=selected_item.item.glob_desc, style_desc=item_data['desc'] ) ) if itemPropWin.can_edit(selected_item.properties()): wid['changedefaults'].state(['!disabled']) else: wid['changedefaults'].state(['disabled']) if selected_item.is_wip and selected_item.is_dep: wid['wip_dep']['text'] = 'WIP, Deprecated Item!' elif selected_item.is_wip: wid['wip_dep']['text'] = 'WIP Item!' elif selected_item.is_dep: wid['wip_dep']['text'] = 'Deprecated Item!' else: wid['wip_dep']['text'] = '' version_lookup, version_names = selected_item.get_version_names() if len(version_names) <= 1: # There aren't any alternates to choose from, disable the box wid['variant'].state(['disabled']) # We want to display WIP / Dep tags still, so users know. if selected_item.is_wip and selected_item.is_dep: wid['variant']['values'] = ['[WIP] [DEP] No Alts!'] elif selected_item.is_wip: wid['variant']['values'] = ['[WIP] No Alt Versions!'] elif selected_item.is_dep: wid['variant']['values'] = ['[DEP] No Alt Versions!'] else: wid['variant']['values'] = ['No Alternate Versions!'] wid['variant'].current(0) else: wid['variant'].state(['!disabled']) wid['variant']['values'] = version_names wid['variant'].current(version_lookup.index(selected_item.selected_ver)) if selected_item.url is None: wid['moreinfo'].state(['disabled']) else: wid['moreinfo'].state(['!disabled']) wid['moreinfo'].tooltip_text = selected_item.url editor_data = item_data['editor'] has_inputs = False has_polarity = False has_outputs = False for inp_list in editor_data.find_all("Exporting", "Inputs"): for inp in inp_list: if inp.name == "connection_standard": has_inputs = True elif inp.name == "connection_tbeam_polarity": has_polarity = True for out_list in editor_data.find_all("Exporting", "Outputs"): for out in out_list: if out.name == "connection_standard": has_outputs = True break has_timer = any(editor_data.find_all("Properties", "TimerDelay")) editor_bit = next(editor_data.find_all("Editor")) rot_type = editor_bit["MovementHandle", "HANDLE_NONE"].casefold() facing_type = editor_bit["InvalidSurface", ""].casefold() surf_wall = "wall" in facing_type surf_floor = "floor" in facing_type surf_ceil = "ceiling" in facing_type is_embed = any(editor_data.find_all("Exporting", "EmbeddedVoxels")) if has_inputs: if has_polarity: wid['sprite'][0]['image'] = png.spr('in_polarity') else: wid['sprite'][0]['image'] = png.spr('in_norm') else: wid['sprite'][0]['image'] = png.spr('in_none') if has_outputs: if has_timer: wid['sprite'][1]['image'] = png.spr('out_tim') else: wid['sprite'][1]['image'] = png.spr('out_norm') else: wid['sprite'][1]['image'] = png.spr('out_none') wid['sprite'][2]['image'] = png.spr( ROT_TYPES.get( rot_type.casefold(), 'rot_none', ) ) if is_embed: wid['sprite'][3]['image'] = png.spr('space_embed') else: wid['sprite'][3]['image'] = png.spr('space_none') face_spr = "surf" if not surf_wall: face_spr += "_wall" if not surf_floor: face_spr += "_floor" if not surf_ceil: face_spr += "_ceil" if face_spr == "surf": face_spr += "_none" wid['sprite'][4]['image'] = png.spr(face_spr)
QUOTE_FONT = font.nametofont('TkHeadingFont').copy() QUOTE_FONT['weight'] = 'bold' IMG_TEXT = { 'sp': _('Singleplayer'), 'coop': _('Cooperative'), 'atlas': _('ATLAS (SP/Coop)'), 'pbody': _('P-Body (SP/Coop)'), 'bendy': _('Bendy'), 'chell': _('Chell'), 'human': _('Human characters (Bendy and Chell)'), 'robot': _('AI characters (ATLAS, P-Body, or Coop)'), } IMG = { spr: (img.png('icons/quote_' + spr), ctx) for spr, ctx in IMG_TEXT.items() } # type: Dict[str, Tuple[PhotoImage, str]] # Friendly names given to certain response channels. RESPONSE_NAMES = { 'death_goo': _('Death - Toxic Goo'), 'death_turret': _('Death - Turrets'), 'death_crush': _('Death - Crusher'), 'death_laserfield': _('Death - LaserField'), } config = config_mid = config_resp = None # type: ConfigFile
def init(): """Initialise all widgets in the given window.""" for cat, btn_text in [ ('back_', 'Restore:'), ('game_', 'Backup:'), ]: UI[cat + 'frame'] = frame = ttk.Frame( window, ) UI[cat + 'title_frame'] = title_frame = ttk.Frame( frame, ) title_frame.grid(row=0, column=0, sticky='EW') UI[cat + 'title'] = ttk.Label( title_frame, font='TkHeadingFont', ) UI[cat + 'title'].grid(row=0, column=0) title_frame.rowconfigure(0, weight=1) title_frame.columnconfigure(0, weight=1) UI[cat + 'details'] = CheckDetails( frame, headers=HEADERS, ) UI[cat + 'details'].grid(row=1, column=0, sticky='NSEW') frame.rowconfigure(1, weight=1) frame.columnconfigure(0, weight=1) button_frame = ttk.Frame( frame, ) button_frame.grid(column=0, row=2) ttk.Label(button_frame, text=btn_text).grid(row=0, column=0) UI[cat + 'btn_all'] = ttk.Button( button_frame, text='All', width=3, ) UI[cat + 'btn_sel'] = ttk.Button( button_frame, text='Checked', width=8, ) UI[cat + 'btn_all'].grid(row=0, column=1) UI[cat + 'btn_sel'].grid(row=0, column=2) UI[cat + 'btn_del'] = ttk.Button( button_frame, text='Delete Checked', width=14, ) UI[cat + 'btn_del'].grid(row=1, column=0, columnspan=3) utils.add_mousewheel( UI[cat + 'details'].wid_canvas, UI[cat + 'frame'], ) UI['game_refresh'] = ttk.Button( UI['game_title_frame'], image=img.png('icons/tool_sub'), command=ui_refresh_game, ) UI['game_refresh'].grid(row=0, column=1, sticky='E') add_tooltip( UI['game_refresh'], "Reload the map list.", ) UI['game_title']['textvariable'] = game_name UI['back_title']['textvariable'] = backup_name UI['game_btn_all']['command'] = ui_backup_all UI['game_btn_sel']['command'] = ui_backup_sel UI['back_btn_all']['command'] = ui_restore_all UI['back_btn_sel']['command'] = ui_restore_sel UI['back_frame'].grid(row=1, column=0, sticky='NSEW') ttk.Separator(orient=tk.VERTICAL).grid( row=1, column=1, sticky='NS', padx=5, ) UI['game_frame'].grid(row=1, column=2, sticky='NSEW') window.rowconfigure(1, weight=1) window.columnconfigure(0, weight=1) window.columnconfigure(2, weight=1)
QUOTE_FONT = font.nametofont('TkHeadingFont').copy() QUOTE_FONT['weight'] = 'bold' IMG_TEXT = { 'sp': _('Singleplayer'), 'coop': _('Cooperative'), 'atlas': _('ATLAS (SP/Coop)'), 'pbody': _('P-Body (SP/Coop)'), 'bendy': _('Bendy'), 'chell': _('Chell'), 'human': _('Human characters (Bendy and Chell)'), 'robot': _('AI characters (ATLAS, P-Body, or Coop)'), } IMG = { spr: (img.png('icons/quote_' + spr), ctx) for spr, ctx in IMG_TEXT.items() } # type: Dict[str, Tuple[PhotoImage, str]] # Friendly names given to certain response channels. RESPONSE_NAMES = { 'death_goo': _('Death - Toxic Goo'), 'death_turret': _('Death - Turrets'), 'death_crush': _('Death - Crusher'), 'death_laserfield': _('Death - LaserField'), } config = config_mid = config_resp = None # type: ConfigFile class TabTypes(Enum):
def init_widgets(): """Initiallise all the window components.""" global prop_window, moreinfo_win prop_window = Toplevel(TK_ROOT) prop_window.overrideredirect(1) prop_window.resizable(False, False) prop_window.transient(master=TK_ROOT) prop_window.attributes('-topmost', 1) prop_window.relX = 0 prop_window.relY = 0 prop_window.withdraw() # starts hidden f = ttk.Frame(prop_window, relief="raised", borderwidth="4") f.grid(row=0, column=0) ttk.Label( f, text="Properties:", anchor="center", ).grid( row=0, column=0, columnspan=3, sticky="EW", ) wid['name'] = ttk.Label(f, text="", anchor="center") wid['name'].grid(row=1, column=0, columnspan=3, sticky="EW") wid['ent_count'] = ttk.Label( f, text="2", anchor="e", compound="left", image=png.spr('gear_ent'), ) wid['ent_count'].grid(row=0, column=2, rowspan=2, sticky=E) wid['author'] = ttk.Label(f, text="", anchor="center", relief="sunken") wid['author'].grid(row=2, column=0, columnspan=3, sticky="EW") sub_frame = ttk.Frame(f, borderwidth=4, relief="sunken") sub_frame.grid(column=0, columnspan=3, row=3) for i, _ in enumerate(wid['subitem']): wid['subitem'][i] = ttk.Label( sub_frame, image=png.png('BEE2/alpha_64'), ) wid['subitem'][i].grid(row=0, column=i) utils.bind_leftclick( wid['subitem'][i], functools.partial(sub_sel, i), ) utils.bind_rightclick( wid['subitem'][i], functools.partial(sub_open, i), ) wid['wip_dep'] = ttk.Label(f, text='', anchor="nw") wid['wip_dep'].grid(row=4, column=0, sticky="NW") ttk.Label(f, text="Description:", anchor="sw").grid( row=4, column=0, sticky="SW", ) spr_frame = ttk.Frame(f, borderwidth=4, relief="sunken") spr_frame.grid(column=1, columnspan=2, row=4, sticky=W) # sprites: inputs, outputs, rotation handle, occupied/embed state, # desiredFacing for i in range(5): spr = png.spr('ap_grey') wid['sprite'][i] = ttk.Label(spr_frame, image=spr, relief="raised") wid['sprite'][i].grid(row=0, column=i) desc_frame = ttk.Frame(f, borderwidth=4, relief="sunken") desc_frame.grid(row=5, column=0, columnspan=3, sticky="EW") desc_frame.columnconfigure(0, weight=1) wid['desc'] = tkRichText(desc_frame, width=40, height=8, font=None) wid['desc'].grid(row=0, column=0, sticky="EW") desc_scroll = ttk.Scrollbar( desc_frame, orient=VERTICAL, command=wid['desc'].yview, ) wid['desc']['yscrollcommand'] = desc_scroll.set desc_scroll.grid(row=0, column=1, sticky="NS") def show_more_info(): url = selected_item.url if url is not None: try: webbrowser.open(url, new=OPEN_IN_TAB, autoraise=True) except webbrowser.Error: if messagebox.askyesno( icon="error", title="BEE2 - Error", message='Failed to open a web browser. Do you wish for ' 'the URL to be copied to the clipboard ' 'instead?', detail='"{!s}"'.format(url), parent=prop_window): print("Saving " + url + "to clipboard!") TK_ROOT.clipboard_clear() TK_ROOT.clipboard_append(url) # Either the webbrowser or the messagebox could cause the # properties to move behind the main window, so hide it # so it doesn't appear there. hide_context(None) wid['moreinfo'] = ttk.Button(f, text="More Info>>", command=show_more_info) wid['moreinfo'].grid(row=6, column=2, sticky=E) tooltip.add_tooltip(wid['moreinfo']) menu_info = Menu(wid['moreinfo']) menu_info.add_command(label='', state='disabled') def show_item_props(): snd.fx('expand') itemPropWin.show_window( selected_item.get_properties(), wid['changedefaults'], selected_sub_item.name, ) wid['changedefaults'] = ttk.Button( f, text="Change Defaults...", command=show_item_props, ) wid['changedefaults'].grid(row=6, column=1) tooltip.add_tooltip( wid['changedefaults'], 'Change the default settings for this item when placed.') wid['variant'] = ttk.Combobox( f, values=['VERSION'], exportselection=0, # On Mac this defaults to being way too wide! width=7 if utils.MAC else None, ) wid['variant'].state(['readonly']) # Prevent directly typing in values wid['variant'].bind('<<ComboboxSelected>>', set_item_version) wid['variant'].current(0) wid['variant'].grid(row=6, column=0, sticky=W) itemPropWin.init(hide_item_props)
def __init__( self, tk, lst, has_none=True, has_def=True, has_snd_sample=False, none_desc=(('line', 'Do not add anything.'),), none_attrs: dict=utils.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. """ self.noneItem = Item( 'NONE', '', desc=none_desc, attributes=dict(none_attrs), ) self.noneItem.icon = img.png('BEE2/none_96') # 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 self.selected = self.item_list[0] # type: Item self.orig_selected = self.selected self.parent = tk self._readonly = False 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) self.win.iconbitmap('../BEE2.ico') # 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\u2500Suggested\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. self.prop_icon_frm = ttk.Frame( self.prop_frm, borderwidth=4, relief='raised', width=ICON_SIZE, height=ICON_SIZE, ) self.prop_icon_frm.grid(row=0, column=0, columnspan=4) self.prop_icon = ttk.Label(self.prop_icon_frm) self.prop_icon.img = img.png('BEE2/blank_96') self.prop_icon['image'] = self.prop_icon.img 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, 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, prefering 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, functools.partial(self.sel_item, item), ) utils.bind_leftclick_double( item.button, self.save, ) # 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
def init() -> None: """Initialise all widgets in the given window.""" for cat, btn_text in [ ('back_', _('Restore:')), ('game_', _('Backup:')), ]: UI[cat + 'frame'] = frame = ttk.Frame(window, ) UI[cat + 'title_frame'] = title_frame = ttk.Frame(frame, ) title_frame.grid(row=0, column=0, sticky='EW') UI[cat + 'title'] = ttk.Label( title_frame, font='TkHeadingFont', ) UI[cat + 'title'].grid(row=0, column=0) title_frame.rowconfigure(0, weight=1) title_frame.columnconfigure(0, weight=1) UI[cat + 'details'] = CheckDetails( frame, headers=HEADERS, ) UI[cat + 'details'].grid(row=1, column=0, sticky='NSEW') frame.rowconfigure(1, weight=1) frame.columnconfigure(0, weight=1) button_frame = ttk.Frame(frame, ) button_frame.grid(column=0, row=2) ttk.Label(button_frame, text=btn_text).grid(row=0, column=0) UI[cat + 'btn_all'] = ttk.Button( button_frame, text='All', width=3, ) UI[cat + 'btn_sel'] = ttk.Button( button_frame, text=_('Checked'), width=8, ) UI[cat + 'btn_all'].grid(row=0, column=1) UI[cat + 'btn_sel'].grid(row=0, column=2) UI[cat + 'btn_del'] = ttk.Button( button_frame, text=_('Delete Checked'), width=14, ) UI[cat + 'btn_del'].grid(row=1, column=0, columnspan=3) utils.add_mousewheel( UI[cat + 'details'].wid_canvas, UI[cat + 'frame'], ) UI['game_refresh'] = ttk.Button( UI['game_title_frame'], image=img.png('icons/tool_sub'), command=ui_refresh_game, ) UI['game_refresh'].grid(row=0, column=1, sticky='E') add_tooltip( UI['game_refresh'], "Reload the map list.", ) UI['game_title']['textvariable'] = game_name UI['back_title']['textvariable'] = backup_name UI['game_btn_all']['command'] = ui_backup_all UI['game_btn_sel']['command'] = ui_backup_sel UI['game_btn_del']['command'] = ui_delete_game UI['back_btn_all']['command'] = ui_restore_all UI['back_btn_sel']['command'] = ui_restore_sel UI['back_btn_del']['command'] = ui_delete_backup UI['back_frame'].grid(row=1, column=0, sticky='NSEW') ttk.Separator(orient=tk.VERTICAL).grid( row=1, column=1, sticky='NS', padx=5, ) UI['game_frame'].grid(row=1, column=2, sticky='NSEW') window.rowconfigure(1, weight=1) window.columnconfigure(0, weight=1) window.columnconfigure(2, weight=1)
def __init__( self, tk, lst, has_none=True, has_def=True, none_desc=(('line', 'Do not add anything.'),), title='BEE2', callback=_NO_OP, 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. - 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. """ self.noneItem = Item('NONE', '', desc=none_desc) self.noneItem.icon = img.png('BEE2/none_96') self.disp_label = StringVar() self.display = None self.disp_btn = None self.chosen_id = None self.callback = callback self.callback_params = callback_params self.suggested = None self.has_def = has_def if has_none: self.item_list = [self.noneItem] + lst else: self.item_list = lst self.selected = self.item_list[0] self.orig_selected = self.selected self.parent = tk self._readonly = False self.win = Toplevel(tk) self.win.withdraw() self.win.title("BEE2 - " + title) self.win.transient(master=tk) self.win.resizable(True, True) self.win.iconbitmap('../BEE2.ico') self.win.protocol("WM_DELETE_WINDOW", self.exit) self.win.bind("<Escape>", self.exit) self.win.columnconfigure(0, weight=1) self.win.rowconfigure(0, weight=1) # 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=0, column=0, sticky="NSEW") self.win.columnconfigure(0, weight=1) self.win.rowconfigure(0, 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 = ttk.Scrollbar( 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 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\u2500Suggested\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. self.prop_icon_frm = ttk.Frame( self.prop_frm, borderwidth=4, relief='raised', width=ICON_SIZE, height=ICON_SIZE, ) self.prop_icon_frm.grid(row=0, column=0, columnspan=4) self.prop_icon = ttk.Label(self.prop_icon_frm) self.prop_icon.img = img.png('BEE2/blank_96') self.prop_icon['image'] = self.prop_icon.img self.prop_icon.grid(row=0, column=0) self.prop_name = ttk.Label( self.prop_frm, text="Item", justify=CENTER, font=("Helvetica", 12, "bold"), ) self.prop_name.grid(row=1, column=0, columnspan=4) 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 = ttk.Scrollbar( 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 = 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'] = font.BOLD # Make a font for previewing the suggested item self.mouseover_font = self.norm_font.copy() self.mouseover_font['slant'] = font.ITALIC self.context_var = IntVar() # Re-order items appropriately. # self.item_list.sort( # Alphabetical order key=lambda it: it.longName ) self.item_list.sort( # Sort by group name key=lambda it: it.group.casefold() if it.group else '' ) self.item_list.sort( # Make non-grouped items go first key=lambda it: 2 if it.group else 1 ) for ind, item in enumerate(self.item_list): 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', ) self.context_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, functools.partial(self.sel_item, item), ) utils.bind_leftclick_double( item.button, self.save, ) 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, (at_id, desc, value) in enumerate(attributes): desc_label = ttk.Label( attr_frame, text=desc, ) self.attr[at_id] = val_label = ttk.Label( attr_frame, ) val_label.default = value if isinstance(value, bool): # It's a tick/cross label val_label['image'] = ( ICON_CHECK if value else ICON_CROSS, ) # 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
def __init__( self, tk, lst, has_none=True, has_def=True, none_desc=(('line', 'Do not add anything.'),), title='BEE2', callback=_NO_OP, callback_params=(), ): """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. - 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. """ self.noneItem = Item('NONE', '', desc=none_desc) self.noneItem.icon = img.png('BEE2/none_96') self.disp_label = StringVar() self.display = None self.disp_btn = None self.chosen_id = None self.callback = callback self.callback_params = callback_params self.suggested = None self.has_def = has_def if has_none: self.item_list = [self.noneItem] + lst else: self.item_list = lst self.selected = self.item_list[0] self.orig_selected = self.selected self.parent = tk self._readonly = False self.win = Toplevel(tk) self.win.withdraw() self.win.title("BEE2 - " + title) self.win.transient(master=tk) self.win.resizable(True, True) self.win.iconbitmap('../BEE2.ico') self.win.protocol("WM_DELETE_WINDOW", self.exit) self.win.bind("<Escape>", self.exit) # PanedWindow allows resizing the two areas independently. self.pane_win = ttk.Panedwindow(self.win, orient=HORIZONTAL) self.pane_win.grid(row=0, column=0, sticky="NSEW") self.wid = {} shim = ttk.Frame(self.pane_win, relief="sunken") self.win.rowconfigure(0, weight=1) self.win.columnconfigure(0, weight=1) 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 = ttk.Scrollbar( 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 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. self.prop_icon_frm = ttk.Frame( self.prop_frm, borderwidth=4, relief='raised', width=ICON_SIZE, height=ICON_SIZE, ) self.prop_icon_frm.grid(row=0, column=0, columnspan=4) self.prop_icon = ttk.Label(self.prop_icon_frm) self.prop_icon.img = img.png('BEE2/blank_96') self.prop_icon['image'] = self.prop_icon.img self.prop_icon.grid(row=0, column=0) self.prop_name = ttk.Label( self.prop_frm, text="Item", justify=CENTER, font=("Helvetica", 12, "bold"), ) self.prop_name.grid(row=1, column=0, columnspan=4) 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=16, font="TkSmallCaptionFont", ) self.prop_desc.grid( row=0, column=0, padx=(2, 0), pady=2, sticky='NSEW', ) self.prop_scroll = ttk.Scrollbar( 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=5, 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=5, column=1, sticky='EW', ) ttk.Button( self.prop_frm, text="Cancel", command=self.exit, ).grid( row=5, column=2, padx=(8, 8), ) self.win.option_add('*tearOff', False) self.context_menu = Menu(self.win) self.norm_font = 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'] = font.BOLD # Make a font for previewing the suggested item self.mouseover_font = self.norm_font.copy() self.mouseover_font['slant'] = font.ITALIC self.context_var = IntVar() # Re-order items appropriately. # self.item_list.sort( # Alphabetical order key=lambda it: it.longName ) self.item_list.sort( # Sort by group name key=lambda it: it.group.casefold() if it.group else '' ) self.item_list.sort( # Make non-grouped items go first key=lambda it: 2 if it.group else 1 ) for ind, item in enumerate(self.item_list): 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', ) self.context_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 item.button.bind( "<Button-1>", functools.partial(self.sel_item, item), ) item.button.bind("<Double-Button-1>", self.save) self.flow_items(None) self.wid_canvas.bind("<Configure>", self.flow_items) self.pane_win.add(shim, weight=1) self.pane_win.add(self.prop_frm)
def make_pane(tool_frame): """Create the styleVar pane. """ global window window = SubPane( TK_ROOT, options=GEN_OPTS, title='Style Properties', name='style', resize_y=True, tool_frame=tool_frame, tool_img=png.png('icons/win_stylevar'), tool_col=3, ) UI['style_can'] = Canvas(window, highlightthickness=0) # need to use a canvas to allow scrolling UI['style_can'].grid(sticky='NSEW') window.rowconfigure(0, weight=1) UI['style_scroll'] = ttk.Scrollbar( window, orient=VERTICAL, command=UI['style_can'].yview, ) UI['style_scroll'].grid(column=1, row=0, rowspan=2, sticky="NS") UI['style_can']['yscrollcommand'] = UI['style_scroll'].set utils.add_mousewheel(UI['style_can'], window) canvas_frame = ttk.Frame(UI['style_can']) frame_all = ttk.Labelframe(canvas_frame, text="All:") frame_all.grid(row=0, sticky='EW') frm_chosen = ttk.Labelframe(canvas_frame, text="Selected Style:") frm_chosen.grid(row=1, sticky='EW') ttk.Separator( canvas_frame, orient=HORIZONTAL, ).grid(row=2, sticky='EW', pady=(10, 5)) frm_other = ttk.Labelframe(canvas_frame, text="Other Styles:") frm_other.grid(row=3, sticky='EW') UI['stylevar_chosen_none'] = ttk.Label( frm_chosen, text='No Options!', font='TkMenuFont', justify='center', ) UI['stylevar_other_none'] = ttk.Label( frm_other, text='None!', font='TkMenuFont', justify='center', ) for pos, var in enumerate(styleOptions): # Add the special stylevars which apply to all styles tk_vars[var.id] = IntVar( value=GEN_OPTS.get_bool('StyleVar', var.id, var.default) ) checkbox_special[var.id] = ttk.Checkbutton( frame_all, variable=tk_vars[var.id], text=var.name, command=functools.partial(set_stylevar, var.id) ) checkbox_special[var.id].grid(row=pos, column=0, sticky="W", padx=3) tooltip.add_tooltip( checkbox_special[var.id], make_desc(var, is_hardcoded=True), ) for var in VAR_LIST: tk_vars[var.id] = IntVar(value=var.enabled) args = { 'variable': tk_vars[var.id], 'text': var.name, 'command': functools.partial(set_stylevar, var.id) } checkbox_chosen[var.id] = ttk.Checkbutton(frm_chosen, **args) checkbox_other[var.id] = ttk.Checkbutton(frm_other, **args) desc = make_desc(var) tooltip.add_tooltip( checkbox_chosen[var.id], desc, ) tooltip.add_tooltip( checkbox_other[var.id], desc, ) UI['style_can'].create_window(0, 0, window=canvas_frame, anchor="nw") UI['style_can'].update_idletasks() UI['style_can'].config( scrollregion=UI['style_can'].bbox(ALL), width=canvas_frame.winfo_reqwidth(), ) if utils.USE_SIZEGRIP: ttk.Sizegrip( window, cursor=utils.CURSORS['stretch_vert'], ).grid(row=1, column=0) UI['style_can'].bind('<Configure>', flow_stylevar)
""" from tkinter import * # ui library from tkinter import font from tkinter import ttk # themed ui components that match the OS import functools import math import img as png # png library for TKinter from richTextBox import tkRichText import utils ICON_SIZE = 96 # Size of the selector win icons ITEM_WIDTH = ICON_SIZE+16 ITEM_HEIGHT = ICON_SIZE+51 err_icon = png.png('BEE2/error_96', resize_to=96) def _NO_OP(*args): """The default callback, triggered whenever the chosen item is changed.""" pass class Item: """An item on the panel. - name: The item ID, used to distinguish it from others. - longName: The full item name. This can be very long. If not set, this will be the same as the short name. - shortName: A shortened version of the full name. This should be <= 20 characters.
import img import utils import tk_tools LOGGER = utils.getLogger(__name__) voice_item = None UI = {} TABS = {} QUOTE_FONT = font.nametofont('TkHeadingFont').copy() QUOTE_FONT['weight'] = 'bold' SP_IMG = img.png('icons/quote_sp') COOP_IMG = img.png('icons/quote_coop') # Friendly names given to certain response channels. RESPONSE_NAMES = { 'death_goo': 'Death - Toxic Goo', 'death_turret': 'Death - Turrets', 'death_crush': 'Death - Crusher', 'death_laserfield': 'Death - LaserField', } config = config_mid = config_resp = None # type: ConfigFile class TabTypes(Enum): NORM = 0
def make_pane(tool_frame): """Create the compiler options pane. """ global window window = SubPane.SubPane( TK_ROOT, options=GEN_OPTS, title='Compile Options', name='compiler', resize_x=True, resize_y=False, tool_frame=tool_frame, tool_img=png.png('icons/win_compiler'), tool_col=4, ) window.columnconfigure(0, weight=1) thumb_frame = ttk.LabelFrame( window, text='Thumbnail', labelanchor=N, ) thumb_frame.grid(row=0, column=0, sticky=EW) thumb_frame.columnconfigure(0, weight=1) UI['thumb_auto'] = ttk.Radiobutton( thumb_frame, text='Auto', value='AUTO', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_peti'] = ttk.Radiobutton( thumb_frame, text='PeTI', value='PETI', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_custom'] = ttk.Radiobutton( thumb_frame, text='Custom:', value='CUST', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_label'] = ttk.Label( thumb_frame, anchor=CENTER, cursor=utils.CURSORS['link'], ) UI['thumb_label'].bind( utils.EVENTS['LEFT'], find_screenshot, ) UI['thumb_cleanup'] = ttk.Checkbutton( thumb_frame, text='Cleanup old screenshots', variable=cleanup_screenshot, command=set_screenshot_cleanup, ) UI['thumb_auto'].grid(row=0, column=0, sticky='W') UI['thumb_peti'].grid(row=0, column=1, sticky='W') UI['thumb_custom'].grid(row=1, column=0, columnspan=2, sticky='NEW') UI['thumb_cleanup'].grid(row=3, columnspan=2, sticky='W') add_tooltip( UI['thumb_auto'], "Override the map image to use a screenshot automatically taken" "from the beginning of a chamber. Press F5 to take a new " "screenshot. If the map has not been previewed recently " "(within the last few hours), the default PeTI screenshot " "will be used instead." ) add_tooltip( UI['thumb_peti'], "Use the normal editor view for the map preview image." ) custom_tooltip = ( "Use a custom image for the map preview image. Click the " "screenshot to select.\n" "Images will be converted to JPEGs if needed." ) add_tooltip( UI['thumb_custom'], custom_tooltip, ) add_tooltip( UI['thumb_label'], custom_tooltip ) add_tooltip( UI['thumb_cleanup'], 'Automatically delete unused Automatic screenshots. ' 'Disable if you want to keep things in "portal2/screenshots". ' ) if chosen_thumb.get() == 'CUST': # Show this if the user has set it before UI['thumb_label'].grid(row=2, column=0, columnspan=2, sticky='EW') set_screenshot() # Load the last saved screenshot vrad_frame = ttk.LabelFrame( window, text='Lighting:', labelanchor=N, ) vrad_frame.grid(row=1, column=0, sticky=EW) UI['light_fast'] = ttk.Radiobutton( vrad_frame, text='Fast', value=0, variable=vrad_light_type, command=set_vrad_type, ) UI['light_fast'].grid(row=0, column=0) UI['light_full'] = ttk.Radiobutton( vrad_frame, text='Full', value=1, variable=vrad_light_type, command=set_vrad_type, ) UI['light_full'].grid(row=0, column=1) add_tooltip( UI['light_fast'], "Compile with lower-quality, fast lighting. This speeds " "up compile times, but does not appear as good. Some " "shadows may appear wrong.\n" "When publishing, this is ignored." ) add_tooltip( UI['light_full'], "Compile with high-quality lighting. This looks correct, " "but takes longer to compute. Use if you're arranging lights." "When publishing, this is always used." ) elev_frame = ttk.LabelFrame( window, text='Spawn at:', labelanchor=N, ) elev_frame.grid(row=2, column=0, sticky=EW) elev_frame.columnconfigure(0, weight=1) elev_frame.columnconfigure(1, weight=1) UI['elev_preview'] = ttk.Radiobutton( elev_frame, text='Entry Door', value=0, variable=start_in_elev, command=set_elev_type, ) UI['elev_elevator'] = ttk.Radiobutton( elev_frame, text='Elevator', value=1, variable=start_in_elev, command=set_elev_type, ) UI['elev_preview'].grid(row=0, column=0, sticky=W) UI['elev_elevator'].grid(row=0, column=1, sticky=W) add_tooltip( UI['elev_elevator'], "When previewing in SP, spawn inside the entry elevator. " "This also disables the map restarts when you reach the " "exit door. Use this to examine the entry and exit corridors." ) add_tooltip( UI['elev_preview'], "When previewing in SP, spawn just before the entry door. " "When you reach the exit door, the map will restart." ) corr_frame = ttk.LabelFrame( window, text='Corridor:', labelanchor=N, ) corr_frame.grid(row=3, column=0, sticky=EW) corr_frame.columnconfigure(0, weight=1) corr_frame.columnconfigure(1, weight=1) UI['corr_sp_entry'] = make_corr_combo( corr_frame, 'sp_entry', width=9, ) UI['corr_sp_exit'] = make_corr_combo( corr_frame, 'sp_exit', width=9, ) UI['corr_coop'] = make_corr_combo( corr_frame, 'coop', width=9, ) UI['corr_sp_entry'].grid(row=1, column=0, sticky=EW) UI['corr_sp_exit'].grid(row=1, column=1, sticky=EW) UI['corr_coop'].grid(row=2, column=1, sticky=EW) ttk.Label( corr_frame, text='SP Entry:', anchor=CENTER, ).grid(row=0, column=0, sticky=EW) ttk.Label( corr_frame, text='SP Exit:', anchor=CENTER, ).grid(row=0, column=1, sticky=EW) ttk.Label( corr_frame, text='Coop:', anchor=CENTER, ).grid(row=2, column=0, sticky=EW) model_frame = ttk.LabelFrame( window, text='Player Model (SP):', labelanchor=N, ) model_frame.grid(row=4, column=0, sticky=EW) UI['player_mdl'] = ttk.Combobox( model_frame, exportselection=0, textvariable=player_model_var, values=PLAYER_MODEL_ORDER, width=20, ) # Users can only use the dropdown UI['player_mdl'].state(['readonly']) UI['player_mdl'].grid(row=0, column=0, sticky=EW) UI['player_mdl'].bind('<<ComboboxSelected>>', set_model) model_frame.columnconfigure(0, weight=1) count_frame = ttk.LabelFrame( window, text='Last Compile:', labelanchor=N, ) count_frame.grid(row=5, column=0, sticky=EW) count_frame.columnconfigure(0, weight=1) count_frame.columnconfigure(2, weight=1) ttk.Label( count_frame, text='Entity', anchor=N, ).grid(row=0, column=0, columnspan=3, sticky=EW) UI['count_ent'] = ttk.Progressbar( count_frame, maximum=MAX_ENTS, variable=count_ents, length=120, ) UI['count_ent'].grid( row=1, column=0, columnspan=3, sticky=EW, padx=5, ) ttk.Label( count_frame, text='Overlay', anchor=CENTER, ).grid(row=2, column=0, sticky=EW) UI['count_over'] = ttk.Progressbar( count_frame, maximum=MAX_OVERLAY, variable=count_overlay, length=50, ) UI['count_over'].grid(row=3, column=0, sticky=EW, padx=5) UI['refresh_counts'] = SubPane.make_tool_button( count_frame, png.png('icons/tool_sub', resize_to=16), refresh_counts, ) UI['refresh_counts'].grid(row=3, column=1) ttk.Label( count_frame, text='Brush', anchor=CENTER, ).grid(row=2, column=2, sticky=EW) UI['count_brush'] = ttk.Progressbar( count_frame, maximum=MAX_BRUSH, variable=count_brush, length=50, ) UI['count_brush'].grid(row=3, column=2, sticky=EW, padx=5) UI['view_logs'] = ttk.Button( count_frame, text='View Logs', ) UI['view_logs'].grid(row=4, column=0, columnspan=3, sticky=EW) refresh_counts(reload=False)
def make_pane(tool_frame): """Create the styleVar pane. """ global window window = SubPane( TK_ROOT, options=GEN_OPTS, title="Style Properties", name="style", resize_y=True, tool_frame=tool_frame, tool_img=png.png("icons/win_stylevar"), tool_col=3, ) UI["style_can"] = Canvas(window, highlightthickness=0) # need to use a canvas to allow scrolling UI["style_can"].grid(sticky="NSEW") window.rowconfigure(0, weight=1) UI["style_scroll"] = ttk.Scrollbar(window, orient=VERTICAL, command=UI["style_can"].yview) UI["style_scroll"].grid(column=1, row=0, rowspan=2, sticky="NS") UI["style_can"]["yscrollcommand"] = UI["style_scroll"].set canvas_frame = ttk.Frame(UI["style_can"]) frame_all = ttk.Labelframe(canvas_frame, text="All:") frame_all.grid(row=0, sticky="EW") frm_chosen = ttk.Labelframe(canvas_frame, text="Selected Style:") frm_chosen.grid(row=1, sticky="EW") ttk.Separator(canvas_frame, orient=HORIZONTAL).grid(row=2, sticky="EW", pady=(10, 5)) frm_other = ttk.Labelframe(canvas_frame, text="Other Styles:") frm_other.grid(row=3, sticky="EW") UI["stylevar_chosen_none"] = ttk.Label(frm_chosen, text="No Options!", font="TkMenuFont", justify="center") UI["stylevar_other_none"] = ttk.Label(frm_other, text="None!", font="TkMenuFont", justify="center") for pos, var in enumerate(styleOptions): # Add the special stylevars which apply to all styles tk_vars[var.id] = IntVar(value=GEN_OPTS.get_bool("StyleVar", var.id, var.enabled)) checkbox_special[var.id] = ttk.Checkbutton( frame_all, variable=tk_vars[var.id], text=var.name, command=functools.partial(set_stylevar, var.id) ) checkbox_special[var.id].grid(row=pos, column=0, sticky="W", padx=3) if var.desc: tooltip.add_tooltip(checkbox_special[var.id], var.desc) for var in var_list: tk_vars[var.id] = IntVar(value=var.default) args = {"variable": tk_vars[var.id], "text": var.name, "command": functools.partial(set_stylevar, var.id)} checkbox_chosen[var.id] = ttk.Checkbutton(frm_chosen, **args) checkbox_other[var.id] = ttk.Checkbutton(frm_other, **args) if var.desc: tooltip.add_tooltip(checkbox_chosen[var.id], var.desc) tooltip.add_tooltip(checkbox_other[var.id], var.desc) UI["style_can"].create_window(0, 0, window=canvas_frame, anchor="nw") UI["style_can"].update_idletasks() UI["style_can"].config(scrollregion=UI["style_can"].bbox(ALL), width=canvas_frame.winfo_reqwidth()) ttk.Sizegrip(window, cursor=utils.CURSORS["stretch_vert"]).grid(row=1, column=0) UI["style_can"].bind("<Configure>", flow_stylevar) # Scroll globally even if canvas is not selected. if utils.WIN: window.bind("<MouseWheel>", lambda e: scroll(int(-1 * (e.delta / 120)))) elif utils.MAC: window.bind("<Button-4>", lambda e: scroll(1)) window.bind("<Button-5>", lambda e: scroll(-1))
def __init__(self, *args): super().__init__(*args) self.is_compact = True font = Font( family='Times', # Generic special case size=-18, # negative = in pixels weight='bold', ) # Must be done late, so we know TK is initialised. import img logo_img = img.png('BEE2/splash_logo') self.lrg_canvas = tk.Canvas(self.win) self.sml_canvas = tk.Canvas( self.win, background='#009678', # 0, 150, 120 ) sml_width = int(min(self.win.winfo_screenwidth() * 0.5, 400)) sml_height = int(min(self.win.winfo_screenheight() * 0.5, 175)) self.lrg_canvas.create_image( 10, 10, anchor='nw', image=logo_img, ) self.sml_canvas.create_text( sml_width / 2, 40, anchor='n', text=self.title_text, fill='white', font=font, ) self.sml_canvas.create_text( sml_width / 2, 60, anchor='n', text=TRANSLATION['version'], fill='white', font=font, ) text1 = self.lrg_canvas.create_text( 10, 125, anchor='nw', text=self.title_text, fill='white', font=font, ) text2 = self.lrg_canvas.create_text( 10, 145, anchor='nw', text=TRANSLATION['version'], fill='white', font=font, ) # Now add shadows behind the text, and draw to the canvas. splash, lrg_width, lrg_height = img.make_splash_screen( max(self.win.winfo_screenwidth() * 0.6, 500), max(self.win.winfo_screenheight() * 0.6, 500), base_height=len(self.stages) * 20, text1_bbox=self.lrg_canvas.bbox(text1), text2_bbox=self.lrg_canvas.bbox(text2), ) self.splash_img = splash # Keep this alive self.lrg_canvas.tag_lower(self.lrg_canvas.create_image( 0, 0, anchor='nw', image=splash, )) self.canvas = [ (self.lrg_canvas, lrg_width, lrg_height), (self.sml_canvas, sml_width, sml_height), ] for canvas, width, height in self.canvas: canvas.create_rectangle( width - 40, 0, width - 20, 20, fill='#00785A', width=0, tags='resize_button', ) # 150, 120, 64 # Diagonal part of arrow. canvas.create_line( width - 20 - 4, 4, width - 20 - 16, 16, fill='black', width=2, tags='resize_button', ) canvas.tag_bind( 'resize_button', '<Button-1>', self.compact_button( canvas is self.lrg_canvas, width, lrg_width if width == sml_width else sml_width, ), ) self.sml_canvas.create_line( sml_width - 20 - 4, 4, sml_width - 20 - 16, 4, fill='black', width=2, tags='resize_button', ) self.sml_canvas.create_line( sml_width - 20 - 4, 4, sml_width - 20 - 4, 16, fill='black', width=2, tags='resize_button', ) self.lrg_canvas.create_line( lrg_width - 20 - 16, 16, lrg_width - 20 - 4, 16, fill='black', width=2, tags='resize_button', ) self.lrg_canvas.create_line( lrg_width - 20 - 16, 16, lrg_width - 20 - 16, 4, fill='black', width=2, tags='resize_button', ) for canvas, width, height in self.canvas: canvas['width'] = width canvas['height'] = height canvas.bind(utils.EVENTS['LEFT_DOUBLE'], self.toggle_compact) canvas.create_rectangle( width-20, 0, width, 20, fill='#00785A', width=0, tags='quit_button', ) canvas.create_rectangle( width-20, 0, width, 20, fill='#00785A', width=0, tags='quit_button', ) # 150, 120, 64 canvas.create_line( width-16, 4, width-4, 16, fill='black', width=2, tags='quit_button', ) canvas.create_line( width-4, 4, width-16, 16, fill='black', width=2, tags='quit_button', ) canvas.tag_bind('quit_button', '<Button-1>', self.cancel) for ind, (st_id, stage_name) in enumerate(reversed(self.stages), start=1): canvas.create_rectangle( 20, height - (ind + 0.5) * 20, 20, height - (ind - 0.5) * 20, fill='#00785A', # 0, 120, 90 width=0, tags='bar_' + st_id, ) # Border canvas.create_rectangle( 20, height - (ind + 0.5) * 20, width - 20, height - (ind - 0.5) * 20, outline='#00785A', width=2, ) canvas.create_text( 25, height - ind * 20, anchor='w', text=stage_name + ': (0/???)', fill='white', tags='text_' + st_id, )
def __init__(self, *args): super().__init__(*args) self.is_compact = True font = Font( family='Times', # Generic special case size=-18, # negative = in pixels weight='bold', ) # Must be done late, so we know TK is initialised. import img logo_img = img.png('BEE2/splash_logo') self.lrg_canvas = tk.Canvas(self.win) self.sml_canvas = tk.Canvas( self.win, background='#009678', # 0, 150, 120 ) sml_width = int(min(self.win.winfo_screenwidth() * 0.5, 400)) sml_height = int(min(self.win.winfo_screenheight() * 0.5, 175)) self.lrg_canvas.create_image( 10, 10, anchor='nw', image=logo_img, ) self.sml_canvas.create_text( sml_width / 2, 40, anchor='n', text=self.title_text, fill='white', font=font, ) self.sml_canvas.create_text( sml_width / 2, 60, anchor='n', text=TRANSLATION['version'], fill='white', font=font, ) text1 = self.lrg_canvas.create_text( 10, 125, anchor='nw', text=self.title_text, fill='white', font=font, ) text2 = self.lrg_canvas.create_text( 10, 145, anchor='nw', text=TRANSLATION['version'], fill='white', font=font, ) # Now add shadows behind the text, and draw to the canvas. splash, lrg_width, lrg_height = img.make_splash_screen( max(self.win.winfo_screenwidth() * 0.6, 500), max(self.win.winfo_screenheight() * 0.6, 500), base_height=len(self.stages) * 20, text1_bbox=self.lrg_canvas.bbox(text1), text2_bbox=self.lrg_canvas.bbox(text2), ) self.splash_img = splash # Keep this alive self.lrg_canvas.tag_lower( self.lrg_canvas.create_image( 0, 0, anchor='nw', image=splash, )) self.canvas = [ (self.lrg_canvas, lrg_width, lrg_height), (self.sml_canvas, sml_width, sml_height), ] for canvas, width, height in self.canvas: canvas.create_rectangle( width - 40, 0, width - 20, 20, fill='#00785A', width=0, tags='resize_button', ) # 150, 120, 64 # Diagonal part of arrow. canvas.create_line( width - 20 - 4, 4, width - 20 - 16, 16, fill='black', width=2, tags='resize_button', ) canvas.tag_bind( 'resize_button', '<Button-1>', self.compact_button( canvas is self.lrg_canvas, width, lrg_width if width == sml_width else sml_width, ), ) self.sml_canvas.create_line( sml_width - 20 - 4, 4, sml_width - 20 - 16, 4, fill='black', width=2, tags='resize_button', ) self.sml_canvas.create_line( sml_width - 20 - 4, 4, sml_width - 20 - 4, 16, fill='black', width=2, tags='resize_button', ) self.lrg_canvas.create_line( lrg_width - 20 - 16, 16, lrg_width - 20 - 4, 16, fill='black', width=2, tags='resize_button', ) self.lrg_canvas.create_line( lrg_width - 20 - 16, 16, lrg_width - 20 - 16, 4, fill='black', width=2, tags='resize_button', ) for canvas, width, height in self.canvas: canvas['width'] = width canvas['height'] = height canvas.bind(utils.EVENTS['LEFT_DOUBLE'], self.toggle_compact) canvas.create_rectangle( width - 20, 0, width, 20, fill='#00785A', width=0, tags='quit_button', ) canvas.create_rectangle( width - 20, 0, width, 20, fill='#00785A', width=0, tags='quit_button', ) # 150, 120, 64 canvas.create_line( width - 16, 4, width - 4, 16, fill='black', width=2, tags='quit_button', ) canvas.create_line( width - 4, 4, width - 16, 16, fill='black', width=2, tags='quit_button', ) canvas.tag_bind('quit_button', '<Button-1>', self.cancel) for ind, (st_id, stage_name) in enumerate(reversed(self.stages), start=1): canvas.create_rectangle( 20, height - (ind + 0.5) * 20, 20, height - (ind - 0.5) * 20, fill='#00785A', # 0, 120, 90 width=0, tags='bar_' + st_id, ) # Border canvas.create_rectangle( 20, height - (ind + 0.5) * 20, width - 20, height - (ind - 0.5) * 20, outline='#00785A', width=2, ) canvas.create_text( 25, height - ind * 20, anchor='w', text=stage_name + ': (0/???)', fill='white', tags='text_' + st_id, )
def make_pane(tool_frame): """Create the compiler options pane. """ global window window = SubPane( TK_ROOT, options=GEN_OPTS, title='Compile Opt', name='compiler', resize_x=True, resize_y=False, tool_frame=tool_frame, tool_img=png.png('icons/win_compiler'), tool_col=4, ) window.columnconfigure(0, weight=1) thumb_frame = ttk.LabelFrame( window, text='Thumbnail', labelanchor=N, ) thumb_frame.grid(row=0, column=0, sticky=EW) thumb_frame.columnconfigure(0, weight=1) UI['thumb_auto'] = ttk.Radiobutton( thumb_frame, text='Auto', value='AUTO', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_peti'] = ttk.Radiobutton( thumb_frame, text='PeTI', value='PETI', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_custom'] = ttk.Radiobutton( thumb_frame, text='Custom:', value='CUST', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_label'] = ttk.Label( thumb_frame, anchor=CENTER, cursor=utils.CURSORS['link'], ) UI['thumb_label'].bind( utils.EVENTS['LEFT'], find_screenshot, ) UI['thumb_cleanup'] = ttk.Checkbutton( thumb_frame, text='Cleanup old screenshots', variable=cleanup_screenshot, command=set_screenshot_cleanup, ) UI['thumb_auto'].grid(row=0, column=0, sticky='W') UI['thumb_peti'].grid(row=0, column=1, sticky='W') UI['thumb_custom'].grid(row=1, column=0, columnspan=2, sticky='NEW') UI['thumb_cleanup'].grid(row=3, columnspan=2, sticky='W') add_tooltip( UI['thumb_auto'], "Override the map image to use a screenshot automatically taken" "from the beginning of a chamber. Press F5 to take a new " "screenshot. If the map has not been previewed recently " "(within the last few hours), the default PeTI screenshot " "will be used instead.") add_tooltip(UI['thumb_peti'], "Use the normal editor view for the map preview image.") custom_tooltip = ( "Use a custom image for the map preview image. Click the " "screenshot to select.\n" "Images will be converted to JPEGs if needed.") add_tooltip( UI['thumb_custom'], custom_tooltip, ) add_tooltip(UI['thumb_label'], custom_tooltip) add_tooltip( UI['thumb_cleanup'], 'Automatically delete unused Automatic screenshots. ' 'Disable if you want to keep things in "portal2/screenshots". ') if chosen_thumb.get() == 'CUST': # Show this if the user has set it before UI['thumb_label'].grid(row=2, column=0, columnspan=2, sticky='EW') set_screenshot() # Load the last saved screenshot vrad_frame = ttk.LabelFrame( window, text='Lighting:', labelanchor=N, ) vrad_frame.grid(row=1, column=0, sticky=EW) UI['light_fast'] = ttk.Radiobutton( vrad_frame, text='Fast', value=0, variable=vrad_light_type, command=set_vrad_type, ) UI['light_fast'].grid(row=0, column=0) UI['light_full'] = ttk.Radiobutton( vrad_frame, text='Full', value=1, variable=vrad_light_type, command=set_vrad_type, ) UI['light_full'].grid(row=0, column=1) add_tooltip( UI['light_fast'], "Compile with lower-quality, fast lighting. This speeds " "up compile times, but does not appear as good. Some " "shadows may appear wrong.\n" "When publishing, this is ignored.") add_tooltip( UI['light_full'], "Compile with high-quality lighting. This looks correct, " "but takes longer to compute. Use if you're arranging lights." "When publishing, this is always used.") elev_frame = ttk.LabelFrame( window, text='Spawn at:', labelanchor=N, ) elev_frame.grid(row=2, column=0, sticky=EW) elev_frame.columnconfigure(0, weight=1) elev_frame.columnconfigure(1, weight=1) UI['elev_preview'] = ttk.Radiobutton( elev_frame, text='Entry Door', value=0, variable=start_in_elev, command=set_elev_type, ) UI['elev_elevator'] = ttk.Radiobutton( elev_frame, text='Elevator', value=1, variable=start_in_elev, command=set_elev_type, ) UI['elev_preview'].grid(row=0, column=0, sticky=W) UI['elev_elevator'].grid(row=0, column=1, sticky=W) add_tooltip( UI['elev_preview'], "When previewing in SP, spawn inside the entry elevator. " "This also disables the map restarts when you reach the " "exit door. Use this to examine the entry and exit corridors.") add_tooltip( UI['elev_elevator'], "When previewing in SP, spawn just before the entry door. " "When you reach the exit door, the map will restart.") corr_frame = ttk.LabelFrame( window, text='Corridor:', labelanchor=N, ) corr_frame.grid(row=3, column=0, sticky=EW) corr_frame.columnconfigure(0, weight=1) corr_frame.columnconfigure(1, weight=1) UI['corr_sp_entry'] = make_corr_combo( corr_frame, 'sp_entry', width=9, ) UI['corr_sp_exit'] = make_corr_combo( corr_frame, 'sp_exit', width=9, ) UI['corr_coop'] = make_corr_combo( corr_frame, 'coop', width=9, ) UI['corr_sp_entry'].grid(row=1, column=0, sticky=EW) UI['corr_sp_exit'].grid(row=1, column=1, sticky=EW) UI['corr_coop'].grid(row=2, column=1, sticky=EW) ttk.Label( corr_frame, text='SP Entry:', anchor=CENTER, ).grid(row=0, column=0, sticky=EW) ttk.Label( corr_frame, text='SP Exit:', anchor=CENTER, ).grid(row=0, column=1, sticky=EW) ttk.Label( corr_frame, text='Coop:', anchor=CENTER, ).grid(row=2, column=0, sticky=EW) model_frame = ttk.LabelFrame( window, text='Player Model (SP):', labelanchor=N, ) model_frame.grid(row=4, column=0, sticky=EW) UI['player_mdl'] = ttk.Combobox( model_frame, exportselection=0, textvariable=player_model_var, values=PLAYER_MODEL_ORDER, width=20, ) # Users can only use the dropdown UI['player_mdl'].state(['readonly']) UI['player_mdl'].grid(row=0, column=0, sticky=EW) UI['player_mdl'].bind('<<ComboboxSelected>>', set_model) model_frame.columnconfigure(0, weight=1) count_frame = ttk.LabelFrame( window, text='Last Compile:', labelanchor=N, ) count_frame.grid(row=5, column=0, sticky=EW) count_frame.columnconfigure(0, weight=1) count_frame.columnconfigure(2, weight=1) ttk.Label( count_frame, text='Entity', anchor=N, ).grid(row=0, column=0, columnspan=3, sticky=EW) UI['count_ent'] = ttk.Progressbar( count_frame, maximum=MAX_ENTS, variable=count_ents, length=120, ) UI['count_ent'].grid( row=1, column=0, columnspan=3, sticky=EW, padx=5, ) ttk.Label( count_frame, text='Overlay', anchor=CENTER, ).grid(row=2, column=0, sticky=EW) UI['count_over'] = ttk.Progressbar( count_frame, maximum=MAX_OVERLAY, variable=count_overlay, length=50, ) UI['count_over'].grid(row=3, column=0, sticky=EW, padx=5) ttk.Button( count_frame, image=png.png('icons/tool_sub'), command=refresh_counts, ).grid(row=3, column=1) ttk.Label( count_frame, text='Brush', anchor=CENTER, ).grid(row=2, column=2, sticky=EW) UI['count_brush'] = ttk.Progressbar( count_frame, maximum=MAX_BRUSH, variable=count_brush, length=50, ) UI['count_brush'].grid(row=3, column=2, sticky=EW, padx=5) UI['view_logs'] = ttk.Button( count_frame, text='View Logs', ) UI['view_logs'].grid(row=4, column=0, columnspan=3, sticky=EW) refresh_counts(reload=False)
def make_pane(tool_frame): """Create the styleVar pane. """ global window window = SubPane( TK_ROOT, options=GEN_OPTS, title='Style Properties', name='style', resize_y=True, tool_frame=tool_frame, tool_img=png.png('icons/win_stylevar'), tool_col=2, ) UI['style_can'] = Canvas(window, highlightthickness=0) # need to use a canvas to allow scrolling UI['style_can'].grid(sticky='NSEW') window.rowconfigure(0, weight=1) UI['style_scroll'] = ttk.Scrollbar( window, orient=VERTICAL, command=UI['style_can'].yview, ) UI['style_scroll'].grid(column=1, row=0, rowspan=2, sticky="NS") UI['style_can']['yscrollcommand'] = UI['style_scroll'].set canvas_frame = ttk.Frame(UI['style_can']) frame_all = ttk.Labelframe(canvas_frame, text="All:") frame_all.grid(row=0, sticky='EW') frm_chosen = ttk.Labelframe(canvas_frame, text="Selected Style:") frm_chosen.grid(row=1, sticky='EW') ttk.Separator( canvas_frame, orient=HORIZONTAL, ).grid(row=2, sticky='EW', pady=(10, 5)) frm_other = ttk.Labelframe(canvas_frame, text="Other Styles:") frm_other.grid(row=3, sticky='EW') UI['stylevar_chosen_none'] = ttk.Label( frm_chosen, text='No Options!', font='TkMenuFont', justify='center', ) UI['stylevar_other_none'] = ttk.Label( frm_other, text='None!', font='TkMenuFont', justify='center', ) for pos, (var_id, name, default) in enumerate(styleOptions): # Add the special stylevars which apply to all styles tk_vars[var_id] = IntVar( value=GEN_OPTS.get_bool('StyleVar', var_id, default) ) checkbox_special[var_id] = ttk.Checkbutton( frame_all, variable=tk_vars[var_id], text=name, command=functools.partial(set_stylevar, var_id) ) checkbox_special[var_id].grid(row=pos, column=0, sticky="W", padx=3) for var in var_list: tk_vars[var.id] = IntVar(value=var.default) args = { 'variable': tk_vars[var.id], 'text': var.name, 'command': functools.partial(set_stylevar, var.id) } checkbox_chosen[var.id] = ttk.Checkbutton(frm_chosen, **args) checkbox_other[var.id] = ttk.Checkbutton(frm_other, **args) UI['style_can'].create_window(0, 0, window=canvas_frame, anchor="nw") UI['style_can'].update_idletasks() UI['style_can'].config( scrollregion=UI['style_can'].bbox(ALL), width=canvas_frame.winfo_reqwidth(), ) ttk.Sizegrip( window, cursor="sb_v_double_arrow", ).grid(row=1, column=0) UI['style_can'].bind('<Configure>', flow_stylevar) # Scroll globally even if canvas is not selected. window.bind( "<MouseWheel>", lambda e: scroll(int(-1*(e.delta/120))), ) window.bind( "<Button-4>", lambda e: scroll(1), ) window.bind( "<Button-5>", lambda e: scroll(-1), )
def init_widgets(): """Initiallise all the window components.""" global prop_window prop_window = Toplevel(TK_ROOT) prop_window.overrideredirect(1) prop_window.resizable(False, False) prop_window.transient(master=TK_ROOT) prop_window.attributes('-topmost', 1) prop_window.withdraw() # starts hidden f = ttk.Frame(prop_window, relief="raised", borderwidth="4") f.grid(row=0, column=0) ttk.Label( f, text="Properties:", anchor="center", ).grid( row=0, column=0, columnspan=3, sticky="EW", ) wid['name'] = ttk.Label(f, text="", anchor="center") wid['name'].grid(row=1, column=0, columnspan=3, sticky="EW") wid['ent_count'] = ttk.Label( f, text="", anchor="e", compound="left", image=png.spr('gear_ent'), ) wid['ent_count'].grid(row=0, column=2, rowspan=2, sticky=E) tooltip.add_tooltip( wid['ent_count'], 'The number of entities used for this item. The Source engine limits ' 'this to 2048 in total. This provides a guide to how many of these ' 'items can be placed in a map at once.' ) wid['author'] = ttk.Label(f, text="", anchor="center", relief="sunken") wid['author'].grid(row=2, column=0, columnspan=3, sticky="EW") sub_frame = ttk.Frame(f, borderwidth=4, relief="sunken") sub_frame.grid(column=0, columnspan=3, row=3) for i in range(5): wid['subitem', i] = ttk.Label( sub_frame, image=png.png('BEE2/alpha_64'), ) wid['subitem', i].grid(row=0, column=i) utils.bind_leftclick( wid['subitem', i], functools.partial(sub_sel, i), ) utils.bind_rightclick( wid['subitem', i], functools.partial(sub_open, i), ) wid['wip_dep'] = ttk.Label(f, text='', anchor="nw") wid['wip_dep'].grid(row=4, column=0, sticky="NW") ttk.Label(f, text="Description:", anchor="sw").grid( row=4, column=0, sticky="SW", ) spr_frame = ttk.Frame(f, borderwidth=4, relief="sunken") spr_frame.grid(column=1, columnspan=2, row=4, sticky=W) # sprites: inputs, outputs, rotation handle, occupied/embed state, # desiredFacing for spr_id in SPR: wid['sprite', spr_id] = sprite = ttk.Label( spr_frame, image=png.spr('ap_grey'), relief="raised", ) sprite.grid(row=0, column=spr_id.value) tooltip.add_tooltip(sprite) desc_frame = ttk.Frame(f, borderwidth=4, relief="sunken") desc_frame.grid(row=5, column=0, columnspan=3, sticky="EW") desc_frame.columnconfigure(0, weight=1) wid['desc'] = tkRichText(desc_frame, width=40, height=8) wid['desc'].grid(row=0, column=0, sticky="EW") desc_scroll = tk_tools.HidingScroll( desc_frame, orient=VERTICAL, command=wid['desc'].yview, ) wid['desc']['yscrollcommand'] = desc_scroll.set desc_scroll.grid(row=0, column=1, sticky="NS") def show_more_info(): url = selected_item.url if url is not None: try: webbrowser.open(url, new=OPEN_IN_TAB, autoraise=True) except webbrowser.Error: if messagebox.askyesno( icon="error", title="BEE2 - Error", message='Failed to open a web browser. Do you wish for ' 'the URL to be copied to the clipboard ' 'instead?', detail='"{!s}"'.format(url), parent=prop_window ): LOGGER.info("Saving {} to clipboard!", url) TK_ROOT.clipboard_clear() TK_ROOT.clipboard_append(url) # Either the webbrowser or the messagebox could cause the # properties to move behind the main window, so hide it # so it doesn't appear there. hide_context(None) wid['moreinfo'] = ttk.Button(f, text="More Info>>", command=show_more_info) wid['moreinfo'].grid(row=6, column=2, sticky=E) tooltip.add_tooltip(wid['moreinfo']) menu_info = Menu(wid['moreinfo']) menu_info.add_command(label='', state='disabled') def show_item_props(): snd.fx('expand') itemPropWin.show_window( selected_item.get_properties(), wid['changedefaults'], selected_sub_item.name, ) wid['changedefaults'] = ttk.Button( f, text="Change Defaults...", command=show_item_props, ) wid['changedefaults'].grid(row=6, column=1) tooltip.add_tooltip( wid['changedefaults'], 'Change the default settings for this item when placed.' ) wid['variant'] = ttk.Combobox( f, values=['VERSION'], exportselection=0, # On Mac this defaults to being way too wide! width=7 if utils.MAC else None, ) wid['variant'].state(['readonly']) # Prevent directly typing in values wid['variant'].bind('<<ComboboxSelected>>', set_item_version) wid['variant'].current(0) wid['variant'].grid(row=6, column=0, sticky=W) itemPropWin.init(hide_item_props)
def make_pane(tool_frame): """Create the styleVar pane. """ global window window = SubPane( TK_ROOT, options=GEN_OPTS, title=_('Style Properties'), name='style', resize_y=True, tool_frame=tool_frame, tool_img=png.png('icons/win_stylevar'), tool_col=3, ) UI['style_can'] = Canvas(window, highlightthickness=0) # need to use a canvas to allow scrolling UI['style_can'].grid(sticky='NSEW') window.rowconfigure(0, weight=1) UI['style_scroll'] = ttk.Scrollbar( window, orient=VERTICAL, command=UI['style_can'].yview, ) UI['style_scroll'].grid(column=1, row=0, rowspan=2, sticky="NS") UI['style_can']['yscrollcommand'] = UI['style_scroll'].set utils.add_mousewheel(UI['style_can'], window) canvas_frame = ttk.Frame(UI['style_can']) frame_all = ttk.Labelframe(canvas_frame, text=_("All:")) frame_all.grid(row=0, sticky='EW') frm_chosen = ttk.Labelframe(canvas_frame, text=_("Selected Style:")) frm_chosen.grid(row=1, sticky='EW') ttk.Separator( canvas_frame, orient=HORIZONTAL, ).grid(row=2, sticky='EW', pady=(10, 5)) frm_other = ttk.Labelframe(canvas_frame, text=_("Other Styles:")) frm_other.grid(row=3, sticky='EW') UI['stylevar_chosen_none'] = ttk.Label( frm_chosen, text=_('No Options!'), font='TkMenuFont', justify='center', ) UI['stylevar_other_none'] = ttk.Label( frm_other, text=_('None!'), font='TkMenuFont', justify='center', ) all_pos = 0 for all_pos, var in enumerate(styleOptions): # Add the special stylevars which apply to all styles tk_vars[var.id] = IntVar( value=GEN_OPTS.get_bool('StyleVar', var.id, var.default)) checkbox_all[var.id] = ttk.Checkbutton(frame_all, variable=tk_vars[var.id], text=var.name, command=functools.partial( set_stylevar, var.id)) checkbox_all[var.id].grid(row=all_pos, column=0, sticky="W", padx=3) tooltip.add_tooltip( checkbox_all[var.id], make_desc(var, is_hardcoded=True), ) for var in VAR_LIST: tk_vars[var.id] = IntVar(value=var.enabled) args = { 'variable': tk_vars[var.id], 'text': var.name, 'command': functools.partial(set_stylevar, var.id) } desc = make_desc(var) if var.applies_to_all(): # Available in all styles - put with the hardcoded variables. all_pos += 1 checkbox_all[var.id] = check = ttk.Checkbutton(frame_all, **args) check.grid(row=all_pos, column=0, sticky="W", padx=3) tooltip.add_tooltip(check, desc) else: # Swap between checkboxes depending on style. checkbox_chosen[var.id] = ttk.Checkbutton(frm_chosen, **args) checkbox_other[var.id] = ttk.Checkbutton(frm_other, **args) tooltip.add_tooltip( checkbox_chosen[var.id], desc, ) tooltip.add_tooltip( checkbox_other[var.id], desc, ) UI['style_can'].create_window(0, 0, window=canvas_frame, anchor="nw") UI['style_can'].update_idletasks() UI['style_can'].config( scrollregion=UI['style_can'].bbox(ALL), width=canvas_frame.winfo_reqwidth(), ) if utils.USE_SIZEGRIP: ttk.Sizegrip( window, cursor=utils.CURSORS['stretch_vert'], ).grid(row=1, column=0) UI['style_can'].bind('<Configure>', flow_stylevar)
def make_pane(tool_frame: Frame, menu_bar: Menu): """Create the styleVar pane. """ global window window = SubPane( TK_ROOT, title=_('Style/Item Properties'), name='style', menu_bar=menu_bar, resize_y=True, tool_frame=tool_frame, tool_img=img.png('icons/win_stylevar'), tool_col=3, ) UI['nbook'] = nbook = ttk.Notebook(window) nbook.grid(row=0, column=0, sticky=NSEW) window.rowconfigure(0, weight=1) window.columnconfigure(0, weight=1) nbook.enable_traversal() stylevar_frame = ttk.Frame(nbook) stylevar_frame.rowconfigure(0, weight=1) stylevar_frame.columnconfigure(0, weight=1) nbook.add(stylevar_frame, text=_('Styles')) UI['style_can'] = Canvas(stylevar_frame, highlightthickness=0) # need to use a canvas to allow scrolling UI['style_can'].grid(sticky='NSEW') window.rowconfigure(0, weight=1) UI['style_scroll'] = ttk.Scrollbar( stylevar_frame, orient=VERTICAL, command=UI['style_can'].yview, ) UI['style_scroll'].grid(column=1, row=0, rowspan=2, sticky="NS") UI['style_can']['yscrollcommand'] = UI['style_scroll'].set utils.add_mousewheel(UI['style_can'], stylevar_frame) canvas_frame = ttk.Frame(UI['style_can']) frame_all = ttk.Labelframe(canvas_frame, text=_("All:")) frame_all.grid(row=0, sticky='EW') frm_chosen = ttk.Labelframe(canvas_frame, text=_("Selected Style:")) frm_chosen.grid(row=1, sticky='EW') ttk.Separator( canvas_frame, orient=HORIZONTAL, ).grid(row=2, sticky='EW', pady=(10, 5)) frm_other = ttk.Labelframe(canvas_frame, text=_("Other Styles:")) frm_other.grid(row=3, sticky='EW') UI['stylevar_chosen_none'] = ttk.Label( frm_chosen, text=_('No Options!'), font='TkMenuFont', justify='center', ) UI['stylevar_other_none'] = ttk.Label( frm_other, text=_('None!'), font='TkMenuFont', justify='center', ) all_pos = 0 for all_pos, var in enumerate(styleOptions): # Add the special stylevars which apply to all styles tk_vars[var.id] = int_var = IntVar(value=var.default) checkbox_all[var.id] = ttk.Checkbutton( frame_all, variable=int_var, text=var.name, ) checkbox_all[var.id].grid(row=all_pos, column=0, sticky="W", padx=3) # Special case - this needs to refresh the filter when swapping, # so the items disappear or reappear. if var.id == 'UnlockDefault': def cmd(): update_filter() checkbox_all[var.id]['command'] = cmd tooltip.add_tooltip( checkbox_all[var.id], make_desc(var, is_hardcoded=True), ) for var in VAR_LIST: tk_vars[var.id] = IntVar(value=var.enabled) args = { 'variable': tk_vars[var.id], 'text': var.name, } desc = make_desc(var) if var.applies_to_all(): # Available in all styles - put with the hardcoded variables. all_pos += 1 checkbox_all[var.id] = check = ttk.Checkbutton(frame_all, **args) check.grid(row=all_pos, column=0, sticky="W", padx=3) tooltip.add_tooltip(check, desc) else: # Swap between checkboxes depending on style. checkbox_chosen[var.id] = ttk.Checkbutton(frm_chosen, **args) checkbox_other[var.id] = ttk.Checkbutton(frm_other, **args) tooltip.add_tooltip( checkbox_chosen[var.id], desc, ) tooltip.add_tooltip( checkbox_other[var.id], desc, ) UI['style_can'].create_window(0, 0, window=canvas_frame, anchor="nw") UI['style_can'].update_idletasks() UI['style_can'].config( scrollregion=UI['style_can'].bbox(ALL), width=canvas_frame.winfo_reqwidth(), ) if utils.USE_SIZEGRIP: ttk.Sizegrip( window, cursor=utils.CURSORS['stretch_vert'], ).grid(row=1, column=0) UI['style_can'].bind('<Configure>', flow_stylevar) item_config_frame = ttk.Frame(nbook) nbook.add(item_config_frame, text=_('Items')) itemconfig.make_pane(item_config_frame)
def __init__(self, *stages): super().__init__(stages) self.stage_names = {} self.splash, width, height = img.get_splash_screen( max(self.winfo_screenwidth() * 0.6, 500), max(self.winfo_screenheight() * 0.6, 500), base_height=len(self.stages) * 20, ) self.height = height self.width = width self.canvas = canvas = Canvas( self, width=width, height=height, ) canvas.grid(row=0, column=0) # Splash screen... canvas.create_image( 0, 0, anchor='nw', image=self.splash, ) canvas.create_image( 10, 10, anchor='nw', image=img.png('BEE2/splash_logo'), ) self.disp_font = font = Font( family='Times', # Generic special case size=-18, # negative = in pixels weight='bold', ) canvas.create_text( 10, 125, anchor='nw', text=_('Better Extended Editor for Portal 2'), fill='white', font=font, ) canvas.create_text( 10, 145, anchor='nw', text=_('Version: ') + utils.BEE_VERSION, fill='white', font=font, ) for ind, (st_id, stage_name) in enumerate(reversed(self.stages), start=1): self.bar_val[st_id] = 0 self.maxes[st_id] = 10 self.stage_names[st_id] = stage_name canvas.create_rectangle( 20, height - (ind + 0.5) * 20, 20, height - (ind - 0.5) * 20, fill='#00785A', # 0, 120, 90 width=0, tags='bar_' + st_id, ) # Border canvas.create_rectangle( 20, height - (ind + 0.5) * 20, width - 20, height - (ind - 0.5) * 20, outline='#00785A', width=2, ) canvas.create_text( 25, height - ind * 20, anchor='w', text=stage_name + ': (0/???)', fill='white', tags='text_' + st_id, )
def run_screen( cmd_source, stages, # Pass in various bits of translated text # so we don't need to do it here. trans_title, trans_version, trans_skipped, ): """Runs in the other process, with an end of a pipe for input.""" window = tk.Tk() window.wm_overrideredirect(True) window.attributes('-topmost', 1) window['cursor'] = utils.CURSORS['wait'] stage_values = {} stage_maxes = {} stage_names = {} import img logo_img = img.png('BEE2/splash_logo') canvas = tk.Canvas(window) canvas.grid(row=0, column=0) canvas.create_image( 10, 10, anchor='nw', image=logo_img, ) font = Font( family='Times', # Generic special case size=-18, # negative = in pixels weight='bold', ) text1 = canvas.create_text( 10, 125, anchor='nw', text=trans_title, fill='white', font=font, ) text2 = canvas.create_text( 10, 145, anchor='nw', text=trans_version, fill='white', font=font, ) # Now add shadows behind the text, and draw to the canvas. splash, canvas['width'], canvas[ 'height'] = splash, width, height = img.make_splash_screen( max(window.winfo_screenwidth() * 0.6, 500), max(window.winfo_screenheight() * 0.6, 500), base_height=len(stages) * 20, text1_bbox=canvas.bbox(text1), text2_bbox=canvas.bbox(text2), ) canvas.tag_lower(canvas.create_image( 0, 0, anchor='nw', image=splash, )) canvas.splash_img = splash # Keep this alive for ind, (st_id, stage_name) in enumerate(reversed(stages), start=1): stage_values[st_id] = 0 stage_maxes[st_id] = 10 stage_names[st_id] = stage_name canvas.create_rectangle( 20, height - (ind + 0.5) * 20, 20, height - (ind - 0.5) * 20, fill='#00785A', # 0, 120, 90 width=0, tags='bar_' + st_id, ) # Border canvas.create_rectangle( 20, height - (ind + 0.5) * 20, width - 20, height - (ind - 0.5) * 20, outline='#00785A', width=2, ) canvas.create_text( 25, height - ind * 20, anchor='w', text=stage_name + ': (0/???)', fill='white', tags='text_' + st_id, ) def bar_length(stage, fraction): """Set a progress bar to this fractional length.""" x1, y1, x2, y2 = canvas.coords('bar_' + stage) canvas.coords( 'bar_' + stage, 20, y1, 20 + round(fraction * (width - 40)), y2, ) def set_nums(stage): canvas.itemconfig('text_' + stage, text='{}: ({}/{})'.format( stage_names[stage], stage_values[stage], stage_maxes[stage], )) bar_length(stage, stage_values[stage] / stage_maxes[stage]) def set_length(stage, num): """Set the number of items in a stage.""" stage_maxes[stage] = num set_nums(stage) canvas.delete('tick_' + stage) if num == 0: return # No ticks # Draw the ticks in... _, y1, _, y2 = canvas.coords('bar_' + stage) dist = (width - 40) / num if round(dist) <= 1: # Don't have ticks if they're right next to each other return tag = 'tick_' + stage for i in range(num): pos = int(20 + dist * i) canvas.create_line( pos, y1, pos, y2, fill='#00785A', tags=tag, ) canvas.tag_lower('tick_' + stage, 'bar_' + stage) def skip_stage(stage): """Skip over this stage of the loading process.""" stage_values[stage] = 0 stage_maxes[stage] = 0 canvas.itemconfig( 'text_' + stage, text=stage_names[stage] + ': ' + trans_skipped, ) bar_length(stage, 1) # Force stage to be max filled. canvas.delete('tick_' + stage) canvas.update() def check_queue(): """Update stages from the parent process.""" while cmd_source.poll(): # Pop off all the values. stage, operation, value = cmd_source.recv() if operation == 'kill': # Destroy everything window.destroy() # mainloop() will quit, this function will too, and # all our stuff will die. return elif operation == 'hide': window.withdraw() elif operation == 'show': window.deiconify() elif operation == 'value': stage_values[stage] = value set_nums(stage) elif operation == 'length': set_length(stage, value) elif operation == 'skip': skip_stage(stage) else: raise ValueError('Bad operation {!r}!'.format(operation)) # Continually re-run this function in the TK loop. window.after_idle(check_queue) # We have to implement dragging ourselves. x = y = None def move_start(event): """Record offset of mouse on click""" nonlocal x, y x = event.x y = event.y window['cursor'] = utils.CURSORS['move_item'] def move_stop(event): """Clear values when releasing.""" window['cursor'] = utils.CURSORS['wait'] nonlocal x, y x = y = None def move_motion(event): """Move the window when moving the mouse.""" if x is None or y is None: return window.geometry('+{x:g}+{y:g}'.format( x=window.winfo_x() + (event.x - x), y=window.winfo_y() + (event.y - y), )) window.bind('<Button-1>', move_start) window.bind('<ButtonRelease-1>', move_stop) window.bind('<B1-Motion>', move_motion) window.deiconify() window.lift() window.update() # Force an update so the reqwidth is correct window.geometry('+{x:g}+{y:g}'.format( x=(window.winfo_screenwidth() - window.winfo_reqwidth()) // 2, y=(window.winfo_screenheight() - window.winfo_reqheight()) // 2, )) window.after(10, check_queue) window.mainloop() # Infinite loop until we've quit here...
from tkinter import * # ui library from tkinter import font from tkinter import ttk # themed ui components that match the OS import functools import math import img # png library for TKinter from richTextBox import tkRichText import utils ICON_SIZE = 96 # Size of the selector win icons ITEM_WIDTH = ICON_SIZE+16 ITEM_HEIGHT = ICON_SIZE+51 # The larger error icon used if an image is not found err_icon = img.png('BEE2/error_96', resize_to=ICON_SIZE) def _NO_OP(*args): """The default callback, triggered whenever the chosen item is changed.""" pass class Item: """An item on the panel. - name: The item ID, used to distinguish it from others. - longName: The full item name. This can be very long. If not set, this will be the same as the short name. - shortName: A shortened version of the full name. This should be <= 20 characters.
def make_pane(tool_frame): """Create the compiler options pane. """ global window window = SubPane( TK_ROOT, options=GEN_OPTS, title='Compile Options', name='compiler', resize_x=False, resize_y=False, tool_frame=tool_frame, tool_img=png.png('icons/win_compiler'), tool_col=3, ) window.columnconfigure(0, weight=1) thumb_frame = ttk.LabelFrame( window, text='Thumbnail', labelanchor=N, ) thumb_frame.grid(row=0, column=0, sticky=EW) thumb_frame.columnconfigure(0, weight=1) UI['thumb_auto'] = ttk.Radiobutton( thumb_frame, text='Auto', value='AUTO', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_peti'] = ttk.Radiobutton( thumb_frame, text='PeTI', value='PETI', variable=chosen_thumb, command=set_screen_type, ) cust_frame = ttk.Frame(thumb_frame) cust_frame.columnconfigure(1, weight=1) UI['thumb_custom'] = ttk.Radiobutton( cust_frame, text='', value='CUST', variable=chosen_thumb, command=set_screen_type, ) UI['thumb_custom_file'] = ttk.Entry( cust_frame, cursor='arrow', textvariable=cust_file_loc_var, width=15, ) UI['thumb_custom_file'].bind("<Button-1>", find_screenshot) UI['thumb_custom_file'].bind("<Key>", set_screenshot_text) UI['thumb_custom_btn'] = ttk.Button( cust_frame, text="...", width=1.5, command=find_screenshot, ) UI['thumb_auto'].grid(row=0, column=0, sticky=W) UI['thumb_peti'].grid(row=0, column=1, sticky=W) cust_frame.grid(row=1, column=0, columnspan=2, sticky=EW) UI['thumb_custom'].grid(row=0, column=0, sticky=W) UI['thumb_custom_file'].grid(row=0, column=1, sticky=EW) UI['thumb_custom_btn'].grid(row=0, column=2, sticky=EW) elev_frame = ttk.LabelFrame( window, text='Spawn at:', labelanchor=N, ) elev_frame.grid(row=1, column=0, sticky=EW) UI['elev_preview'] = ttk.Radiobutton( elev_frame, text='Entry Door', value=0, variable=start_in_elev, command=set_elev_type, ) UI['elev_elevator'] = ttk.Radiobutton( elev_frame, text='Elevator', value=1, variable=start_in_elev, command=set_elev_type, ) UI['elev_preview'].grid(row=0, column=0, sticky=W) UI['elev_elevator'].grid(row=0, column=1, sticky=W) model_frame = ttk.LabelFrame( window, text='Player Model (SP):', labelanchor=N, ) model_frame.grid(row=2, column=0, sticky=EW) UI['player_mdl'] = ttk.Combobox( model_frame, exportselection=0, textvariable=player_model_var, values=PLAYER_MODEL_ORDER, ) # Users can only use the dropdown UI['player_mdl'].state(['readonly']) UI['player_mdl'].grid(row=0, column=0, sticky=EW) UI['player_mdl'].bind('<<ComboboxSelected>>', set_model) count_frame = ttk.LabelFrame( window, text='Last Compile:', labelanchor=N, ) count_frame.grid(row=3, column=0) ttk.Label( count_frame, text='Entity', anchor=N, ).grid(row=0, column=0, columnspan=3, sticky=EW) UI['count_ent'] = ttk.Progressbar( count_frame, maximum=MAX_ENTS, variable=count_ents, length=120, ) UI['count_ent'].grid( row=1, column=0, columnspan=3, sticky=EW, padx=5, ) ttk.Label( count_frame, text='Overlay', anchor=CENTER, ).grid(row=2, column=0, sticky=EW) UI['count_over'] = ttk.Progressbar( count_frame, maximum=MAX_OVERLAY, variable=count_overlay, length=50, ) UI['count_over'].grid(row=3, column=0, sticky=EW, padx=5) ttk.Button( count_frame, image=png.png('icons/tool_sub'), command=refresh_counts, ).grid(row=3, column=1) ttk.Label( count_frame, text='Brush', anchor=CENTER, ).grid(row=2, column=2, sticky=EW) UI['count_brush'] = ttk.Progressbar( count_frame, maximum=MAX_BRUSH, variable=count_brush, length=50, ) UI['count_brush'].grid(row=3, column=2, sticky=EW, padx=5) UI['view_logs'] = ttk.Button( count_frame, text='View Logs', ) UI['view_logs'].grid(row=4, column=0, columnspan=3, sticky=EW) refresh_counts(reload=False)
def make_pane(tool_frame): """Create the styleVar pane. """ global window window = SubPane( TK_ROOT, options=GEN_OPTS, title='Style Properties', name='style', resize_y=True, tool_frame=tool_frame, tool_img=png.png('icons/win_stylevar'), tool_col=3, ) UI['style_can'] = Canvas(window, highlightthickness=0) # need to use a canvas to allow scrolling UI['style_can'].grid(sticky='NSEW') window.rowconfigure(0, weight=1) UI['style_scroll'] = ttk.Scrollbar( window, orient=VERTICAL, command=UI['style_can'].yview, ) UI['style_scroll'].grid(column=1, row=0, rowspan=2, sticky="NS") UI['style_can']['yscrollcommand'] = UI['style_scroll'].set canvas_frame = ttk.Frame(UI['style_can']) frame_all = ttk.Labelframe(canvas_frame, text="All:") frame_all.grid(row=0, sticky='EW') frm_chosen = ttk.Labelframe(canvas_frame, text="Selected Style:") frm_chosen.grid(row=1, sticky='EW') ttk.Separator( canvas_frame, orient=HORIZONTAL, ).grid(row=2, sticky='EW', pady=(10, 5)) frm_other = ttk.Labelframe(canvas_frame, text="Other Styles:") frm_other.grid(row=3, sticky='EW') UI['stylevar_chosen_none'] = ttk.Label( frm_chosen, text='No Options!', font='TkMenuFont', justify='center', ) UI['stylevar_other_none'] = ttk.Label( frm_other, text='None!', font='TkMenuFont', justify='center', ) for pos, var in enumerate(styleOptions): # Add the special stylevars which apply to all styles tk_vars[var.id] = IntVar( value=GEN_OPTS.get_bool('StyleVar', var.id, var.enabled)) checkbox_special[var.id] = ttk.Checkbutton(frame_all, variable=tk_vars[var.id], text=var.name, command=functools.partial( set_stylevar, var.id)) checkbox_special[var.id].grid(row=pos, column=0, sticky="W", padx=3) if var.desc: tooltip.add_tooltip( checkbox_special[var.id], var.desc, ) for var in var_list: tk_vars[var.id] = IntVar(value=var.default) args = { 'variable': tk_vars[var.id], 'text': var.name, 'command': functools.partial(set_stylevar, var.id) } checkbox_chosen[var.id] = ttk.Checkbutton(frm_chosen, **args) checkbox_other[var.id] = ttk.Checkbutton(frm_other, **args) if var.desc: tooltip.add_tooltip( checkbox_chosen[var.id], var.desc, ) tooltip.add_tooltip( checkbox_other[var.id], var.desc, ) UI['style_can'].create_window(0, 0, window=canvas_frame, anchor="nw") UI['style_can'].update_idletasks() UI['style_can'].config( scrollregion=UI['style_can'].bbox(ALL), width=canvas_frame.winfo_reqwidth(), ) ttk.Sizegrip( window, cursor=utils.CURSORS['stretch_vert'], ).grid(row=1, column=0) UI['style_can'].bind('<Configure>', flow_stylevar) # Scroll globally even if canvas is not selected. if utils.WIN: window.bind( "<MouseWheel>", lambda e: scroll(int(-1 * (e.delta / 120))), ) elif utils.MAC: window.bind( "<Button-4>", lambda e: scroll(1), ) window.bind( "<Button-5>", lambda e: scroll(-1), )