def __init__(self, title: str, text: str): super().__init__(TK_ROOT) self.withdraw() self.title(title) self.transient(master=TK_ROOT) self.resizable(width=True, height=True) self.text = text tk_tools.set_window_icon(self) # Hide when the exit button is pressed, or Escape # on the keyboard. self.protocol("WM_DELETE_WINDOW", self.withdraw) self.bind("<Escape>", self.withdraw) frame = tk.Frame(self, background='white') frame.grid(row=0, column=0, sticky='nsew') self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.textbox = tkRichText(frame, width=80, height=24) self.textbox.configure(background='white', relief='flat') self.textbox.grid(row=0, column=0, sticky='nsew') frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) scrollbox = tk_tools.HidingScroll( frame, orient='vertical', command=self.textbox.yview, ) scrollbox.grid(row=0, column=1, sticky='ns') self.textbox['yscrollcommand'] = scrollbox.set ttk.Button( frame, text=gettext('Close'), command=self.withdraw, ).grid( row=1, column=0, )
def __init__( self, f: tk.Frame, menu: tk.Menu, *, cmd_clear: Callable[[], None], cmd_shuffle: Callable[[], None], get_items: Callable[[], list[tuple[str, int]]], set_items: Callable[[Palette], None], ) -> None: """Initialises the palette pane. The parameters are used to communicate with the item list: - cmd_clear and cmd_shuffle are called to do those actions to the list. - pal_get_items is called to retrieve the current list of selected items. - cmd_save_btn_state is the .state() method on the save button. - cmd_set_items is called to apply a palette to the list of items. """ self.palettes: dict[UUID, Palette] = { pal.uuid: pal for pal in paletteLoader.load_palettes() } try: self.selected_uuid = UUID(hex=BEE2_config.GEN_OPTS.get_val( 'Last_Selected', 'palette_uuid', '')) except ValueError: self.selected_uuid = UUID_PORTAL2 f.rowconfigure(1, weight=1) f.columnconfigure(0, weight=1) self.var_save_settings = tk.BooleanVar( value=BEE2_config.GEN_OPTS.get_bool('General', 'palette_save_settings')) self.var_pal_select = tk.StringVar(value=self.selected_uuid.hex) self.get_items = get_items self.set_items = set_items # Overwritten to configure the save state button. self.save_btn_state = lambda s: None ttk.Button( f, text=gettext('Clear Palette'), command=cmd_clear, ).grid(row=0, sticky="EW") self.ui_treeview = treeview = ttk.Treeview(f, show='tree', selectmode='browse') self.ui_treeview.grid(row=1, sticky="NSEW") self.ui_treeview.tag_bind(TREE_TAG_PALETTES, '<ButtonPress>', self.event_select_tree) # Avoid re-registering the double-lambda, just do it here. # This makes clicking the groups return selection to the palette. evtid_reselect = self.ui_treeview.register(self.treeview_reselect) self.ui_treeview.tag_bind( TREE_TAG_GROUPS, '<ButtonPress>', lambda e: treeview.tk.call('after', 'idle', evtid_reselect)) # And ensure when focus returns we reselect, in case it deselects. f.winfo_toplevel().bind('<FocusIn>', lambda e: self.treeview_reselect(), add=True) scrollbar = tk_tools.HidingScroll( f, orient='vertical', command=self.ui_treeview.yview, ) scrollbar.grid(row=1, column=1, sticky="NS") self.ui_treeview['yscrollcommand'] = scrollbar.set self.ui_remove = ttk.Button( f, text=gettext('Delete Palette'), command=self.event_remove, ) self.ui_remove.grid(row=2, sticky="EW") if tk_tools.USE_SIZEGRIP: ttk.Sizegrip(f).grid(row=2, column=1) self.ui_menu = menu self.ui_group_menus: dict[str, tk.Menu] = {} self.ui_group_treeids: dict[str, str] = {} menu.add_command( label=gettext('Clear'), command=cmd_clear, ) menu.add_command( # Placeholder.. label=gettext('Delete Palette'), # This name is overwritten later command=self.event_remove, ) self.ui_menu_delete_index = menu.index('end') menu.add_command( label=gettext('Change Palette Group...'), command=self.event_change_group, ) self.ui_menu_regroup_index = menu.index('end') menu.add_command( label=gettext('Rename Palette...'), command=self.event_rename, ) self.ui_menu_rename_index = menu.index('end') menu.add_command( label=gettext('Fill Palette'), command=cmd_shuffle, ) menu.add_separator() menu.add_checkbutton( label=gettext('Save Settings in Palettes'), variable=self.var_save_settings, ) menu.add_separator() menu.add_command( label=gettext('Save Palette'), command=self.event_save, accelerator=tk_tools.ACCEL_SAVE, ) self.ui_menu_save_ind = menu.index('end') menu.add_command( label=gettext('Save Palette As...'), command=self.event_save_as, accelerator=tk_tools.ACCEL_SAVE_AS, ) menu.add_separator() self.ui_menu_palettes_index = menu.index('end') + 1 self.update_state()
def init(start_open: bool, log_level: str = 'info') -> None: """Initialise the window.""" global log_handler, text_box, level_selector 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( logging.Formatter( # One letter for level name '[{levelname[0]}] {module}.{funcName}(): {message}', style='{', )) logging.getLogger().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_widgets(master: ttk.Frame) -> Optional[tk.Widget]: """Construct the widgets, returning the configuration button. If no signages are defined, this returns None. """ if not any(Signage.all()): return ttk.Label(master) window.resizable(True, True) window.title(gettext('Configure Signage')) frame_selected = ttk.Labelframe( window, text=gettext('Selected'), relief='raised', labelanchor='n', ) canv_all = tk.Canvas(window) scroll = tk_tools.HidingScroll(window, orient='vertical', command=canv_all.yview) canv_all['yscrollcommand'] = scroll.set name_label = ttk.Label(window, text='', justify='center') frame_preview = ttk.Frame(window, relief='raised', borderwidth=4) frame_selected.grid(row=0, column=0, sticky='nsew') ttk.Separator(orient='horizontal').grid(row=1, column=0, sticky='ew') name_label.grid(row=2, column=0) frame_preview.grid(row=3, column=0, pady=4) canv_all.grid(row=0, column=1, rowspan=4, sticky='nsew') scroll.grid(row=0, column=2, rowspan=4, sticky='ns') window.columnconfigure(1, weight=1) window.rowconfigure(3, weight=1) tk_tools.add_mousewheel(canv_all, canv_all, window) preview_left = ttk.Label(frame_preview, anchor='e') preview_right = ttk.Label(frame_preview, anchor='w') img.apply(preview_left, IMG_BLANK) img.apply(preview_right, IMG_BLANK) preview_left.grid(row=0, column=0) preview_right.grid(row=0, column=1) try: sign_arrow = Signage.by_id('SIGN_ARROW') except KeyError: LOGGER.warning('No arrow signage defined!') sign_arrow = None hover_arrow = False hover_toggle_id = None hover_sign: Optional[Signage] = None def hover_toggle() -> None: """Toggle between arrows and dual icons.""" nonlocal hover_arrow, hover_toggle_id hover_arrow = not hover_arrow if hover_sign is None: return if hover_arrow and sign_arrow: left = hover_sign.dnd_icon right = sign_arrow.dnd_icon else: try: left = Signage.by_id(hover_sign.prim_id or '').dnd_icon except KeyError: left = hover_sign.dnd_icon try: right = Signage.by_id(hover_sign.sec_id or '').dnd_icon except KeyError: right = IMG_BLANK img.apply(preview_left, left) img.apply(preview_right, right) hover_toggle_id = TK_ROOT.after(1000, hover_toggle) def on_hover(slot: dragdrop.Slot[Signage]) -> None: """Show the signage when hovered.""" nonlocal hover_arrow, hover_sign if slot.contents is not None: name_label['text'] = gettext('Signage: {}').format(slot.contents.name) hover_sign = slot.contents hover_arrow = True hover_toggle() else: on_leave(slot) def on_leave(slot: dragdrop.Slot[Signage]) -> None: """Reset the visible sign when left.""" nonlocal hover_toggle_id, hover_sign name_label['text'] = '' hover_sign = None if hover_toggle_id is not None: TK_ROOT.after_cancel(hover_toggle_id) hover_toggle_id = None img.apply(preview_left, IMG_BLANK) img.apply(preview_right, IMG_BLANK) drag_man.reg_callback(dragdrop.Event.HOVER_ENTER, on_hover) drag_man.reg_callback(dragdrop.Event.HOVER_EXIT, on_leave) for i in range(3, 31): SLOTS_SELECTED[i] = slot = drag_man.slot( frame_selected, source=False, label=f'00:{i:02g}' ) row, col = divmod(i-3, 4) slot.grid(row=row, column=col, padx=1, pady=1) prev_id = DEFAULT_IDS.get(i, '') if prev_id: try: slot.contents = Signage.by_id(prev_id) except KeyError: LOGGER.warning('Missing sign id: {}', prev_id) for sign in sorted(Signage.all(), key=lambda s: s.name): if not sign.hidden: slot = drag_man.slot(canv_all, source=True) slot.contents = sign drag_man.flow_slots(canv_all, drag_man.sources()) canv_all.bind( '<Configure>', lambda e: drag_man.flow_slots(canv_all, drag_man.sources()), ) def hide_window() -> None: """Hide the window.""" window.withdraw() drag_man.unload_icons() img.apply(preview_left, IMG_BLANK) img.apply(preview_right, IMG_BLANK) def show_window() -> None: """Show the window.""" drag_man.load_icons() window.deiconify() utils.center_win(window, TK_ROOT) window.protocol("WM_DELETE_WINDOW", hide_window) return ttk.Button( master, text=gettext('Configure Signage'), command=show_window, )
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['item_id'] = ttk.Label(f, text="", anchor="center") wid['item_id'].grid(row=2, column=0, columnspan=3, sticky="EW") tooltip.add_tooltip(wid['item_id']) 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=3, column=0, columnspan=3, sticky="EW") sub_frame = ttk.Frame(f, borderwidth=4, relief="sunken") sub_frame.grid(column=0, columnspan=3, row=4) 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=5, 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=6, 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=7, 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(): sound.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=7, 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=7, column=0, sticky=W) itemPropWin.init(hide_item_props)
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", quit) win.bind("<Escape>", quit) 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") 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 badges, line, line_id in line_iter: line_frame = ttk.Frame(frame, ) line_frame.grid( column=0, padx=(10, 0), sticky=W, ) for x, (img, ctx) in enumerate(badges): label = ttk.Label(line_frame, image=img, padding=0) label.grid(row=0, column=x) add_tooltip(label, ctx) line_frame.columnconfigure(len(badges), weight=1) check = ttk.Checkbutton( line_frame, # note: default voice line name next to checkbox. text=line['name', _('No Name?')], ) 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( row=0, column=len(badges), ) check.bind("<Enter>", show_trans) def configure_canv(e): """Allow resizing the windows.""" canv['scrollregion'] = ( 4, 0, canv.winfo_reqwidth(), frame.winfo_reqheight(), ) frame['width'] = canv.winfo_reqwidth() canv.bind('<Configure>', configure_canv) return outer_frame
def __init__(self, translations: dict, pipe: multiprocessing.connection.Connection) -> None: """Initialise the window.""" self.win = window = tk.Toplevel(TK_ROOT) self.pipe = pipe window.columnconfigure(0, weight=1) window.rowconfigure(0, weight=1) window.title(translations['log_title']) window.protocol('WM_DELETE_WINDOW', self.evt_close) window.withdraw() self.has_text = False self.text = tk.Text( window, name='text_box', width=50, height=15, ) self.text.grid(row=0, column=0, sticky='NSEW') scroll = tk_tools.HidingScroll( window, name='scroll', orient=tk.VERTICAL, command=self.text.yview, ) scroll.grid(row=0, column=1, sticky='NS') self.text['yscrollcommand'] = scroll.set # Assign colours for each logging level for level, colour in LVL_COLOURS.items(): self.text.tag_config( logging.getLevelName(level), foreground=colour, # For multi-line messages, indent this much. lmargin2=30, ) self.text.tag_config( logging.getLevelName(logging.CRITICAL), background='red', ) # If multi-line messages contain carriage returns, lmargin2 doesn't # work. Add an additional tag for that. self.text.tag_config( 'INDENT', lmargin1=30, lmargin2=30, ) 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=translations['clear'], command=self.evt_clear, ).grid(row=0, column=0) ttk.Button( button_frame, name='copy_btn', text=translations['copy'], command=self.evt_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=translations['log_show'], anchor='e', justify='right', ).grid(row=0, column=0, sticky='E') self.level_selector = ttk.Combobox( sel_frame, name='level_selector', values=translations['level_text'], exportselection=False, # On Mac this defaults to being way too wide! width=15 if utils.MAC else None, ) self.level_selector.state(['readonly' ]) # Prevent directly typing in values self.level_selector.bind('<<ComboboxSelected>>', self.evt_set_level) self.level_selector.current(1) self.level_selector.grid(row=0, column=1, sticky='E') sel_frame.columnconfigure(1, weight=1) tk_tools.add_mousewheel(self.text, window, sel_frame, button_frame) if tk_tools.USE_SIZEGRIP: ttk.Sizegrip(button_frame).grid(row=0, column=3)