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=img.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=img.invis_square(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), ) 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=img.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=16) 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 __init__( self, tk, lst, *, # Make all keyword-only for readability has_none=True, has_def=True, sound_sys: FileSystemChain = None, modal=False, # i18n: 'None' item description none_desc=_('Do not add anything.'), none_attrs=EmptyMapping, none_icon='BEE2/none_96.png', # i18n: 'None' item name. none_name=_("<None>"), title='BEE2', desc='', readonly_desc='', callback=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 snd_sample_sys is set, a '>' button will appear next to names to play the associated audio sample for the item. The value should be a FileSystem to look for samples in. - none_desc holds an optional description for the <none> Item, which can be used to describe what it results in. - none_icon allows changing the icon for the <none> Item. - none_name allows setting the name shown for the <none> Item. - 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 argument to the callback is always the selected item ID. - full_context controls if the short or long names are used for the context menu. - attributes is a list of AttrDef tuples. Each tuple should contain an ID, display text, and default value. If the values are True or False a check/cross will be displayed, otherwise they're a string. - desc is descriptive text to display on the window, and in the widget tooltip. - readonly_desc will be displayed on the widget tooltip when readonly. - modal: If True, the window will block others while open. """ self.noneItem = Item( name='<NONE>', short_name='', icon=none_icon, desc=none_desc, attributes=dict(none_attrs), ) # The textbox on the parent window. self.display = None # type: tk_tools.ReadOnlyEntry # Variable associated with self.display. self.disp_label = StringVar() # The '...' button to open our window. self.disp_btn = None # type: ttk.Button # ID of the currently chosen item self.chosen_id = None # Callback function, and positional arugments to pass if callback is not None: self.callback = callback self.callback_params = list(callback_params) else: self.callback = None self.callback_params = () # Item object for the currently suggested item. self.suggested = None # Should we have the 'reset to default' button? self.has_def = has_def self.description = desc self.readonly_description = readonly_desc if has_none: self.item_list = [self.noneItem] + lst else: self.item_list = lst try: self.selected = self.item_list[0] # type: Item except IndexError: LOGGER.error('No items for window "{}"!', title) # We crash without items, forcefully add the None item in so at # least this works. self.item_list = [self.noneItem] self.selected = self.noneItem self.orig_selected = self.selected self.parent = tk self._readonly = False self.modal = modal self.win = Toplevel(tk) self.win.withdraw() self.win.title("BEE2 - " + title) self.win.transient(master=tk) # Allow resizing in X and Y. self.win.resizable(True, True) tk_tools.set_window_icon(self.win) # Run our quit command when the exit button is pressed, or Escape # on the keyboard. self.win.protocol("WM_DELETE_WINDOW", self.exit) self.win.bind("<Escape>", self.exit) # Allow navigating with arrow keys. self.win.bind("<KeyPress>", self.key_navigate) # A map from group name -> header widget self.group_widgets = {} # A map from folded name -> display name self.group_names = {} self.grouped_items = defaultdict(list) # A list of folded group names in the display order. self.group_order = [] # The maximum number of items that fits per row (set in flow_items) self.item_width = 1 if desc: self.desc_label = ttk.Label( self.win, text=desc, justify=LEFT, anchor=W, width=5, # Keep a small width, so this doesn't affect the # initial window size. ) self.desc_label.grid(row=0, column=0, sticky='EW') # PanedWindow allows resizing the two areas independently. self.pane_win = PanedWindow( self.win, orient=HORIZONTAL, sashpad=2, # Padding above/below panes sashwidth=3, # Width of border sashrelief=RAISED, # Raise the border between panes ) self.pane_win.grid(row=1, column=0, sticky="NSEW") self.win.columnconfigure(0, weight=1) self.win.rowconfigure(1, weight=1) shim = ttk.Frame(self.pane_win, relief="sunken") shim.rowconfigure(0, weight=1) shim.columnconfigure(0, weight=1) # We need to use a canvas to allow scrolling. self.wid_canvas = Canvas(shim, highlightthickness=0) self.wid_canvas.grid(row=0, column=0, sticky="NSEW") # Add another frame inside to place labels on. self.pal_frame = ttk.Frame(self.wid_canvas) self.wid_canvas.create_window(1, 1, window=self.pal_frame, anchor="nw") self.wid_scroll = tk_tools.HidingScroll( shim, orient=VERTICAL, command=self.wid_canvas.yview, ) self.wid_scroll.grid(row=0, column=1, sticky="NS") self.wid_canvas['yscrollcommand'] = self.wid_scroll.set utils.add_mousewheel(self.wid_canvas, self.win) if utils.MAC: # Labelframe doesn't look good here on OSX self.sugg_lbl = ttk.Label( self.pal_frame, # Draw lines with box drawing characters text="\u250E\u2500" + _("Suggested") + "\u2500\u2512", ) else: self.sugg_lbl = ttk.LabelFrame( self.pal_frame, text=_("Suggested"), labelanchor=N, height=50, ) # Holds all the widgets which provide info for the current item. self.prop_frm = ttk.Frame(self.pane_win, borderwidth=4, relief='raised') self.prop_frm.columnconfigure(1, weight=1) # Border around the selected item icon. width, height = img.tuple_size(ICON_SIZE_LRG) self.prop_icon_frm = ttk.Frame( self.prop_frm, borderwidth=4, relief='raised', width=width, height=height, ) self.prop_icon_frm.grid(row=0, column=0, columnspan=4) self.prop_icon = ttk.Label( self.prop_icon_frm, image=img.color_square(img.PETI_ITEM_BG, ICON_SIZE_LRG), ) self.prop_icon.grid(row=0, column=0) name_frame = ttk.Frame(self.prop_frm) self.prop_name = ttk.Label( name_frame, text="Item", justify=CENTER, 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 sound_sys is not None 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, system=sound_sys, ) 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 # If we have a sound sampler, hold the system open while the window # is so it doesn't snap open/closed while finding files. self.sampler_held_open = False self.prop_author = ttk.Label(self.prop_frm, text="Author") self.prop_author.grid(row=2, column=0, columnspan=4) self.prop_desc_frm = ttk.Frame(self.prop_frm, relief="sunken") self.prop_desc_frm.grid(row=4, column=0, columnspan=4, sticky="NSEW") self.prop_desc_frm.rowconfigure(0, weight=1) self.prop_desc_frm.columnconfigure(0, weight=1) self.prop_frm.rowconfigure(4, weight=1) self.prop_desc = tkRichText( self.prop_desc_frm, width=40, height=4, font="TkSmallCaptionFont", ) self.prop_desc.grid( row=0, column=0, padx=(2, 0), pady=2, sticky='NSEW', ) self.prop_scroll = tk_tools.HidingScroll( self.prop_desc_frm, orient=VERTICAL, command=self.prop_desc.yview, ) self.prop_scroll.grid( row=0, column=1, sticky="NS", padx=(0, 2), pady=2, ) self.prop_desc['yscrollcommand'] = self.prop_scroll.set ttk.Button( self.prop_frm, text=_("OK"), command=self.save, ).grid( row=6, column=0, padx=(8, 8), ) if self.has_def: self.prop_reset = ttk.Button( self.prop_frm, text=_("Reset to Default"), command=self.sel_suggested, ) self.prop_reset.grid( row=6, column=1, sticky='EW', ) ttk.Button( self.prop_frm, text=_("Cancel"), command=self.exit, ).grid( row=6, column=2, padx=(8, 8), ) self.win.option_add('*tearOff', False) self.context_menu = Menu(self.win) self.norm_font = tk_font.nametofont('TkMenuFont') # Make a font for showing suggested items in the context menu self.sugg_font = self.norm_font.copy() self.sugg_font['weight'] = tk_font.BOLD # Make a font for previewing the suggested item self.mouseover_font = self.norm_font.copy() self.mouseover_font['slant'] = tk_font.ITALIC self.context_var = IntVar() # The headers for the context menu self.context_menus = {} # Sort alphabetically, preferring a sort key if present. self.item_list.sort(key=lambda it: it.sort_key or it.longName) for ind, item in enumerate(self.item_list): item._selector = self if item == self.noneItem: item.button = ttk.Button( self.pal_frame, image=item.icon, ) item.context_lbl = none_name 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_menus[''] = 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._context_ind = len(self.grouped_items[group_key]) - 1 @utils.bind_leftclick(item.button) def click_item(event=None, *, _item=item): """Handle clicking on the item. If it's already selected, save and close the window. """ # We need to capture the item in a default, since it's # the same variable in different iterations if _item is self.selected: self.save() else: self.sel_item(_item) # Convert to a normal dictionary, after adding all items. self.grouped_items = dict(self.grouped_items) # Figure out the order for the groups - alphabetical. # Note - empty string should sort to the beginning! self.group_order[:] = sorted(self.grouped_items.keys()) for index, (key, menu) in enumerate( sorted(self.context_menus.items(), key=itemgetter(0)), # We start with the ungrouped items, so increase the index # appropriately. start=len(self.grouped_items.get('', ()))): if key == '': # Don't add the ungrouped menu to itself! continue 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._context_index = index for group_key, text in self.group_names.items(): self.group_widgets[group_key] = GroupHeader( self, text, ) 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 = self.desc_label = None self.flow_items() self.wid_canvas.bind("<Configure>", self.flow_items)
def init_widgets(): """Make all the window components.""" win.columnconfigure(0, weight=1) win.transient(master=TK_ROOT) tk_tools.set_window_icon(win) win.protocol("WM_DELETE_WINDOW", win.withdraw) win.bind("<Escape>", lambda event: win.withdraw()) pane = PanedWindow( win, orient=VERTICAL, sashpad=2, # Padding above/below panes sashwidth=3, # Width of border sashrelief=RAISED, # Raise the border between panes ) UI['pane'] = pane pane.grid(row=1, column=0, sticky='NSEW') win.rowconfigure(1, weight=1) UI['tabs'] = ttk.Notebook(pane) UI['tabs'].enable_traversal() # Add keyboard shortcuts pane.add(UI['tabs']) pane.paneconfigure(UI['tabs'], minsize=50) trans_frame = ttk.Frame(pane) trans_frame.rowconfigure(1, weight=1) trans_frame.columnconfigure(0, weight=1) ttk.Label( trans_frame, text=_('Transcript:'), ).grid( row=0, column=0, sticky=W, ) trans_inner_frame = ttk.Frame(trans_frame, borderwidth=2, relief='sunken') trans_inner_frame.grid(row=1, column=0, sticky='NSEW') trans_inner_frame.rowconfigure(0, weight=1) trans_inner_frame.columnconfigure(0, weight=1) default_bold_font = font.nametofont('TkDefaultFont').copy() default_bold_font['weight'] = 'bold' UI['trans'] = Text( trans_inner_frame, width=10, height=4, wrap='word', relief='flat', state='disabled', font='TkDefaultFont', ) UI['trans_scroll'] = tk_tools.HidingScroll( trans_inner_frame, orient=VERTICAL, command=UI['trans'].yview, ) UI['trans'].tag_config( 'bold', font=default_bold_font, ) UI['trans']['yscrollcommand'] = UI['trans_scroll'].set UI['trans_scroll'].grid(row=0, column=1, sticky='NS') UI['trans'].grid(row=0, column=0, sticky='NSEW') ttk.Button( win, text=_('Save'), command=save, ).grid(row=2, column=0) # Don't allow resizing the transcript box to be smaller than the # original size. trans_frame.update_idletasks() pane.paneconfigure(trans_frame, minsize=trans_frame.winfo_reqheight())
def make_tab(group, config: ConfigFile, tab_type): """Create all the widgets for a tab.""" if tab_type is TabTypes.MIDCHAMBER: # Mid-chamber voice lines have predefined values. group_name = _('Mid - Chamber') group_id = 'MIDCHAMBER' group_desc = _('Lines played during the actual chamber, ' 'after specific events have occurred.') elif tab_type is TabTypes.RESPONSE: # Note: 'Response' tab header, and description group_name = _('Responses') group_id = None group_desc = _('Lines played in response to certain events in Coop.') elif tab_type is TabTypes.NORM: group_name = group['name', 'No Name!'] group_id = group_name.upper() group_desc = group['desc', ''] + ':' else: raise ValueError('Invalid tab type!') # This is just to hold the canvas and scrollbar outer_frame = ttk.Frame(UI['tabs']) outer_frame.columnconfigure(0, weight=1) outer_frame.rowconfigure(0, weight=1) TABS[group_name] = outer_frame # We add this attribute so the refresh() method knows all the # tab names outer_frame.nb_text = group_name outer_frame.nb_type = tab_type # We need a canvas to make the list scrollable. canv = Canvas( outer_frame, highlightthickness=0, ) scroll = tk_tools.HidingScroll( outer_frame, orient=VERTICAL, command=canv.yview, ) canv['yscrollcommand'] = scroll.set canv.grid(row=0, column=0, sticky='NSEW') scroll.grid(row=0, column=1, sticky='NS') UI['tabs'].add(outer_frame) # This holds the actual elements frame = ttk.Frame(canv, ) frame.columnconfigure(0, weight=1) canv.create_window(0, 0, window=frame, anchor="nw") # We do this so we can adjust the scrollregion later in # <Configure>. canv.frame = frame ttk.Label( frame, text=group_name, anchor='center', font='tkHeadingFont', ).grid( row=0, column=0, sticky='EW', ) ttk.Label( frame, text=group_desc, ).grid( row=1, column=0, sticky='EW', ) ttk.Separator(frame, orient=HORIZONTAL).grid( row=2, column=0, sticky='EW', ) if tab_type is TabTypes.RESPONSE: sorted_quotes = sorted(group, key=lambda prop: prop.real_name) else: sorted_quotes = sorted( group.find_all('Quote'), key=quote_sort_func, reverse=True, ) for quote in sorted_quotes: # type: Property if not quote.has_children(): continue # Skip over config commands.. if tab_type is TabTypes.RESPONSE: try: name = RESPONSE_NAMES[quote.name] except KeyError: # Convert channels of the form 'death_goo' into 'Death - Goo'. channel, ch_arg = quote.name.split('_', 1) name = channel.title() + ' - ' + ch_arg.title() del channel, ch_arg group_id = quote.name else: # note: default for quote names name = quote['name', _('No Name!')] ttk.Label( frame, text=name, font=QUOTE_FONT, ).grid( column=0, sticky=W, ) if tab_type is TabTypes.RESPONSE: line_iter = find_resp_lines(quote) else: line_iter = find_lines(quote) for badge, line, line_id in line_iter: if line_id is None: line_id = line['id', line['name']] check = ttk.Checkbutton( frame, # note: default voice line name next to checkbox. text=line['name', _('No Name?')], compound=LEFT, image=badge, ) check.quote_var = IntVar(value=config.get_bool( group_id, line_id, True), ) check['variable'] = check.quote_var check['command'] = functools.partial( check_toggled, var=check.quote_var, config_section=config[group_id], quote_id=line_id, ) check.transcript = list(get_trans_lines(line)) check.grid( column=0, padx=(10, 0), sticky=W, ) check.bind("<Enter>", show_trans) canv.bind('<Configure>', configure_canv) return outer_frame
def init_widgets(): pane = PanedWindow( win, orient=VERTICAL, sashpad=2, # Padding above/below panes sashwidth=3, # Width of border sashrelief=RAISED, # Raise the border between panes ) UI['pane'] = pane pane.grid(row=1, column=0, sticky='NSEW') win.rowconfigure(1, weight=1) UI['tabs'] = ttk.Notebook(pane) UI['tabs'].enable_traversal() # Add keyboard shortcuts pane.add(UI['tabs']) pane.paneconfigure(UI['tabs'], minsize=50) trans_frame = ttk.Frame(pane) trans_frame.rowconfigure(1, weight=1) trans_frame.columnconfigure(0, weight=1) ttk.Label( trans_frame, text=_('Transcript:'), ).grid( row=0, column=0, sticky=W, ) trans_inner_frame = ttk.Frame(trans_frame, borderwidth=2, relief='sunken') trans_inner_frame.grid(row=1, column=0, sticky='NSEW') trans_inner_frame.rowconfigure(0, weight=1) trans_inner_frame.columnconfigure(0, weight=1) UI['trans'] = Text( trans_inner_frame, width=10, height=4, wrap='word', relief='flat', state='disabled', font=('Helvectia', 10), ) UI['trans_scroll'] = tk_tools.HidingScroll( trans_inner_frame, orient=VERTICAL, command=UI['trans'].yview, ) UI['trans'].tag_config( 'bold', font=('Helvectia', 10, 'bold'), ) UI['trans']['yscrollcommand'] = UI['trans_scroll'].set UI['trans_scroll'].grid(row=0, column=1, sticky='NS') UI['trans'].grid(row=0, column=0, sticky='NSEW') ttk.Button( win, text='Save', command=save, ).grid(row=2, column=0) # Don't allow resizing the transcript box to be smaller than the # original size. trans_frame.update_idletasks() pane.paneconfigure(trans_frame, minsize=trans_frame.winfo_reqheight())
def init(start_open, log_level='info'): """Initialise the window.""" global window, log_handler, text_box, level_selector window = tk.Toplevel(TK_ROOT, name='console_win') window.withdraw() window.columnconfigure(0, weight=1) window.rowconfigure(0, weight=1) window.title(_('Logs - {}').format(utils.BEE_VERSION)) window.protocol('WM_DELETE_WINDOW', lambda: set_visible(False)) text_box = tk.Text( window, name='text_box', width=50, height=15, ) text_box.grid(row=0, column=0, sticky='NSEW') log_level = logging.getLevelName(log_level.upper()) log_handler = TextHandler(text_box) try: log_handler.setLevel(log_level) except ValueError: log_level = logging.INFO log_handler.setFormatter(utils.short_log_format) logging.getLogger('BEE2').addHandler(log_handler) scroll = tk_tools.HidingScroll( window, name='scroll', orient=tk.VERTICAL, command=text_box.yview, ) scroll.grid(row=0, column=1, sticky='NS') text_box['yscrollcommand'] = scroll.set button_frame = ttk.Frame(window, name='button_frame') button_frame.grid(row=1, column=0, columnspan=2, sticky='EW') ttk.Button( button_frame, name='clear_btn', text='Clear', command=btn_clear, ).grid(row=0, column=0) ttk.Button( button_frame, name='copy_btn', text=_('Copy'), command=btn_copy, ).grid(row=0, column=1) sel_frame = ttk.Frame(button_frame, ) sel_frame.grid(row=0, column=2, sticky='EW') button_frame.columnconfigure(2, weight=1) ttk.Label( sel_frame, text=_('Show:'), anchor='e', justify='right', ).grid(row=0, column=0, sticky='E') level_selector = ttk.Combobox( sel_frame, name='level_selector', values=[LVL_TEXT[level] for level in BOX_LEVELS], exportselection=0, # On Mac this defaults to being way too wide! width=15 if utils.MAC else None, ) level_selector.state(['readonly']) # Prevent directly typing in values level_selector.bind('<<ComboboxSelected>>', set_level) level_selector.current(BOX_LEVELS.index(log_level)) level_selector.grid(row=0, column=1, sticky='E') sel_frame.columnconfigure(1, weight=1) utils.add_mousewheel(text_box, window, sel_frame, button_frame) if utils.USE_SIZEGRIP: ttk.Sizegrip(button_frame).grid(row=0, column=3) if start_open: window.deiconify() window.lift() # Force an update, we're busy with package extraction... window.update() else: window.withdraw()
def init(frm): """Initialise the UI objects.""" frm.bind('<Enter>', expand) frm.bind('<Leave>', contract) wid['tag_mode_any'] = widget = ttk.Radiobutton( frm, text='Any', variable=TAG_MODE, value='ANY', command=filter_items, ) widget.grid(row=0, column=0, sticky='W') wid['tag_mode_all'] = widget = ttk.Radiobutton( frm, text='All', variable=TAG_MODE, value='ALL', command=filter_items, ) widget.grid(row=1, column=0, sticky='W') wid['cur_tags'] = cur_tags = tk.Text( frm, font='TkDefaultFont', width=10, height=2, cursor=utils.CURSORS['regular'], ) cur_tags.grid(row=0, rowspan=2, column=1, sticky='EW') wid['expand_frame'] = exp = ttk.Frame(frm, ) # Resize to fit the expansion frame frm.columnconfigure(1, weight=1) frm.rowconfigure(2, weight=1) ttk.Label( exp, text='Available Tags (click):', ).grid(row=0, column=0, columnspan=2) # Make the tag section the dynamically-resizing portion exp.columnconfigure(0, weight=1) exp.rowconfigure(0, weight=1) wid['tag_list'] = tag_list = tk.Text( exp, font='TkDefaultFont', width=10, height=8, cursor=utils.CURSORS['regular'], ) tag_list.grid(row=1, column=0, sticky='NSEW') wid['tag_scroll'] = tag_scroll = tk_tools.HidingScroll( exp, orient=tk.VERTICAL, command=tag_list.yview, ) tag_scroll.grid(row=1, column=1, sticky='NS') tag_list['yscrollcommand'] = tag_scroll.set tag_list['state'] = "disabled" # Prevent users from editing the text def event_hover_tag(text: tk.Text, e): """When hovering over a tag, change the cursor.""" text['cursor'] = utils.CURSORS['link'] def event_unhover_tag(text: tk.Text, e): text['cursor'] = utils.CURSORS['regular'] for text in (tag_list, cur_tags): text.tag_config( 'highlight', underline=1, ) # We can't change the cursor property for individual tags, so # change it globally when the cursor enters and leaves. text.tag_bind('highlight', '<Enter>', partial(event_hover_tag, text)) text.tag_bind('highlight', '<Leave>', partial(event_unhover_tag, text)) tag_list.tag_config( 'header', font=BOLD_FONT, ) refresh_tags()