def _visibility_trace(self, *args): visible = self.visible.get() if visible: self.show() else: self.hide() CONFIG.set('Code structure', 'visible', str(visible)) CONFIG.save()
def show(self): layout = CONFIG.get("General", "layout") if layout in ["vertical", "horizontal2"]: self.manager.add(self, text=self.name) self.manager.select(self) else: self.manager.insert(0, self, weight=1) w = self.master.winfo_width() pos = int(float(CONFIG.get("Layout", layout).split()[0]) * w) self.master.sashpos(0, pos)
def update_style(self): with open(CSS_PATH.format(theme=CONFIG.get('General', 'theme'))) as f: self.stylesheet = f.read() try: self.html.set_style(self.stylesheet) except tk.TclError: pass
def edit_filter(self): def ok(event=None): CONFIG.set('File browser', 'filename_filter', entry.get()) CONFIG.save() self.load_filters() self.populate(self.filetree.get_children()[0], history=False) top.destroy() top = tk.Toplevel(self, padx=4, pady=4) top.title('Filename filters') top.resizable(True, False) top.columnconfigure(0, weight=1) top.columnconfigure(1, weight=1) ttk.Label(top, text='Name filters:').grid(columnspan=2, sticky='w') entry = ttk.Entry(top) entry.grid(columnspan=2, sticky='ew', pady=4) entry.insert( 0, CONFIG.get('File browser', 'filename_filter', fallback='README, *.py, *.rst')) entry.bind('<Return>', ok) entry.bind('<Escape>', lambda e: top.destroy()) entry.focus_set() ttk.Button(top, text='Ok', command=ok).grid(row=2, column=0, padx=4, sticky='e') ttk.Button(top, text='Cancel', command=top.destroy).grid(row=2, column=1, padx=4, sticky='w') top.transient(self) top.grab_set()
def load_filters(self): filters = CONFIG.get('File browser', 'filename_filter', fallback='README, *.py, *.rst').split(', ') filters = [ '^' + ext.strip().replace('.', '\.').replace('*', '.*') + '$' for ext in filters ] self.filter = re.compile('|'.join(filters))
def manager(self, new_manager): if CONFIG.get("General", "layout") in ["vertical", "horizontal2"]: self.configure(style='TFrame') self._close_btn.grid_remove() else: self.configure(style='border.TFrame') self._close_btn.grid() if self.visible.get(): try: self._manager.forget(self) except TclError: pass self._manager = new_manager self.show() else: self._manager = new_manager
def hide(self): try: layout = CONFIG.get("General", "layout") if layout in ["vertical", "horizontal2"]: self.manager.hide(self) else: # save layout old = CONFIG.get("Layout", layout).split() w = self.master.winfo_width() pos = '%.3f' % (self.master.sashpos(0)/w) CONFIG.set("Layout", layout, f"{pos} {old[1]}") CONFIG.save() self.manager.forget(self) except TclError: pass
def run(self, interactive=True): """Run file in external console""" if self.current_tab >= 0: file = self.files[self.current_tab] if file: filename = os.path.join( os.path.dirname(os.path.dirname(__file__)), 'utils', 'console.py') external_console = CONFIG.get('Run', 'external_console', fallback='').split() try: Popen(external_console + [f"python {filename} {file} {interactive}"]) except Exception: showerror( "Error", "PyTkEditor failed to run the file, please check \ the external terminal configuration in the settings.", parent=self)
def __init__(self, master=None, histfile=HISTFILE, current_session=False, **kw): """ Crée un historique vide """ kw.setdefault('width', 1) RichText.__init__(self, master, **kw) self.histfile = histfile self.maxsize = CONFIG.getint('History', 'max_size', fallback=10000) self.history = [] self.current_session = current_session # --- bindings self.bind('<1>', lambda e: self.focus_set()) self.bind('<Control-a>', self.select_all) # --- load previous session history try: with open(histfile, 'rb') as file: dp = pickle.Unpickler(file) self.history = dp.load() self._session_start = len(self.history) except (FileNotFoundError, pickle.UnpicklingError, EOFError): self._session_start = 0 self.reset_text(init=True)
def _init_general(self): frame_general = ttk.Frame(self.notebook, padding=4) self.notebook.add(frame_general, text='General') # --- theme self.theme = tk.StringVar(self, CONFIG.get('General', 'theme')) ctheme = ttk.Combobox(frame_general, textvariable=self.theme, state='readonly', values=['dark', 'light'], width=5) ttk.Label(frame_general, text='Theme:').grid(row=0, column=0, sticky='e', padx=4, pady=8) ctheme.grid(row=0, column=1, sticky='w', padx=4, pady=8) # --- font families = list(font.families()) families.sort() length = max([len(f) for f in families]) self.family = AutoCompleteCombobox(frame_general, values=families, width=length - 1) self.family.insert(0, CONFIG.get('General', 'fontfamily')) self.size = AutoCompleteCombobox(frame_general, values=[str(i) for i in range(6, 20)], allow_other_values=True, width=3) self.size.insert(0, CONFIG.get('General', 'fontsize')) ttk.Label(frame_general, text='Font:').grid(row=1, column=0, sticky='e', padx=4, pady=8) self.family.grid(row=1, column=1, sticky='w', padx=4, pady=8) self.size.grid(row=1, column=2, sticky='w', padx=4, pady=8) # --- new file template frame_template = ttk.Frame(frame_general) ttk.Label(frame_template, text='Edit new file template').pack(side='left', padx=4, pady=8) ttk.Button(frame_template, image='img_edit', padding=1, command=self.edit_template).pack(side='left', padx=4, pady=8) frame_template.grid(row=2, columnspan=2, sticky='w') # --- confirm quit self.confirm_quit = ttk.Checkbutton( frame_general, text="Show confirmation dialog before exiting") if CONFIG.getboolean("General", "confirm_quit", fallback=False): self.confirm_quit.state(('selected', '!alternate')) else: self.confirm_quit.state(('!selected', '!alternate')) self.confirm_quit.grid(row=3, columnspan=2, sticky='w', padx=4, pady=4)
def __init__(self, parent=None, title="", message="", button=_("Ok"), image=None): """ Create a message box with one button. Arguments: parent: parent of the toplevel window title: message box title message: message box text (that can be selected) button: message displayed on the button image: image displayed at the left of the message, either a PhotoImage or a string """ tk.Toplevel.__init__(self, parent) self.transient(parent) self.resizable(False, False) self.title(title) self.result = "" self.button = button if image in ICONS: image = f"::tk::icons::{image}" elif isinstance(image, str) and os.path.exists(image): self.img = tk.PhotoImage(master=self, file=image) image = self.img frame = ttk.Frame(self) frame.rowconfigure(0, weight=1) frame.columnconfigure(1, weight=1) msg = wrap(message, 50) h = len(msg) + 1 w = len(max(msg, key=lambda x: len(x))) if h < 3: msg = wrap(message, 35) w = len(max(msg, key=lambda x: len(x))) h = len(msg) + 1 theme_name = '{} Theme'.format( CONFIG.get('General', 'theme').capitalize()) fg = CONFIG.get(theme_name, 'fg') display = tk.Text(frame, font="TkDefaultFont 10 bold", fg=fg, height=h, width=w, wrap="word") display.configure( inactiveselectbackground=display.cget("selectbackground")) display.insert("1.0", message) display.configure(state="disabled") display.grid(row=0, column=1, pady=(10, 4), padx=4, sticky="ewns") display.update_idletasks() if display.bbox('end-1c') is None: display.configure(height=h + 1) display.bind("<Button-1>", lambda event: display.focus_set()) if image: ttk.Label(frame, image=image).grid(row=0, column=0, padx=4, pady=(10, 4)) b = ttk.Button(self, text=button, command=self.validate) frame.pack() b.pack(padx=10, pady=10) try: self.grab_set() except tk.TclError: pass b.focus_set()
def _init_run(self): frame_run = ttk.Frame(self.notebook, padding=4) self.notebook.add(frame_run, text='Run') frame_run.columnconfigure(2, weight=1) # --- run self.run_console = tk.StringVar(self, CONFIG.get('Run', 'console')) self.external_interactive = ttk.Checkbutton( frame_run, text='Interact with the Python console after execution') self.external_interactive.state([ '!alternate', '!' * (self.run_console.get() == 'external') + 'disabled', '!' * (not CONFIG.getboolean('Run', 'external_interactive')) + 'selected' ]) self.external_console = ttk.Entry(frame_run) self.external_console.insert( 0, CONFIG.get('Run', 'external_console', fallback='')) ttk.Label(frame_run, text='Execute in:').grid(row=1, column=0, padx=(4, 8), pady=4, sticky='w') ttk.Radiobutton(frame_run, text='external terminal:', value='external', command=self._run_setting, variable=self.run_console).grid(row=1, column=1, pady=4, sticky='w') ttk.Radiobutton(frame_run, text='embedded console', value='console', command=self._run_setting, variable=self.run_console).grid(row=2, column=1, pady=4, sticky='w') jqt = ttk.Radiobutton(frame_run, text='Jupyter QtConsole', value='qtconsole', command=self._run_setting, variable=self.run_console) jqt.grid(row=3, column=1, pady=4, sticky='w') self.external_console.grid(row=1, column=2, sticky='ew', padx=(0, 4), pady=4) self.external_interactive.grid(row=4, columnspan=3, padx=4, pady=4, sticky='w') ttk.Separator(frame_run, orient='horizontal').grid(row=5, columnspan=3, sticky='ew', pady=4) # --- run cell self.run_cell_in = tk.StringVar( self, CONFIG.get('Run', 'cell', fallback="console")) ttk.Label(frame_run, text='Execute cells in:').grid(row=6, column=0, padx=(4, 8), pady=4, sticky='w') ttk.Radiobutton(frame_run, text='embedded console', value='console', variable=self.run_cell_in).grid(row=6, column=1, pady=4, sticky='w') jqt2 = ttk.Radiobutton(frame_run, text='Jupyter QtConsole', value='qtconsole', variable=self.run_cell_in) jqt2.grid(row=7, column=1, pady=4, sticky='w') if not JUPYTER: jqt.state(['disabled']) jqt2.state(['disabled'])
def update_style(self): RichText.update_style(self) self.maxsize = CONFIG.getint('History', 'max_size', fallback=10000)
def _current_session_toggle(self): val = self._current_session.get() self.history.current_session = val self.history.reset_text() CONFIG.set('History', 'current_session', str(val)) CONFIG.save()
def __init__(self, master=None, histfile=HISTFILE, **kw): BaseWidget.__init__(self, master, 'History', **kw) self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) self._search_count = tk.IntVar(self) current_session = CONFIG.getboolean('History', 'current_session', fallback=False) self._current_session = tk.BooleanVar(self, current_session) # --- menu self.menu = tk.Menu(self) self.menu.add_checkbutton(label='Current session only', image='img_menu_dummy_cb', selectimage='img_selected', indicatoron=False, compound='left', command=self._current_session_toggle, variable=self._current_session) self.menu.add_command(label='Find', command=self.find, image='img_menu_dummy', compound='left') syh = AutoHideScrollbar(self, orient='vertical') self.history = History(self, HISTFILE, current_session, yscrollcommand=syh.set, relief='flat', borderwidth=0, highlightthickness=0) syh.configure(command=self.history.yview) # --- search bar self._highlighted = '' self.frame_search = ttk.Frame(self, padding=2) self.frame_search.columnconfigure(1, weight=1) self.entry_search = ttk.Entry(self.frame_search) self.entry_search.bind('<Return>', self.search) self.entry_search.bind('<Escape>', lambda e: self.frame_search.grid_remove()) search_buttons = ttk.Frame(self.frame_search) ttk.Button(search_buttons, style='Up.TButton', padding=0, command=lambda: self.search(backwards=True)).pack(side='left', padx=2, pady=4) ttk.Button(search_buttons, style='Down.TButton', padding=0, command=self.search).pack(side='left', padx=2, pady=4) self.case_sensitive = ttk.Checkbutton(search_buttons, text='aA') self.case_sensitive.state(['selected', '!alternate']) self.case_sensitive.pack(side='left', padx=2, pady=4) self.regexp = ttk.Checkbutton(search_buttons, text='regexp') self.regexp.state(['!selected', '!alternate']) self.regexp.pack(side='left', padx=2, pady=4) self.full_word = ttk.Checkbutton(search_buttons, text='[-]') self.full_word.state(['!selected', '!alternate']) self.full_word.pack(side='left', padx=2, pady=4) self._highlight_btn = ttk.Checkbutton(search_buttons, image='img_highlight', padding=0, style='toggle.TButton', command=self.highlight_all) self._highlight_btn.pack(side='left', padx=2, pady=4) frame_find = ttk.Frame(self.frame_search) ttk.Button(frame_find, padding=0, command=lambda: self.frame_search.grid_remove(), style='close.TButton').pack(side='left') ttk.Label(frame_find, text='Find:').pack(side='right') frame_find.grid(row=1, column=0, padx=2, pady=4, sticky='ew') self.entry_search.grid(row=1, column=1, sticky='ew', pady=4, padx=2) search_buttons.grid(row=1, column=2, sticky='w') self.bind('<Control-f>', self.find) self.history.bind('<Control-f>', self.find) # --- placement syh.grid(row=0, column=1, sticky='ns') self.history.grid(row=0, column=0, sticky='nswe') self.frame_search.grid(row=1, columnspan=2, sticky='we') self.frame_search.grid_remove() self.update_style = self.history.update_style
def _init_editor(self): frame_editor = ttk.Frame(self.notebook, padding=4) self.notebook.add(frame_editor, text='Editor') frame_editor.columnconfigure(1, weight=1) # --- comments self.comment_marker = ttk.Entry(frame_editor, width=3) self.comment_marker.insert( 0, CONFIG.get("Editor", "comment_marker", fallback="~")) self.comment_toggle_mode = tk.StringVar( self, CONFIG.get('Editor', 'toggle_comment_mode', fallback='line_by_line')) rbtn_frame = ttk.Frame(frame_editor) ttk.Radiobutton(rbtn_frame, text='Line by line', variable=self.comment_toggle_mode, value='line_by_line').pack(side='left', padx=4, pady=4) ttk.Radiobutton(rbtn_frame, text='Block', variable=self.comment_toggle_mode, value='block').pack(side='left', padx=4, pady=4) # --- code checking self.code_check = ttk.Checkbutton(frame_editor, text='Check code') if CONFIG.getboolean("Editor", "code_check", fallback=True): self.code_check.state(('selected', '!alternate')) else: self.code_check.state(('!selected', '!alternate')) self.style_check = ttk.Checkbutton( frame_editor, text='Check style - PEP8 guidelines') if CONFIG.getboolean("Editor", "style_check", fallback=True): self.style_check.state(('selected', '!alternate')) else: self.style_check.state(('!selected', '!alternate')) # --- syntax highlighting frame_s_h = ttk.Frame(frame_editor) frame_s_h.columnconfigure(1, weight=1) styles = list(get_all_styles()) styles.sort() w = len(max(styles, key=lambda x: len(x))) self.editor_style = AutoCompleteCombobox(frame_s_h, values=styles, width=w) self.editor_style.insert(0, CONFIG.get('Editor', 'style')) mb = CONFIG.get('Editor', 'matching_brackets', fallback='#00B100;;bold').split(';') self.editor_matching_brackets = FormattingFrame(frame_s_h, *mb) umb = CONFIG.get('Editor', 'unmatched_bracket', fallback='#FF0000;;bold').split(';') self.editor_unmatched_bracket = FormattingFrame(frame_s_h, *umb) ttk.Label(frame_s_h, text='Theme:').grid(row=1, column=0, sticky='e', pady=(0, 10)) self.editor_style.grid(row=1, column=1, sticky='w', padx=8, pady=(0, 10)) ttk.Label(frame_s_h, text='Matching brackets:').grid(row=2, column=0, columnspan=2, sticky='w') self.editor_matching_brackets.grid(row=3, column=0, columnspan=2, sticky='w', padx=4) ttk.Label(frame_s_h, text='Unmatched bracket:').grid(row=4, column=0, columnspan=2, sticky='w', pady=(8, 0)) self.editor_unmatched_bracket.grid(row=5, column=0, columnspan=2, sticky='w', padx=4) # --- placement ttk.Label(frame_editor, text='Comment toggle marker:').grid(row=0, column=0, sticky='e', padx=4, pady=4) self.comment_marker.grid(row=0, column=1, sticky='w', padx=4, pady=4) ttk.Label(frame_editor, text='Comment toggle mode:').grid(row=1, column=0, sticky='e', padx=4, pady=4) rbtn_frame.grid(row=1, column=1, sticky='w') ttk.Label(frame_editor, text='Code checking (on file saving):').grid(row=2, column=0, sticky='e', padx=4, pady=4) self.code_check.grid(row=2, column=1, sticky='w', padx=3, pady=4) self.style_check.grid(row=3, column=1, sticky='w', padx=3, pady=4) ttk.Separator(frame_editor, orient='horizontal').grid(row=4, columnspan=2, sticky='ew', pady=4) ttk.Label(frame_editor, text='Syntax Highlighting:').grid(row=5, columnspan=2, sticky='w', pady=4, padx=4) frame_s_h.grid(row=6, columnspan=2, sticky='ew', pady=(4, 8), padx=12)
def _init_console(self): frame_console = ttk.Frame(self.notebook, padding=4) self.notebook.add(frame_console, text='Console') frame_console.columnconfigure(1, weight=1) # --- history self.history_size = ttk.Entry(frame_console, width=6) self.history_size.insert( 0, CONFIG.get('History', 'max_size', fallback='10000')) # --- syntax highlighting frame_s_h = ttk.Frame(frame_console) frame_s_h.columnconfigure(1, weight=1) styles = list(get_all_styles()) styles.sort() w = len(max(styles, key=lambda x: len(x))) self.console_style = AutoCompleteCombobox(frame_s_h, values=styles, width=w) self.console_style.insert(0, CONFIG.get('Console', 'style')) mb = CONFIG.get('Console', 'matching_brackets', fallback='#00B100;;bold').split(';') self.console_matching_brackets = FormattingFrame(frame_s_h, *mb) umb = CONFIG.get('Console', 'unmatched_bracket', fallback='#FF0000;;bold').split(';') self.console_unmatched_bracket = FormattingFrame(frame_s_h, *umb) ttk.Label(frame_s_h, text='Theme:').grid(row=1, column=0, sticky='e', pady=(0, 10)) self.console_style.grid(row=1, column=1, sticky='w', padx=8, pady=(0, 10)) ttk.Label(frame_s_h, text='Matching brackets:').grid(row=2, column=0, columnspan=2, sticky='w') self.console_matching_brackets.grid(row=3, column=0, columnspan=2, sticky='w', padx=4) ttk.Label(frame_s_h, text='Unmatched bracket:').grid(row=4, column=0, columnspan=2, sticky='w', pady=(8, 0)) self.console_unmatched_bracket.grid(row=5, column=0, columnspan=2, sticky='w', padx=4) # --- Jupyter QtConsole frame_qtconsole = ttk.Frame(frame_console) f1 = ttk.Frame(frame_qtconsole) f1.columnconfigure(1, weight=1) f2 = ttk.Frame(frame_qtconsole) f1.pack(anchor='w', fill='x') f2.pack(anchor='w', fill='x') ttk.Label(f1, text='IPYTHONDIR').grid(sticky='e', row=0, column=0, pady=4, padx=(0, 4)) ttk.Label(f1, text='JUPYTER_CONFIG_DIR').grid(sticky='e', row=1, column=0, pady=4, padx=(0, 4)) self.ipythondir = ttk.Entry(f1) home = os.path.expanduser('~') self.ipythondir.insert( 0, CONFIG.get('Console', 'ipython_dir', fallback=f'{home}/.ipython')) self.ipythondir.grid(row=0, column=1, sticky='ew', pady=4) self.jupyterdir = ttk.Entry(f1) self.jupyterdir.insert( 0, CONFIG.get('Console', 'jupyter_config_dir', fallback=f'{home}/.jupyter')) self.jupyterdir.grid(row=1, column=1, sticky='ew', pady=4) ttk.Button(f1, text='...', width=2, command=lambda: self.select_dir(self.ipythondir), padding=0).grid(row=0, column=2, sticky='ns', pady=4) ttk.Button(f1, text='...', width=2, command=lambda: self.select_dir(self.jupyterdir), padding=0).grid(row=1, column=2, sticky='ns', pady=4) ttk.Label( f2, text='options to pass to jupyter-qtconsole').pack(side='left') self.jupyter_options = ttk.Entry(f2) self.jupyter_options.pack(side='right', fill='x', expand=True, padx=(4, 0)) self.jupyter_options.insert( 0, CONFIG.get('Console', 'jupyter_options', fallback='')) if not JUPYTER: self.jupyter_options.state(['disabled']) # --- placement ttk.Label(frame_console, text='Maximum history size (truncated when quitting):').grid( row=0, column=0, sticky='e', padx=4, pady=4) self.history_size.grid(row=0, column=1, sticky='w', padx=4, pady=4) ttk.Separator(frame_console, orient='horizontal').grid(row=1, columnspan=2, sticky='ew', pady=4) ttk.Label(frame_console, text='Syntax Highlighting:').grid(row=2, columnspan=2, sticky='w', pady=4, padx=4) frame_s_h.grid(row=3, columnspan=2, sticky='ew', pady=(4, 8), padx=12) ttk.Separator(frame_console, orient='horizontal').grid(row=4, columnspan=2, sticky='ew', pady=4) ttk.Label(frame_console, text='Jupyter Qtconsole:').grid(row=5, columnspan=2, sticky='w', pady=4, padx=4) frame_qtconsole.grid(row=6, columnspan=2, sticky='ew', pady=(4, 8), padx=12)
def ok(event=None): CONFIG.set('File browser', 'filename_filter', entry.get()) CONFIG.save() self.load_filters() self.populate(self.filetree.get_children()[0], history=False) top.destroy()
def validate(self): # --- general CONFIG.set('General', 'theme', self.theme.get()) family = self.family.get() if family: CONFIG.set('General', 'fontfamily', family) try: size = int(self.size.get()) assert size > 0 except (ValueError, AssertionError): pass else: CONFIG.set('General', 'fontsize', str(size)) CONFIG.set('General', 'confirm_quit', str('selected' in self.confirm_quit.state())) # --- editor # --- --- comments CONFIG.set('Editor', 'toggle_comment_mode', self.comment_toggle_mode.get()) CONFIG.set('Editor', 'comment_marker', self.comment_marker.get()) # --- --- syntax highlighting estyle = self.editor_style.get() if estyle: CONFIG.set('Editor', 'style', estyle) CONFIG.set('Editor', 'matching_brackets', self.editor_matching_brackets.get_formatting()) CONFIG.set('Editor', 'unmatched_bracket', self.editor_unmatched_bracket.get_formatting()) cstyle = self.console_style.get() # --- --- code checking CONFIG.set('Editor', 'code_check', str('selected' in self.code_check.state())) CONFIG.set('Editor', 'style_check', str('selected' in self.style_check.state())) # --- console # --- --- history try: size = int(self.history_size.get()) assert size > 0 except (ValueError, AssertionError): pass else: CONFIG.set('History', 'max_size', str(size)) # --- --- syntax highlighting cstyle = self.console_style.get() if cstyle: CONFIG.set('Console', 'style', cstyle) CONFIG.set('Console', 'matching_brackets', self.console_matching_brackets.get_formatting()) CONFIG.set('Console', 'unmatched_bracket', self.console_unmatched_bracket.get_formatting()) CONFIG.set('Console', 'ipython_dir', self.ipythondir.get()) CONFIG.set('Console', 'jupyter_config_dir', self.jupyterdir.get()) CONFIG.set('Console', 'jupyter_options', self.jupyter_options.get()) # --- run console = self.run_console.get() CONFIG.set('Run', 'console', console) CONFIG.set('Run', 'external_interactive', str('selected' in self.external_interactive.state())) external_console = self.external_console.get() CONFIG.set('Run', 'external_console', external_console) CONFIG.set('Run', 'cell', self.run_cell_in.get()) if not external_console and console: ans = askokcancel( "Warning", 'No external terminal is set so executing code in an external terminal will fail.', self) if not ans: return CONFIG.save() self.destroy()
def __init__(self, parent=None, title="", message="", traceback="", report_msg=False, button=_("Ok"), image="error"): """ Create an error messagebox. Arguments: parent: parent of the toplevel window title: message box title message: message box text (that can be selected) button: message displayed on the button traceback: error traceback to display below the error message report_msg: if True display a suggestion to report error image: image displayed at the left of the message, either a PhotoImage or a string """ tk.Toplevel.__init__(self, parent) self.transient(parent) self.resizable(False, False) self.title(title) self.result = "" self.button = button # --- style theme_name = '{} Theme'.format( CONFIG.get('General', 'theme').capitalize()) fg = CONFIG.get(theme_name, 'fg') fieldbg = CONFIG.get(theme_name, 'fieldbg') if not parent: style = ttk.Style(self) style.theme_use('clam') style.configure("url.TLabel", foreground="blue") style.configure("txt.TFrame", background='white') if image in ICONS: image = f"::tk::icons::{image}" elif isinstance(image, str) and os.path.exists(image): self.img = tk.PhotoImage(master=self, file=image) image = self.img frame = ttk.Frame(self) frame.rowconfigure(0, weight=1) frame.columnconfigure(1, weight=1) msg = wrap(message, 50) h = len(msg) + 1 w = len(max(msg, key=lambda x: len(x))) if not traceback and h < 3: msg = wrap(message, 35) w = len(max(msg, key=lambda x: len(x))) h = len(msg) + 1 if traceback: tbk = wrap(traceback, 50) w = max(w, len(max(tbk, key=lambda x: len(x)))) display = tk.Text(frame, font="TkDefaultFont 10 bold", fg=fg, height=h, width=w, wrap="word") display.configure( inactiveselectbackground=display.cget("selectbackground")) display.insert("1.0", message) display.configure(state="disabled") display.grid(row=0, column=1, pady=(10, 4), padx=4, sticky="ewns") display.bind("<Button-1>", lambda event: display.focus_set()) display.update_idletasks() if display.bbox('end-1c') is None: display.configure(height=h + 1) if image: ttk.Label(frame, image=image).grid(row=0, column=0, padx=4, pady=(10, 4)) frame.pack(fill='x') if traceback: frame2 = ttk.Frame(self) frame2.columnconfigure(0, weight=1) frame2.rowconfigure(0, weight=1) txt_frame = ttk.Frame(frame2, style='txt.TFrame', relief='sunken', borderwidth=1) error_msg = tk.Text(txt_frame, width=w, wrap='word', font="TkDefaultFont 10", bg=fieldbg, fg=fg, height=8) error_msg.bind("<Button-1>", lambda event: error_msg.focus_set()) error_msg.insert('1.0', traceback) error_msg.configure(state="disabled") scrolly = Scrollbar(frame2, orient='vertical', command=error_msg.yview) scrolly.grid(row=0, column=1, sticky='ns') scrollx = Scrollbar(frame2, orient='horizontal', command=error_msg.xview) scrollx.grid(row=1, column=0, sticky='ew') error_msg.configure(yscrollcommand=scrolly.set, xscrollcommand=scrollx.set) error_msg.pack(side='left', fill='both', expand=True) txt_frame.grid(row=0, column=0, sticky='ewsn') frame2.pack(fill='both', padx=4, pady=(4, 4)) if report_msg: report_frame = ttk.Frame(self) ttk.Label(report_frame, text=_("Please report this bug on ")).pack(side="left") url = ttk.Label(report_frame, style="url.TLabel", cursor="hand1", font="TkDefaultFont 10 underline", text=REPORT_URL) url.pack(side="left") url.bind("<Button-1>", lambda e: url_open(REPORT_URL)) report_frame.pack(fill="x", padx=4, pady=(4, 0)) b = ttk.Button(self, text=button, command=self.validate) b.pack(padx=10, pady=(4, 10)) self.update_idletasks() self.set_hight(display, h) try: self.grab_set() except tk.TclError: pass b.focus_set()