class AddAlertPopup(object): def __init__(self, master, coin): self.top = Toplevel(master) self.top.title("Add Alert") Label(self.top, text=("Alert when price goes")).grid(row=0, column=0, columnspan=2, sticky="NEWS") b_m = ["above", "below"] self.a = Combobox(self.top, values=b_m, width=10) self.a.set(b_m[0]) self.a.grid(row=1, column=0, sticky="NEWS") self.e = Entry(self.top) self.e.focus_set() self.e.grid(row=1, column=1, sticky="NEWS") mark = [coin.market, "USDT"] self.m = Combobox(self.top, values=mark, width=10) self.m.set(mark[0]) self.m.grid(row=1, column=2, sticky="NEWS") Button(self.top, text='Ok', command=self.cleanup).grid(row=2, column=0, columnspan=3) ##center after packing center(self.top, master) def cleanup(self): self.value = [self.a.get(), self.e.get(), self.m.get()] self.top.destroy()
def main(): root = Tk() root.geometry("250x85+1250+650") app = Example() frame1 = Frame(root) frame1.pack(fill=BOTH) lbl1 = Label(frame1, text="Enter today's work") lbl1.pack(fill=X, padx=5, pady=5) frame2 = Frame(root) frame2.pack(fill=BOTH) entry1 = Entry(frame2) entry1.pack(fill=X, padx=5, expand=True) entry1.focus_set() def callback(): print(entry1.get()) print_values(entry1.get()) write_into_workbook(entry1.get()) frame3 = Frame(root) frame3.pack(fill=BOTH, expand=True) subbtn = Button(frame3, text="Done", command=callback) subbtn.place(x=4,y=5) root.mainloop()
class AddCoinPopup(object): def __init__(self, master, overview): #228 107 self.top = Toplevel(master, padx=50, pady=5) self.top.title("Add Coin") #Label(self.top, text="Add coin").grid(row=0,column=0,columnspan=2,sticky="NEWS") exch = ["Bittrex", "Binance"] mark = ["BTC", "ETH", "BNB", "USDT"] self.c = Combobox(self.top, values=exch, width=10) self.c.set(exch[0]) self.c.grid(row=0, column=0, columnspan=2, sticky="NEWS") self.m = Combobox(self.top, values=mark, width=10) self.m.set(mark[0]) self.m.grid(row=1, column=0, sticky="NEWS") self.e = Entry(self.top) self.e.focus_set() self.e.grid(row=1, column=1, columnspan=1, sticky="NEWS") Button(self.top, text='Ok', command=self.cleanup).grid(row=2, column=0, columnspan=2) ##center after packing center(self.top, master) def cleanup(self, ): self.value = [self.c.get(), self.m.get(), self.e.get()] self.top.destroy()
def set_password(self): """Set the master password used to encrypt the mailbox config files.""" def ok(event=None): pwd = getpwd.get() pwd2 = confpwd.get() if pwd == pwd2: # passwords match cryptedpwd = crypt.crypt(pwd, crypt.mksalt(crypt.METHOD_SHA512)) with open(os.path.join(LOCAL_PATH, '.pwd'), "w") as fich: fich.write(cryptedpwd) top.destroy() self.pwd = pwd logging.info('New password set') else: showerror(_('Error'), _('Passwords do not match!')) top = Toplevel(self, class_="CheckMails") top.iconphoto(True, self.im_icon) top.title(_("Set password")) top.resizable(False, False) Label(top, text=_("Enter master password")).pack(padx=10, pady=(4, 10)) getpwd = Entry(top, show='*', justify='center') getpwd.pack(padx=10, pady=4) Label(top, text=_("Confirm master password")).pack(padx=10, pady=4) confpwd = Entry(top, show='*', justify='center') confpwd.pack(padx=10, pady=4) Button(top, text="Ok", command=ok).pack(padx=10, pady=(4, 10)) confpwd.bind("<Key-Return>", ok) getpwd.focus_set() self.wait_window(top)
class DateWidget(Frame): """Gets a date from the user.""" def __init__(self, master): """Make boxes, register callbacks etc.""" Frame.__init__(self, master) self.label = Label(self, text="När är du född?") self.label.pack() self.entry_text = StringVar() self.entry_text.trace("w", lambda *args: self.onEntryChanged()) self.entry = Entry(self, width=date_entry_width, textvariable=self.entry_text) self.entry.insert(0, "ÅÅÅÅ-MM-DD") self.entry.pack(pady=small_pad) self.button = Button(self, text="Uppdatera", command=lambda: self.onDateChanged()) self.button.pack() self.entry.focus_set() self.entry.select_range(0, END) self.entry.bind("<Return>", lambda x: self.onDateChanged()) def setListener(self, pred_view): """Select whom to notify when a new date is entered.""" self.pred_view = pred_view def onDateChanged(self): """Notifies the PredictionWidget that the date has been changed.""" try: date = datetime.datetime.strptime(self.entry.get(), "%Y-%m-%d").date() self.pred_view.update(date) except ValueError: self.entry.configure(foreground="red") def onEntryChanged(self): """Reset the text color.""" self.entry.configure(foreground="")
class Add(Toplevel): def __init__(self, master): Toplevel.__init__(self, master, class_=APP_NAME, padx=6, pady=6) self.title(_("Add Feed")) self.grab_set() self.resizable(True, False) self.columnconfigure(1, weight=1) Label(self, text=_('URL')).grid(row=0, column=0, sticky='e', pady=4, padx=4) self.url = "" self.url_entry = Entry(self, width=30) self.url_entry.grid(row=0, column=1, sticky='ew', pady=4, padx=4) self.url_entry.bind('<Return>', self.validate) frame = Frame(self) frame.grid(row=1, column=0, columnspan=2) Button(frame, text=_('Ok'), command=self.validate).grid(row=0, column=0, sticky='e', pady=4, padx=4) Button(frame, text=_('Cancel'), command=self.destroy).grid(row=0, column=1, sticky='w', pady=4, padx=4) self.url_entry.focus_set() def validate(self, event=None): self.url = self.url_entry.get().strip() self.destroy()
def ask_password(self): """Ask the master password in order to decrypt the mailbox config files.""" def ok(event=None): with open(os.path.join(LOCAL_PATH, '.pwd')) as fich: cryptedpwd = fich.read() pwd = getpwd.get() if crypt.crypt(pwd, cryptedpwd) == cryptedpwd: # passwords match top.destroy() logging.info('Authentication successful') self.pwd = pwd else: showerror(_('Error'), _('Incorrect password!')) logging.warning('Authentication failed') getpwd.delete(0, "end") top = Toplevel(self, class_="CheckMails") top.title(_("Password")) top.resizable(False, False) Label(top, text=_("Enter password")).pack(padx=10, pady=(10, 4)) getpwd = Entry(top, show='*', justify='center') getpwd.pack(padx=10, pady=4) Button(top, text="Ok", command=ok).pack(padx=10, pady=(4, 10)) getpwd.bind("<Key-Return>", ok) getpwd.focus_set() self.wait_window(top)
def get_number_from(entry: Entry) -> float: try: return float(fix_separator(entry.get())) except ValueError: entry.focus_set() entry.select_range(0, END) stop_execution("Неверный формат строки")
def add_task(self): def ajoute(event=None): task = nom.get() if task and not CONFIG.has_option("Tasks", task): index = len(CONFIG.options("Tasks")) self.menu_tasks.insert_radiobutton(index, label=task, value=task, variable=self.task) CONFIG.set("Tasks", task, CMAP[index % len(CMAP)]) top.destroy() with open(PATH_CONFIG, "w") as file: CONFIG.write(file) self.menu_tasks.invoke(index) else: nom.delete(0, "end") top = Toplevel(self) top.title(_("New task")) top.transient(self) top.grab_set() nom = Entry(top, width=20) nom.grid(row=0, columnspan=2, sticky="ew") nom.focus_set() nom.bind('<Key-Return>', ajoute) Button(top, text=_("Cancel"), command=top.destroy).grid(row=1, column=0) Button(top, text=_("Ok"), command=ajoute).grid(row=1, column=1) top.wait_window(top)
def change_name(self, event): """Change category name.""" def ok(event): cats = [l.cget('text').lower() for l in self.cat_labels.values()] cat = name.get().strip().lower() if cat and cat not in cats: label.configure(text=cat.capitalize()) if old_cat == self.default_category.get(): self.default_category.set(cat.capitalize()) self.update_def_cat_menu() name.destroy() label = event.widget old_cat = label.cget('text') name = Entry(self, justify='center') name.insert(0, label.cget('text')) name.place(in_=label, relx=0, rely=0, anchor='nw', relwidth=1, relheight=1) name.bind('<FocusOut>', lambda e: name.destroy()) name.bind('<Escape>', lambda e: name.destroy()) name.bind('<Return>', ok) name.selection_range(0, 'end') name.focus_set()
def add_link(self): def ok(eveny=None): lien = link.get() txt = text.get() if lien: if not txt: txt = lien self.nb_links += 1 if self.txt.tag_ranges("sel"): index = self.txt.index("sel.first") self.txt.delete('sel.first', 'sel.last') else: index = "current" tags = self.txt.tag_names(index) + ("link", "link#%i" % self.nb_links) self.txt.insert("current", txt, tags) if not lien[:4] == "http": lien = "http://" + lien self.links[self.nb_links] = lien self.txt.tag_bind("link#%i" % self.nb_links, "<Button-1>", lambda e: open_url(lien)) top.destroy() top = Toplevel(self) top.transient(self) top.update_idletasks() top.geometry("+%i+%i" % top.winfo_pointerxy()) top.grab_set() top.resizable(True, False) top.title(_("Link")) top.columnconfigure(1, weight=1) text = Entry(top) link = Entry(top) if self.txt.tag_ranges('sel'): txt = self.txt.get('sel.first', 'sel.last') else: txt = '' text.insert(0, txt) text.icursor("end") Label(top, text=_("Text")).grid(row=0, column=0, sticky="e", padx=4, pady=4) Label(top, text=_("Link")).grid(row=1, column=0, sticky="e", padx=4, pady=4) text.grid(row=0, column=1, sticky="ew", padx=4, pady=4) link.grid(row=1, column=1, sticky="ew", padx=4, pady=4) Button(top, text="Ok", command=ok).grid(row=2, columnspan=2, padx=4, pady=4) text.focus_set() text.bind("<Return>", ok) link.bind("<Return>", ok)
def customDate(): def displayCustomDate(date): enterDateBox.destroy() print("Start listing songs from: " + date + ".") try: chart = billboard.ChartData("hot-100", date) label1 = str("1. " + str(chart[0])) label2 = str("2. " + str(chart[1])) label3 = str("3. " + str(chart[2])) label4 = str("4. " + str(chart[3])) label5 = str("5. " + str(chart[4])) topSong1.set(label1) topSong2.set(label2) topSong3.set(label3) topSong4.set(label4) topSong5.set(label5) chart_date.set("Chart data date: " + date) print("Done listing songs from: " + date + ".") except Exception as e: logDump(e) messagebox.showerror( "Out of range date", "Check your dates again. It could be due to:\n-Date too long in the past (before BillBoard charts existed)\-" ) def cancelDialog(): enterDateBox.destroy() date_object = datetime.now() formatted_date = "Chart data date: " + date_object.strftime( '%Y-%m-%d') chart_date.set(formatted_date) enterDateBox = Toplevel(billpy_window) enterDateBox.grab_set() enterDateBox.title("Custom date entry") enterDateBox.focus_set() enterDate_msg = Label(enterDateBox, text="Enter the date below (YYYY-MM-DD):", font=("Segoe UI", 10)) enterDate_entry = Entry(enterDateBox) enterDate_entry.focus_set() enterDate_confirm = Button( enterDateBox, text="OK", command=lambda: displayCustomDate(enterDate_entry.get())) enterDate_confirm.bind( "<Return>", lambda: displayCustomDate(enterDate_entry.get())) enterDate_cancel = Button(enterDateBox, text="Cancel", command=cancelDialog) enterDate_msg.pack() enterDate_entry.pack() enterDate_confirm.pack() enterDate_cancel.pack() centerForms(enterDateBox) chart_date.set("Loading charts...") enterDateBox.mainloop()
def __init__(self, parent, url=None, buttonSEC=False, buttonRSS=False): super(DialogURL, self).__init__(parent) self.parent = parent parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", parent.geometry()) dialogX = int(parentGeometry.group(3)) dialogY = int(parentGeometry.group(4)) self.accepted = False self.url = None self.transient(self.parent) self.title("Enter URL") self.urlVar = StringVar() self.urlVar.set(url if url is not None else "http://") frame = Frame(self) urlLabel = Label(frame, text=_("URL:"), underline=0) urlEntry = Entry(frame, textvariable=self.urlVar, width=60) urlEntry.focus_set() okButton = Button(frame, text=_("OK"), command=self.ok) cancelButton = Button(frame, text=_("Cancel"), command=self.close) if buttonSEC: usSecButton = Button(frame, text=_("SEC search"), command=self.usSec) usSecButton.grid(row=1, column=1, sticky=W, pady=3) ToolTip(usSecButton, text=_("Opens US SEC Edgar Company Search (in web browser)\n\n" "(1) Find the company in web browser,\n" "(2) Click 'documents' button for desired filing,\n" "(3) Find 'data files' panel, instance document row, 'document' column,\n" "(4) On instance document file name, right-click browser menu: 'copy shortcut',\n" "(5) Come back to this dialog window,\n" "(6) Ctrl-v (paste) shortcut into above URL text box,\n" "(7) Click ok button to load instance document"), wraplength=480) if buttonRSS: rssButton = Button(frame, text=_("SEC RSS"), command=self.rssFeed) rssButton.grid(row=1, column=1, pady=3) ToolTip(rssButton, text=_("Opens current US SEC Edgar RSS feed"), wraplength=480) urlLabel.grid(row=0, column=0, sticky=W, pady=3, padx=3) urlEntry.grid(row=0, column=1, columnspan=3, sticky=EW, pady=3, padx=3) okButton.grid(row=1, column=2, sticky=E, pady=3) ToolTip(okButton, text=_("Opens above URL from web cache, downloading to cache if necessary"), wraplength=240) cancelButton.grid(row=1, column=3, sticky=EW, pady=3, padx=3) ToolTip(cancelButton, text=_("Cancel operation")) frame.grid(row=0, column=0, sticky=(N,S,E,W)) frame.columnconfigure(1, weight=1) window = self.winfo_toplevel() window.columnconfigure(0, weight=1) self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100)) self.bind("<Alt-u>", lambda *ignore: urlEntry.focus_set()) self.bind("<Return>", self.ok) self.bind("<Escape>", self.close) self.protocol("WM_DELETE_WINDOW", self.close) self.grab_set() self.wait_window(self)
def add_cat(self): """Add a category.""" top = Toplevel(self, class_='MyNotes') top.transient(self) top.grab_set() top.resizable(False, False) top.title(_("New Category")) def valide(event=None): cats = [l.cget('text').lower() for l in self.cat_labels.values()] cat = name.get().strip().lower() if cat and cat not in cats: c, i = self.frame_cat.grid_size() self.cat_labels[cat] = Label(self.frame_cat, text="%s" % cat.capitalize()) self.cat_labels[cat].grid(row=i, column=0, sticky="e") self.cat_colors[cat] = StringVar(self, _("Yellow")) self.cat_menus[cat] = OptionMenu( self.frame_cat, self.cat_colors[cat], _("Yellow"), *self.colors, command=lambda color, c=cat: self.change_menubutton_color( color, c), style="%s.TMenubutton" % cat) self.style.configure("%s.TMenubutton" % cat, background=COLORS[_("Yellow")]) optionmenu_patch(self.cat_menus[cat], self.cat_colors[cat]) self.cat_menus[cat].grid(row=i, column=1, padx=4, pady=4) self.cat_buttons[cat] = Button( self.frame_cat, image=self.im_delete, padding=0, command=lambda c=cat: self.del_cat(c)) self.cat_buttons[cat].grid(row=i, column=2, padx=4, pady=4, sticky='ns') self.cat_buttons[self.categories[0]].configure(state="normal") self.categories.append(cat) self.categories.sort() self.update_def_cat_menu() top.destroy() name = Entry(top, justify="center") name.grid(row=0, column=0, columnspan=2, sticky="ew") name.bind("<Return>", valide) name.bind("<Escape>", lambda e: top.destroy()) name.focus_set() Button(top, text="Ok", command=valide).grid(row=1, column=0, sticky="nswe") Button(top, text=_("Cancel"), command=top.destroy).grid(row=1, column=1, sticky="nswe")
def change_password(self): """ Change the master password: decrypt all the mailbox config files using the old password and encrypt them with the new. """ def ok(event=None): with open(os.path.join(LOCAL_PATH, '.pwd')) as fich: cryptedpwd = fich.read() old = oldpwd.get() pwd = newpwd.get() pwd2 = confpwd.get() if crypt.crypt(old, cryptedpwd) == cryptedpwd: # passwords match if pwd == pwd2: # new passwords match cryptedpwd = crypt.crypt(pwd, crypt.mksalt(crypt.METHOD_SHA512)) with open(os.path.join(LOCAL_PATH, '.pwd'), "w") as fich: fich.write(cryptedpwd) mailboxes = CONFIG.get( "Mailboxes", "active").split(", ") + CONFIG.get( "Mailboxes", "inactive").split(", ") while "" in mailboxes: mailboxes.remove("") for mailbox in mailboxes: server, login, password, folder = decrypt(mailbox, old) if server is not None: encrypt(mailbox, pwd, server, login, password, folder) self.pwd = pwd top.destroy() logging.info('Password changed') return pwd else: showerror(_('Error'), _('Passwords do not match!')) else: showerror(_('Error'), _('Incorrect password!')) top = Toplevel(self, class_="CheckMails") top.iconphoto(True, self.im_icon) top.resizable(False, False) Label(top, text=_("Old password")).pack(padx=10, pady=(10, 4)) oldpwd = Entry(top, show='*', justify='center') oldpwd.pack(padx=10, pady=4) Label(top, text=_("New password")).pack(padx=10, pady=4) newpwd = Entry(top, show='*', justify='center') newpwd.pack(padx=10, pady=4) Label(top, text=_("Confirm password")).pack(padx=10, pady=4) confpwd = Entry(top, show='*', justify='center') confpwd.pack(padx=10, pady=4) Button(top, text="Ok", command=ok).pack(padx=10, pady=(4, 10)) confpwd.bind("<Key-Return>", ok) oldpwd.focus_set() self.wait_window(top)
class WinLogin(Tk): def __init__(self, *args, **kwargs): ''' Se establecen las configuraciones de la ventana de logueo ''' global connection self.cursor = connection.cursor() self.accepted = False self.root = Tk() self.root.title("Inicio de sesión") self.root.resizable(0, 0) # Cuando se genera el evento de eliminar la pantalla (cerrarla) # se destuye la ventana principal de la aplicación que estaba minimizada self.root.protocol("WM_DELETE_WINDOW", self.root.destroy) lbl_user = Label(self.root, text="Usuario: ") lbl_user.grid(row=0, column=0, padx=(20, 10), pady=(10, 0)) self.e_user = Entry(self.root, width=25) self.e_user.grid(row=0, column=1, padx=(10, 20), pady=(20, 10)) self.e_user.focus_set() lbl_password = Label(self.root, text="Contraseña: ") lbl_password.grid(row=2, column=0, padx=(20, 10)) self.e_password = Entry(self.root, width=25, show="*") self.e_password.grid(row=2, column=1, padx=(10, 20), pady=10) btn_login = Button(self.root, text="Ingresar", command=self.login) btn_login.grid(row=3, column=0, columnspan=3, padx=10, pady=(5, 20)) self.root.mainloop() def login(self): ''' Realiza el proceso de logueo de un administrador ''' # Busca el administrador que posea el usuario y la contraseña proporcionados. sql = '''SELECT * FROM `Administrador` WHERE usuario = (?) and contrasena = (?)''' self.cursor.execute(sql, (self.e_user.get(), self.e_password.get())) # Si lo encontró, completa el logueo. Caso contrario, informa el error. if self.cursor.fetchone(): self.accepted = True self.root.destroy() else: mb.showerror("Datos inválidos", "El usuario y/o la contraseña son incorrectos.")
class StudentIDDlg(Toplevel): def __init__(self, initialText, title, labeltext = '' ): Toplevel.__init__(self) self.initUI(initialText,title,labeltext) def initUI(self,initialText,title,labeltext=''): self.STID = initialText self.geometry("200x120") if len(title) > 0: self.title(title) self.style = Style() self.style.theme_use("default") # default style = Style() style.configure("Exit.TButton", foreground="red", background="white") style.configure("MainButton.TButton", foreground="yellow", background="red") if len(labeltext) == 0: labeltext = 'Please enter your ID..' self.bind("<Return>", self.ok) xpos = 40 ypos = 30 xpos2 = xpos+100 l1 = Label(self, text=initialText, foreground = "#ff0000", background = "light blue", font = "Arial 9") # Arial 12 bold italic l1.place(x=xpos, y=ypos) self.txtID = Entry(self) self.txtID.place(x=xpos2, y = ypos, width=70) self.txtID.bind("<Return>", self.ok) self.txtID.bind("<Escape>", self.cancel) self.txtID.focus_set() ypos += 30 okButton = Button(self, text="OK", background = "light blue", command=self.ok) #okButton.configure(style="ExitButton.TButton") # does not work okButton.place(x=xpos2, y=ypos) def getID(self): return self.STID def ok(self, event=None): self.STID = self.txtID.get() self.destroy() def cancel(self, event=None): self.destroy()
def add(self): def ok(event=None): txt = e.get() txt = txt.strip().replace(" ", "\ ") if txt and txt not in self.exclude_list: self.exclude_list.append(txt) self.listvar.set(" ".join(self.exclude_list)) top.destroy() top = Toplevel(self) top.transient(self) top.grab_set() e = Entry(top) e.pack(expand=True, fill="x", padx=10, pady=(10, 4)) e.focus_set() e.bind("<Key-Return>", ok) Button(top, text="Ok", command=ok).pack(padx=10, pady=(4, 10))
class SelectKernel(Toplevel): """About Toplevel.""" def __init__(self, master): """Create the Toplevel to select an existing Jupyter kernel.""" Toplevel.__init__(self, master, class_=master.winfo_class(), padx=10, pady=10) self.resizable(True, False) self.title("Select existing kernel") self.columnconfigure(0, minsize=22) self.columnconfigure(1, weight=1) self.columnconfigure(2, weight=1) self.selected_kernel = '' Label(self, text="Select the .json connection file or enter the existing kernel's id:").grid(columnspan=4, sticky='w', pady=4) self.entry = Entry(self, width=10) self.entry.grid(row=1, columnspan=3, sticky='ew', pady=4) self.entry.bind('<Return>', lambda e: self.validate()) Button(self, text='...', width=2, padding=0, command=self.select_file).grid(row=1, column=3, sticky='sn', pady=4) Button(self, text='Ok', command=self.validate).grid(row=2, column=1, padx=4, pady=4, sticky='e') Button(self, text='Cancel', command=self.destroy).grid(row=2, column=2, padx=4, pady=4, sticky='w') self.transient(master) self.entry.focus_set() self.update_idletasks() self.grab_set() def validate(self): self.selected_kernel = self.entry.get() self.destroy() def select_file(self): filename = askopenfilename(self, "Select connection file", initialdir=jupyter_runtime_dir(), defaultextension='.json', filetypes=[('JSON', '*.json'), ('All files', '*')]) if filename: self.entry.delete(0, 'end') self.entry.insert(0, filename)
def add_task(self): def ajoute(event=None): task = nom.get().lower().strip() if task in self.tasks: showerror( _("Error"), _("The task {task} already exists.").format(task=task), parent=self) elif task: coul = CMAP[(len(self.tasks) + 1) % len(CMAP)] i = self.task_frame.grid_size()[1] + 1 self.tasks[task] = ColorFrame(self.task_frame, coul, task.capitalize()) self.tasks[task].grid(row=i, column=0, sticky='e', padx=4, pady=4) b = Button(self.task_frame, image=self.im_moins, padding=2, command=lambda t=task: self.del_task(t)) b.grid(row=i, column=1, sticky='w', padx=4, pady=4) self._tasks_btns[task] = b self._tasks_btns[CONFIG.options("PomodoroTasks")[0]].state( ['!disabled']) top.destroy() top = Toplevel(self) top.title(_("New task")) top.transient(self) top.grab_set() nom = Entry(top, width=20, justify='center') nom.grid(row=0, columnspan=2, sticky="ew") nom.focus_set() nom.bind('<Key-Return>', ajoute) Button(top, text=_("Cancel"), command=top.destroy).grid(row=1, column=0) Button(top, text=_("Ok"), command=ajoute).grid(row=1, column=1) top.wait_window(top)
def text_query(self, query_lable, original_text=None, accept_func=None): if self._query is not None: return frame = Frame(self.menubar) label = Label(frame, text=query_lable) label.grid(column=0, row=0, sticky=(N, S)) self._accept_func = accept_func entry = Entry(frame) if original_text is not None: entry.insert(0, original_text) entry.original_value = original_text entry.grid(column=1, row=0, sticky=(N,S,W,E)) kb.make_bindings(kb.text_query, {'accept': self.accept_query, 'cancel': lambda e: self.close_query()}, entry.bind) frame.grid(column=self._menucolumn, row=0) self._menucolumn += 1 entry.focus_set() self._query = frame
def text_query(self, query_lable, original_text=None, accept_func=None): if self._query is not None: return frame = Frame(self.menubar) label = Label(frame, text=query_lable) label.grid(column=0, row=0, sticky=(N, S)) self._accept_func = accept_func entry = Entry(frame) if original_text is not None: entry.insert(0, original_text) entry.original_value = original_text entry.grid(column=1, row=0, sticky=(N, S, W, E)) kb.make_bindings(kb.text_query, { 'accept': self.accept_query, 'cancel': lambda e: self.close_query() }, entry.bind) frame.grid(column=self._menucolumn, row=0) self._menucolumn += 1 entry.focus_set() self._query = frame
class FunnelEditor(tk.Frame, Subscriber, Observable): """Editor for one funnel Args: parent (tk.Frame): parent widget funnel (model.shipdata.Funnel): self explanatory index (int): just a number to indicate the funnel's number. Display only command_stack (framework.Command_stack): undo/redo stack """ def __init__(self, parent, funnel, index, command_stack): tk.Frame.__init__(self, parent, borderwidth=4, relief="raised") Subscriber.__init__(self, funnel) Observable.__init__(self) self._funnel = funnel self._command_stack = command_stack self._active_var = tk.IntVar() self._active_var.trace_add("write", self._switch_active) self._y_var = tk.StringVar() self._y_var.set(0) self._y_var.trace_add("write", self._set_position) self._x_var = tk.StringVar() self._x_var.set(0) self._x_var.trace_add("write", self._set_position) self._oval_var = tk.IntVar() self._oval_var.trace_add("write", self._switch_oval) self._update() self.bind("<Button-1>", self._on_click) is_active = Checkbutton( self, text=f"Funnel n°{index}: ", variable=self._active_var) is_active.grid(columnspan=3) is_active.bind("<FocusIn>", self._on_get_focus) is_active.bind("<FocusOut>", self._on_lost_focus) x_label = Label(self, text="\u21d5", anchor=tk.CENTER) x_label.grid(sticky=tk.E + tk.W) x_label.bind("<Button-1>", self._on_click) self.x_entry = Entry(self, textvariable=self._x_var, width=6) self.x_entry.grid(row=2, column=0, sticky=tk.W + tk.E) self.x_entry.bind("<FocusIn>", self._on_get_focus) self.x_entry.bind("<FocusOut>", self._on_lost_focus) y_label = Label(self, text="\u21d4", anchor=tk.CENTER) y_label.grid(row=1, column=1, sticky=tk.E+tk.W) y_label.bind("<Button-1>", self._on_click) self.y_entry = Entry(self, textvariable=self._y_var, width=6) self.y_entry.grid(row=2, column=1, sticky=tk.W + tk.E) self.y_entry.bind("<FocusIn>", self._on_get_focus) self.y_entry.bind("<FocusOut>", self._on_lost_focus) is_oval = Checkbutton(self, text="Is Oval", variable=self._oval_var) is_oval.grid(row=1, column=2) is_oval.bind("<FocusIn>", self._on_get_focus) is_oval.bind("<FocusOut>", self._on_lost_focus) force_centerline = Button( self, text="force on centerline", command=self._force_centerline) force_centerline.grid(row=2, column=2) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.columnconfigure(2, weight=1) def _on_click(self, _event): self.x_entry.focus_set() def _on_notification(self, observable, event_type, event_info): self._update() def _on_lost_focus(self, event): self.configure(relief="raised") def _on_get_focus(self, *_args): self.configure(relief="sunken") self._notify("Focus", {}) def _update(self, *_args): """Set all the displayed info to what is in the funnel data """ # flags to avoid circular update of the values self._updating = True self._active_var.set(int(self._funnel.y != 0)) self._y_var.set(round(self._funnel.y, 1)) self._x_var.set(round(self._funnel.x, 1)) self._oval_var.set(self._funnel.oval) self._notify( "Update", {"Position": [self.x, self.y], "Oval": self.oval}) self._updating = False def _set_position(self, _var_name, _list_index, _operation): """Called when the position of a funnel is modified """ if not self._updating and is_float(self._x_var.get()) and is_float(self._y_var.get()): self._command_stack.do(model.funnel.MoveFunnel(self._funnel, float(self._x_var.get()), float(self._y_var.get()))) def _switch_active(self, _var_name, _list_index, _operation): """Called when switching the funnel on and off """ if not self._updating: if not bool(self._active_var.get()): self._command_stack.do( model.funnel.MoveFunnel(self._funnel, 0)) else: self._command_stack.do( model.funnel.MoveFunnel(self._funnel, 1)) def _switch_oval(self, _var_name, _list_index, _operation): """Called when switching the funnel from oval to circular and vice-versa """ if not self._updating: self._command_stack.do(model.funnel.OvalFunnel(self._funnel, bool(self._oval_var.get()))) def update_to_coord(self, point): """Move the funnel to the Y position of the given point Intended to be called from click on the top view point in funnel coordinates """ self._command_stack.do(model.funnel.MoveFunnel(self._funnel, point[0], point[1])) def _force_centerline(self): self._x_var.set(0) @property def oval(self): """Pipe throught the funnel's data state""" return self._funnel.oval @property def y(self): """Pipe throught the funnel's data state""" return self._funnel.y @property def x(self): """Pipe throught the funnel's data state""" return self._funnel.x
class Query(Toplevel): """Base class for getting verified answer from a user. For this base class, accept any non-blank string. """ def __init__(self, parent, title, message, *, text0='', used_names={}, _htest=False, _utest=False): """Create modal popup, return when destroyed. Additional subclass init must be done before this unless _utest=True is passed to suppress wait_window(). title - string, title of popup dialog message - string, informational message to display text0 - initial value for entry used_names - names already in use _htest - bool, change box location when running htest _utest - bool, leave window hidden and not modal """ self.parent = parent # Needed for Font call. self.message = message self.text0 = text0 self.used_names = used_names Toplevel.__init__(self, parent) self.withdraw() # Hide while configuring, especially geometry. self.title(title) self.transient(parent) if not _utest: # Otherwise fail when directly run unittest. self.grab_set() _setup_dialog(self) if self._windowingsystem == 'aqua': self.bind("<Command-.>", self.cancel) self.bind('<Key-Escape>', self.cancel) self.protocol("WM_DELETE_WINDOW", self.cancel) self.bind('<Key-Return>', self.ok) self.bind("<KP_Enter>", self.ok) self.create_widgets() self.update_idletasks() # Need here for winfo_reqwidth below. self.geometry( # Center dialog over parent (or below htest box). "+%d+%d" % (parent.winfo_rootx() + (parent.winfo_width() / 2 - self.winfo_reqwidth() / 2), parent.winfo_rooty() + ((parent.winfo_height() / 2 - self.winfo_reqheight() / 2) if not _htest else 150))) self.resizable(height=False, width=False) if not _utest: self.deiconify() # Unhide now that geometry set. self.wait_window() def create_widgets(self, ok_text='OK'): # Do not replace. """Create entry (rows, extras, buttons. Entry stuff on rows 0-2, spanning cols 0-2. Buttons on row 99, cols 1, 2. """ # Bind to self the widgets needed for entry_ok or unittest. self.frame = frame = Frame(self, padding=10) frame.grid(column=0, row=0, sticky='news') frame.grid_columnconfigure(0, weight=1) entrylabel = Label(frame, anchor='w', justify='left', text=self.message) self.entryvar = StringVar(self, self.text0) self.entry = Entry(frame, width=30, textvariable=self.entryvar) self.entry.focus_set() self.error_font = Font(name='TkCaptionFont', exists=True, root=self.parent) self.entry_error = Label(frame, text=' ', foreground='red', font=self.error_font) # Display or blank error by setting ['text'] =. entrylabel.grid(column=0, row=0, columnspan=3, padx=5, sticky=W) self.entry.grid(column=0, row=1, columnspan=3, padx=5, sticky=W + E, pady=[10, 0]) self.entry_error.grid(column=0, row=2, columnspan=3, padx=5, sticky=W + E) self.create_extra() self.button_ok = Button(frame, text=ok_text, default='active', command=self.ok) self.button_cancel = Button(frame, text='Cancel', command=self.cancel) self.button_ok.grid(column=1, row=99, padx=5) self.button_cancel.grid(column=2, row=99, padx=5) def create_extra(self): pass # Override to add widgets. def showerror(self, message, widget=None): #self.bell(displayof=self) (widget or self.entry_error)['text'] = 'ERROR: ' + message def entry_ok(self): # Example: usually replace. "Return non-blank entry or None." entry = self.entry.get().strip() if not entry: self.showerror('blank line.') return None return entry def ok(self, event=None): # Do not replace. '''If entry is valid, bind it to 'result' and destroy tk widget. Otherwise leave dialog open for user to correct entry or cancel. ''' self.entry_error['text'] = '' entry = self.entry_ok() if entry is not None: self.result = entry self.destroy() else: # [Ok] moves focus. (<Return> does not.) Move it back. self.entry.focus_set() def cancel(self, event=None): # Do not replace. "Set dialog result to None and destroy tk widget." self.result = None self.destroy() def destroy(self): self.grab_release() super().destroy()
class MainApp(): def __init__(self): self.File_name = None self.Programe_Name = "CHE-Editor" self.WDG = Tk() self.WDG.title(self.Programe_Name) self.WDG.iconbitmap("icons/icon_editor.ico") self.WDG.geometry("860x620") self.WDG.maxsize(width=1340, height=700) self.WDG.minsize(width=860, height=620) self.Main_UI() def Main_UI(self): self.MenuBar = Menu(self.WDG) #1 #MenuBar #File_menu self.File_menu = Menu(self.MenuBar, tearoff=0, title="File") self.MenuBar.add_cascade(label="File", menu=self.File_menu) #Edit_menu self.Edit_menu = Menu(self.MenuBar, tearoff=0, title="Edit") self.MenuBar.add_cascade(label="Edit", menu=self.Edit_menu) #View_menu self.View_menu = Menu(self.MenuBar, tearoff=0, title="View") self.MenuBar.add_cascade(label="View", menu=self.View_menu) #Theme_menu in View self.Theme_menu = Menu(self.View_menu, tearoff=0, title="Theme") self.View_menu.add_cascade(label="Theme", menu=self.Theme_menu) #Option_menu self.Options_menu = Menu(self.MenuBar, tearoff=0, title="Options") self.MenuBar.add_cascade(label="Options", menu=self.Options_menu) #Help_menu self.Help_menu = Menu(self.MenuBar, tearoff=0, title="Help") self.MenuBar.add_cascade(label="Help", menu=self.Help_menu) #2 #Icons Variables #Edit_Menu Icons Undo = PhotoImage(file="icons/Undo.gif") Redo = PhotoImage(file="icons/redo.gif") Paste = PhotoImage(file="icons/paste.gif") Copy = PhotoImage(file="icons/copy.gif") Cut = PhotoImage(file="icons/cut.gif") #Help_Menu_Icons Help = PhotoImage(file="icons/help.gif") About = PhotoImage(file="icons/about.gif") #File_Menu_Icons New = PhotoImage(file="icons/new.gif") Open = PhotoImage(file="icons/open.gif") Save = PhotoImage(file="icons/save.gif") Save_As = PhotoImage(file="icons/save_as.gif") Exit = PhotoImage(file="icons/exit.gif") #Appear menubar in app self.WDG.config(menu=self.MenuBar) #self.WDG.config(menu=self.IconBar) #3 #Set commands in menus #File_Menu self.File_menu.add_command(label="New", accelerator="Ctrl+N", compound="left", underline=0, command=self.New) self.File_menu.add_command(label="Open", accelerator="Ctrl+O", compound="left", underline=0, command=self.Open) self.File_menu.add_command(label="Save", accelerator="Ctrl+S", compound="left", underline=0, command=self.Save) self.File_menu.add_command(label="Save as", accelerator="Shift+Ctrl+S", compound="left", underline=0, command=self.Save_As) self.File_menu.add_separator() self.File_menu.add_command(label="Exit", accelerator="F4", compound="left", underline=0, command=self.Exit) #Edit_Menu self.Edit_menu.add_command(label="Undo", accelerator="Ctrl+Z", compound="left", underline=0, command=self.Undo) self.Edit_menu.add_command(label="Redo", accelerator='Ctrl+Y', compound='left', underline=0, command=self.Redo) self.Edit_menu.add_command(label="Select all", accelerator='Ctrl+A', compound='left', underline=0, command=self.Select) self.Edit_menu.add_command(label="Cut", accelerator='Ctrl+X', compound='left', underline=7, command=self.Cut) self.Edit_menu.add_command(label="Copy", accelerator='Ctrl+C', compound='left', underline=0, command=self.Copy) self.Edit_menu.add_command(label="Paste", accelerator='Ctrl+V', compound='left', underline=0, command=self.Paste) self.Edit_menu.add_command(label="Search", accelerator='Ctrl+F', compound='left', underline=0, command=self.Search) #Help_Menu self.Help_menu.add_command(label="Help", accelerator="F1", compound="left", underline=0, command=self.Help) self.Help_menu.add_command(label="About", compound="left", underline=0, command=self.About) #View_Menu self.Show_line_number = IntVar() self.Show_line_number.set(1) self.theme_name = StringVar() self.View_menu.add_checkbutton(label="Show Line Number", variable=self.Show_line_number) self.Highlightline = BooleanVar() self.View_menu.add_checkbutton(label='Highlight Current Line', onvalue=1, offvalue=0, variable=self.Highlightline, command=self.Toggle_highlight) self.cursorcoord = BooleanVar() self.View_menu.add_checkbutton(label='Show Cursor Location', variable=self.cursorcoord, command=self.Show_cursor_coord) self.Theme_menu.add_radiobutton(label="Default", variable=self.theme_name) #4 #add Shortcut_Bar & Row_Number_Bar #Shortcut_Bar self.Shortcut_Bar = Frame(self.WDG, height=25) self.Shortcut_Bar.pack(expand='no', fill='x') Icons = ['New', 'Open', 'Save', 'Copy', 'Cut', 'Paste', 'Undo', 'Redo'] for i, icon in enumerate(Icons): Tool_icon = PhotoImage(file='icons/{}.gif'.format(icon)) #c_var = 'self.{}'.format(icon) cmd = eval('self.{}'.format(icon)) self.Tool_bar_btn = Button(self.Shortcut_Bar, image=Tool_icon, command=cmd) self.Tool_bar_btn.image = Tool_icon self.Tool_bar_btn.pack(side='left') #Row_Number_Bar self.Row_Number_Bar = Text(self.WDG, width=3, padx=3, takefocus=0, border=0, background='khaki', state='disabled', wrap='none') self.Row_Number_Bar.pack(side='left', fill='y') #5 #add Content_Text self.Content_Text = Text(self.WDG, wrap='word', undo=1) self.Content_Text.pack(expand='yes', fill='both') self.Content_Text.tag_configure('active_line', background='ivory2') self.Scroll_Bar = Scrollbar(self.Content_Text) self.Content_Text.configure(yscrollcommand=self.Scroll_Bar.set) self.Scroll_Bar.config(command=self.Content_Text.yview) self.Scroll_Bar.pack(side='right', fill='y') #6 #add_Cursor_Coord_Bar self.Cursor_Coord_Bar = Label(self.Content_Text, text='Row: 1 | Column: 1') self.Cursor_Coord_Bar.pack(fill=None, expand='no', side='right', anchor='se') #7 #Binding self.Content_Text.bind("<Control-o>", self.Open) self.Content_Text.bind("<Control-O>", self.Open) self.Content_Text.bind("<Control-s>", self.Save) self.Content_Text.bind("<Control-S>", self.Save) self.Content_Text.bind("<Shift-Control-KeyPress-s>", self.Save_As) self.Content_Text.bind("<Shift-Control-KeyPress-S>", self.Save_As) self.Content_Text.bind("<Control-n>", self.New) self.Content_Text.bind("<Control-N>", self.New) self.Content_Text.bind("<Control-z>", self.Undo) self.Content_Text.bind("<Control-Z>", self.Undo) self.Content_Text.bind("<Control-y>", self.Redo) self.Content_Text.bind("<Control-Y>", self.Redo) self.Content_Text.bind("<Control-x>", self.Cut) self.Content_Text.bind("<Control-X>", self.Cut) self.Content_Text.bind("<Control-a>", self.Select) self.Content_Text.bind("<Control-A>", self.Select) self.Content_Text.bind("<Control-c>", self.Copy) self.Content_Text.bind("<Control-C>", self.Copy) self.Content_Text.bind("<Control-v>", self.Paste) self.Content_Text.bind("<Control-V>", self.Paste) self.Content_Text.bind("<Control-f>", self.Search) self.Content_Text.bind("<Control-F>", self.Search) self.Content_Text.bind("<Any-KeyPress>", self.Content_changed) self.WDG.bind_all("<KeyPress-F1>", self.Help) self.WDG.bind_all("<KeyPress-F4>", self.Exit) #8 #Built In Finctions #File_Menu_Functions def New(self, event=None): self.Content_Text.delete(1., 'end') self.WDG.title('{} - {}'.format('Untitled', self.Programe_Name)) ## def Open(self, event=None): self.Open_file_name = filedialog.askopenfilename( defaultextension=".txt", filetypes=[("All Files", "*.*"), ("Text Documents", "*.txt")]) if self.Open_file_name: self.File_name = self.Open_file_name self.WDG.title("{} - {}".format(os.path.basename(self.File_name), self.Programe_Name)) self.Content_Text.delete(1.0, 'end') with open(self.File_name) as _File: self.Content_Text.insert(1.0, _File.read()) ## def Save(self, event=None): if not self.File_name: self.Save_As() else: self.Write_to_file(self.File_name) return "break" ## def Save_As(self, event=None): self.Save_file_name = filedialog.asksaveasfilename( defaultextension='.txt', filetypes=[('All Files', '*.*'), ('Text Documents', '*.txt')]) if self.Save_file_name: self.File_name = self.Save_file_name self.Write_to_file(self.File_name) self.WDG.title('{} - {}'.format(os.path.basename(self.File_name), self.Programe_Name)) return "break" ## def Write_to_file(self, filename): try: self.content = self.Content_Text.get(1.0, 'end') with open(self.File_name, 'w') as the_file: the_file.write(self.content) except IOError as er: print(er) ## def Exit(self, event=None): self.msg_exit = messagebox.askyesno('Exit Editor', 'Do you want to exit?') if self.msg_exit: self.WDG.destroy() #Edit_Menu_Functions ## def Select(self, event=None): self.Content_Text.tag_add("sel", 1.0, "end") print("Done1") return "breake" ## def Cut(self, event=None): self.Content_Text.event_generate("<<Cut>>") return "breake" ## def Copy(self, event=None): self.Content_Text.event_generate("<<Copy>>") return "breake" ## def Paste(self, event=None): self.Content_Text.event_generate("<<Paste>>") return "breake" ## def Undo(self, event=None): self.Content_Text.event_generate("<<Undo>>") return "breake" ## def Redo(self, event=None): self.Content_Text.event_generate("<<Redo>>") return "breake" ## def Search(self, event=None): self.Search_Window = Toplevel(self.WDG) self.Search_Window.title("Search About...") self.Search_Window.transient(self.WDG) self.Search_Window.resizable(False, False) self.S_lbl_1 = Label(self.Search_Window, text='Search About :') self.S_lbl_1.grid(row=0, column=0, sticky='e') self.S_ent_1 = Entry(self.Search_Window, width=28) self.S_ent_1.grid(row=0, column=1, padx=2, pady=2, sticky='we') self.S_ent_1.focus_set() Ignore_case_value = IntVar() self.S_chk_1 = Checkbutton(self.Search_Window, text='Ignor Case', variable=Ignore_case_value) self.S_chk_1.grid(row=1, column=1, padx=2, pady=2, sticky='e') self.S_btn_1 = Button( self.Search_Window, text='Find', underline=0, command=lambda: self.Search_results(self.S_ent_1.get( ), Ignore_case_value.get(), self.Content_Text, self.Search_Window, self.S_ent_1)) self.S_btn_1.grid(row=0, column=2, padx=2, pady=2, sticky='e' + 'w') self.S_btn_2 = Button(self.Search_Window, text='Cancel', underline=0, command=self.Close_Search_Window) self.S_btn_2.grid(row=1, column=2, padx=2, pady=2, sticky='e' + 'w') ## def Search_results(self, Keyword, IfIgnoreCase, Content, Output, Input): Content.tag_remove('match', '1.0', 'end') matches_found = 0 if Keyword: start_pos = '1.0' while True: start_pos = Content.search(Keyword, start_pos, nocase=IfIgnoreCase, stopindex='end') if not start_pos: break end_pos = "{} + {}c".format(start_pos, len(Keyword)) Content.tag_add('match', start_pos, end_pos) matches_found += 1 start_pos = end_pos Content.tag_config('match', foreground='red', background='yellow') Input.focus_set() Output.title("{} matches found".format(matches_found)) ## def Close_Search_Window(self): self.Content_Text.tag_remove('match', '1.0', 'end') self.Search_Window.destroy() #self.Search_Window.protocol('WM_DELETE_WINDOW',self.Close_Search_Window) return "break" #View_Menu_Functions ## def Content_changed(self, event=None): self.Update_line_numbers() self.Update_cursor_coord() ## def Get_line_numbers(self, event=None): self.Number = "" if self.Show_line_number.get(): self.Row, self.Column = self.Content_Text.index('end').split('.') for i in range(1, int(self.Row)): self.Number += str(i) + "\n" return self.Number ## def Update_line_numbers(self): self.Line_Number = self.Get_line_numbers() self.Row_Number_Bar.config(state='normal') self.Row_Number_Bar.delete(1.0, 'end') self.Row_Number_Bar.insert(1.0, self.Line_Number) self.Row_Number_Bar.config(state='disabled') ## def Toggle_highlight(self, event=None): if self.Highlightline.get(): self.Highlight_line() else: self.Undo_highlight() ## def Highlight_line(self, interval=100): self.Content_Text.tag_remove('active_line', 1.0, 'end') self.Content_Text.tag_add('active_line', "insert linestart", "insert lineend+1c") self.Content_Text.after(interval, self.Toggle_highlight) ## def Undo_highlight(self): self.Content_Text.tag_remove('active_line', 1.0, 'end') ## def Show_cursor_coord(self): self.cursor_coord_checked = self.cursorcoord.get() if self.cursor_coord_checked: self.Cursor_Coord_Bar.pack(expand='no', fill=None, side='right', anchor='se') else: self.Cursor_Coord_Bar.pack_forget() ## def Update_cursor_coord(self): self.Row_2, self.Column_2 = self.Content_Text.index('insert').split( '.') self.row_num, self.col_num = str(int( self.Row_2)), str(int(self.Column_2) + 1) self.Coord = "Row: {} | Column: {}".format(self.row_num, self.col_num) self.Cursor_Coord_Bar.config(text=self.Coord) #Help_Menu_Functions ## def About(self, event=None): messagebox.showinfo( 'About', '{} {}'.format(self.Programe_Name, '\nDeveloped by \n TaReK')) ## def Help(self, event=None): messagebox.showinfo('Help', 'Text Editor building in python', icon='question')
class GetKeysDialog(Toplevel): # Dialog title for invalid key sequence keyerror_title = 'Key Sequence Error' def __init__(self, parent, title, action, current_key_sequences, *, _htest=False, _utest=False): """ parent - parent of this dialog title - string which is the title of the popup dialog action - string, the name of the virtual event these keys will be mapped to current_key_sequences - list, a list of all key sequence lists currently mapped to virtual events, for overlap checking _htest - bool, change box location when running htest _utest - bool, do not wait when running unittest """ Toplevel.__init__(self, parent) self.withdraw() # Hide while setting geometry. self.configure(borderwidth=5) self.resizable(height=False, width=False) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.cancel) self.parent = parent self.action = action self.current_key_sequences = current_key_sequences self.result = '' self.key_string = StringVar(self) self.key_string.set('') # Set self.modifiers, self.modifier_label. self.set_modifiers_for_platform() self.modifier_vars = [] for modifier in self.modifiers: variable = StringVar(self) variable.set('') self.modifier_vars.append(variable) self.advanced = False self.create_widgets() self.update_idletasks() self.geometry( "+%d+%d" % ( parent.winfo_rootx() + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), parent.winfo_rooty() + ((parent.winfo_height()/2 - self.winfo_reqheight()/2) if not _htest else 150) ) ) # Center dialog over parent (or below htest box). if not _utest: self.deiconify() # Geometry set, unhide. self.wait_window() def showerror(self, *args, **kwargs): # Make testing easier. Replace in #30751. messagebox.showerror(*args, **kwargs) def create_widgets(self): self.frame = frame = Frame(self, borderwidth=2, relief='sunken') frame.pack(side='top', expand=True, fill='both') frame_buttons = Frame(self) frame_buttons.pack(side='bottom', fill='x') self.button_ok = Button(frame_buttons, text='OK', width=8, command=self.ok) self.button_ok.grid(row=0, column=0, padx=5, pady=5) self.button_cancel = Button(frame_buttons, text='Cancel', width=8, command=self.cancel) self.button_cancel.grid(row=0, column=1, padx=5, pady=5) # Basic entry key sequence. self.frame_keyseq_basic = Frame(frame, name='keyseq_basic') self.frame_keyseq_basic.grid(row=0, column=0, sticky='nsew', padx=5, pady=5) basic_title = Label(self.frame_keyseq_basic, text=f"New keys for '{self.action}' :") basic_title.pack(anchor='w') basic_keys = Label(self.frame_keyseq_basic, justify='left', textvariable=self.key_string, relief='groove', borderwidth=2) basic_keys.pack(ipadx=5, ipady=5, fill='x') # Basic entry controls. self.frame_controls_basic = Frame(frame) self.frame_controls_basic.grid(row=1, column=0, sticky='nsew', padx=5) # Basic entry modifiers. self.modifier_checkbuttons = {} column = 0 for modifier, variable in zip(self.modifiers, self.modifier_vars): label = self.modifier_label.get(modifier, modifier) check = Checkbutton(self.frame_controls_basic, command=self.build_key_string, text=label, variable=variable, onvalue=modifier, offvalue='') check.grid(row=0, column=column, padx=2, sticky='w') self.modifier_checkbuttons[modifier] = check column += 1 # Basic entry help text. help_basic = Label(self.frame_controls_basic, justify='left', text="Select the desired modifier keys\n"+ "above, and the final key from the\n"+ "list on the right.\n\n" + "Use upper case Symbols when using\n" + "the Shift modifier. (Letters will be\n" + "converted automatically.)") help_basic.grid(row=1, column=0, columnspan=4, padx=2, sticky='w') # Basic entry key list. self.list_keys_final = Listbox(self.frame_controls_basic, width=15, height=10, selectmode='single') self.list_keys_final.insert('end', *AVAILABLE_KEYS) self.list_keys_final.bind('<ButtonRelease-1>', self.final_key_selected) self.list_keys_final.grid(row=0, column=4, rowspan=4, sticky='ns') scroll_keys_final = Scrollbar(self.frame_controls_basic, orient='vertical', command=self.list_keys_final.yview) self.list_keys_final.config(yscrollcommand=scroll_keys_final.set) scroll_keys_final.grid(row=0, column=5, rowspan=4, sticky='ns') self.button_clear = Button(self.frame_controls_basic, text='Clear Keys', command=self.clear_key_seq) self.button_clear.grid(row=2, column=0, columnspan=4) # Advanced entry key sequence. self.frame_keyseq_advanced = Frame(frame, name='keyseq_advanced') self.frame_keyseq_advanced.grid(row=0, column=0, sticky='nsew', padx=5, pady=5) advanced_title = Label(self.frame_keyseq_advanced, justify='left', text=f"Enter new binding(s) for '{self.action}' :\n" + "(These bindings will not be checked for validity!)") advanced_title.pack(anchor='w') self.advanced_keys = Entry(self.frame_keyseq_advanced, textvariable=self.key_string) self.advanced_keys.pack(fill='x') # Advanced entry help text. self.frame_help_advanced = Frame(frame) self.frame_help_advanced.grid(row=1, column=0, sticky='nsew', padx=5) help_advanced = Label(self.frame_help_advanced, justify='left', text="Key bindings are specified using Tkinter keysyms as\n"+ "in these samples: <Control-f>, <Shift-F2>, <F12>,\n" "<Control-space>, <Meta-less>, <Control-Alt-Shift-X>.\n" "Upper case is used when the Shift modifier is present!\n\n" + "'Emacs style' multi-keystroke bindings are specified as\n" + "follows: <Control-x><Control-y>, where the first key\n" + "is the 'do-nothing' keybinding.\n\n" + "Multiple separate bindings for one action should be\n"+ "separated by a space, eg., <Alt-v> <Meta-v>." ) help_advanced.grid(row=0, column=0, sticky='nsew') # Switch between basic and advanced. self.button_level = Button(frame, command=self.toggle_level, text='<< Basic Key Binding Entry') self.button_level.grid(row=2, column=0, stick='ew', padx=5, pady=5) self.toggle_level() def set_modifiers_for_platform(self): """Determine list of names of key modifiers for this platform. The names are used to build Tk bindings -- it doesn't matter if the keyboard has these keys; it matters if Tk understands them. The order is also important: key binding equality depends on it, so config-keys.def must use the same ordering. """ if sys.platform == "darwin": self.modifiers = ['Shift', 'Control', 'Option', 'Command'] else: self.modifiers = ['Control', 'Alt', 'Shift'] self.modifier_label = {'Control': 'Ctrl'} # Short name. def toggle_level(self): "Toggle between basic and advanced keys." if self.button_level.cget('text').startswith('Advanced'): self.clear_key_seq() self.button_level.config(text='<< Basic Key Binding Entry') self.frame_keyseq_advanced.lift() self.frame_help_advanced.lift() self.advanced_keys.focus_set() self.advanced = True else: self.clear_key_seq() self.button_level.config(text='Advanced Key Binding Entry >>') self.frame_keyseq_basic.lift() self.frame_controls_basic.lift() self.advanced = False def final_key_selected(self, event=None): "Handler for clicking on key in basic settings list." self.build_key_string() def build_key_string(self): "Create formatted string of modifiers plus the key." keylist = modifiers = self.get_modifiers() final_key = self.list_keys_final.get('anchor') if final_key: final_key = translate_key(final_key, modifiers) keylist.append(final_key) self.key_string.set(f"<{'-'.join(keylist)}>") def get_modifiers(self): "Return ordered list of modifiers that have been selected." mod_list = [variable.get() for variable in self.modifier_vars] return [mod for mod in mod_list if mod] def clear_key_seq(self): "Clear modifiers and keys selection." self.list_keys_final.select_clear(0, 'end') self.list_keys_final.yview('moveto', '0.0') for variable in self.modifier_vars: variable.set('') self.key_string.set('') def ok(self, event=None): keys = self.key_string.get().strip() if not keys: self.showerror(title=self.keyerror_title, parent=self, message="No key specified.") return if (self.advanced or self.keys_ok(keys)) and self.bind_ok(keys): self.result = keys self.grab_release() self.destroy() def cancel(self, event=None): self.result = '' self.grab_release() self.destroy() def keys_ok(self, keys): """Validity check on user's 'basic' keybinding selection. Doesn't check the string produced by the advanced dialog because 'modifiers' isn't set. """ final_key = self.list_keys_final.get('anchor') modifiers = self.get_modifiers() title = self.keyerror_title key_sequences = [key for keylist in self.current_key_sequences for key in keylist] if not keys.endswith('>'): self.showerror(title, parent=self, message='Missing the final Key') elif (not modifiers and final_key not in FUNCTION_KEYS + MOVE_KEYS): self.showerror(title=title, parent=self, message='No modifier key(s) specified.') elif (modifiers == ['Shift']) \ and (final_key not in FUNCTION_KEYS + MOVE_KEYS + ('Tab', 'Space')): msg = 'The shift modifier by itself may not be used with'\ ' this key symbol.' self.showerror(title=title, parent=self, message=msg) elif keys in key_sequences: msg = 'This key combination is already in use.' self.showerror(title=title, parent=self, message=msg) else: return True return False def bind_ok(self, keys): "Return True if Tcl accepts the new keys else show message." try: binding = self.bind(keys, lambda: None) except TclError as err: self.showerror( title=self.keyerror_title, parent=self, message=(f'The entered key sequence is not accepted.\n\n' f'Error: {err}')) return False else: self.unbind(keys, binding) return True
class Query(Toplevel): """Base class for getting verified answer from a user. For this base class, accept any non-blank string. """ def __init__(self, parent, title, message, *, text0='', used_names={}, _htest=False, _utest=False): """Create popup, do not return until tk widget destroyed. Additional subclass init must be done before calling this unless _utest=True is passed to suppress wait_window(). title - string, title of popup dialog message - string, informational message to display text0 - initial value for entry used_names - names already in use _htest - bool, change box location when running htest _utest - bool, leave window hidden and not modal """ Toplevel.__init__(self, parent) self.withdraw() # Hide while configuring, especially geometry. self.parent = parent self.title(title) self.message = message self.text0 = text0 self.used_names = used_names self.transient(parent) self.grab_set() windowingsystem = self.tk.call('tk', 'windowingsystem') if windowingsystem == 'aqua': try: self.tk.call('::tk::unsupported::MacWindowStyle', 'style', self._w, 'moveableModal', '') except: pass self.bind("<Command-.>", self.cancel) self.bind('<Key-Escape>', self.cancel) self.protocol("WM_DELETE_WINDOW", self.cancel) self.bind('<Key-Return>', self.ok) self.bind("<KP_Enter>", self.ok) self.resizable(height=False, width=False) self.create_widgets() self.update_idletasks() # Needed here for winfo_reqwidth below. self.geometry( # Center dialog over parent (or below htest box). "+%d+%d" % ( parent.winfo_rootx() + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), parent.winfo_rooty() + ((parent.winfo_height()/2 - self.winfo_reqheight()/2) if not _htest else 150) ) ) if not _utest: self.deiconify() # Unhide now that geometry set. self.wait_window() def create_widgets(self): # Call from override, if any. # Bind to self widgets needed for entry_ok or unittest. self.frame = frame = Frame(self, padding=10) frame.grid(column=0, row=0, sticky='news') frame.grid_columnconfigure(0, weight=1) entrylabel = Label(frame, anchor='w', justify='left', text=self.message) self.entryvar = StringVar(self, self.text0) self.entry = Entry(frame, width=30, textvariable=self.entryvar) self.entry.focus_set() self.error_font = Font(name='TkCaptionFont', exists=True, root=self.parent) self.entry_error = Label(frame, text=' ', foreground='red', font=self.error_font) self.button_ok = Button( frame, text='OK', default='active', command=self.ok) self.button_cancel = Button( frame, text='Cancel', command=self.cancel) entrylabel.grid(column=0, row=0, columnspan=3, padx=5, sticky=W) self.entry.grid(column=0, row=1, columnspan=3, padx=5, sticky=W+E, pady=[10,0]) self.entry_error.grid(column=0, row=2, columnspan=3, padx=5, sticky=W+E) self.button_ok.grid(column=1, row=99, padx=5) self.button_cancel.grid(column=2, row=99, padx=5) def showerror(self, message, widget=None): #self.bell(displayof=self) (widget or self.entry_error)['text'] = 'ERROR: ' + message def entry_ok(self): # Example: usually replace. "Return non-blank entry or None." self.entry_error['text'] = '' entry = self.entry.get().strip() if not entry: self.showerror('blank line.') return None return entry def ok(self, event=None): # Do not replace. '''If entry is valid, bind it to 'result' and destroy tk widget. Otherwise leave dialog open for user to correct entry or cancel. ''' entry = self.entry_ok() if entry is not None: self.result = entry self.destroy() else: # [Ok] moves focus. (<Return> does not.) Move it back. self.entry.focus_set() def cancel(self, event=None): # Do not replace. "Set dialog result to None and destroy tk widget." self.result = None self.destroy()
class GraphicalUserInterface(Frame): """ Graphical User Interface class """ # constant tag names STRONG = "STRONG" ITALIC = "ITALIC" HIGHLIGHT = "HIGHLIGHT" window_title = "DiAnnotator" # window title padding = config.get_int("padding", 25) # padding for text area") wrap_length = config.get_int( "wrap_length", 960) # max length of labels before automatic newline") # text parameters text_font_family = config.get_string("text_font_family", "mono") text_font_size = config.get_int("text_font_size", 12) text_font_weight = config.get_string("text_font_weight", "normal") text_foreground = config.get_string("text_foreground", "#ffffff") text_background = config.get_string("text_background", "#171717") # label parameters label_font_family = config.get_string("label_font_family", "mono") label_font_size = config.get_int("label_font_size", 12) label_font_weight = config.get_string("label_font_weight", "normal") # entry parameters entry_font_family = config.get_string("entry_font_family", "TkDefaultFont") entry_font_size = config.get_int("entry_font_size", 14) entry_font_weight = config.get_string("entry_font_weight", "normal") # menu parameters menu_font_family = config.get_string("menu_font_family", "modern") # button parameters button_font_family = config.get_string("button_font_family", "modern") # special button parameters special_button_font_family = config.get_string( "special_button_font_family", "mono") special_button_font_size = config.get_int("special_button_font_size", 11) special_button_font_weight = config.get_string( "special_button_font_weight", "bold") special_button_active = config.get_string("special_button_active", "#353131") # prompt parameters prompt_font_family = config.get_string("prompt_font_family", "modern") prompt_font_size = config.get_int("prompt_font_size", 12) prompt_font_weight = config.get_string("prompt_font_weight", "bold") # selection parameters select_background = config.get_string("select_background", "#332f2f") # highlight parameters highlight_background = config.get_string("highlight_background", "#332f2f") special_button_style = "Special.TButton" def __init__(self): """ Initializes the graphical user interface """ # main interface object self.parent = Tk() self.parent.state = False # strings manager strings = Strings() self._ = strings.get # theme style = ThemedStyle(self.parent) style.set_theme("arc") # button style style.configure("TButton", font=self.button_font_family) # special button style style.configure(self.special_button_style, font=(self.special_button_font_family, self.special_button_font_size, self.special_button_font_weight)) # make buttons change foreground when hovered style.map("TButton", foreground=[("active", self.special_button_active)]) # root window initialization Frame.__init__(self, self.parent) w = config.get_int("minimum_window_width", 1280) # minimum width for the Tk parent h = config.get_int("minimum_window_height", 800) # minimum height for the Tk parent # get screen width and height ws = self.parent.winfo_screenwidth() # width of the screen hs = self.parent.winfo_screenheight() # height of the screen self.parent.geometry("%dx%d+0+0" % (ws, hs)) # set the dimensions of the window self.parent.minsize(w, h) # minimum size of the window # window title self.parent.title(self.window_title) # menu bar self.menu_bar = Menu(self.parent, font=self.menu_font_family) self.file_menu = Menu(self.menu_bar, tearoff=0, font=self.menu_font_family) self.menu_bar.add_cascade(label=self._("menu.cascade.file"), menu=self.file_menu) self.edit_menu = Menu(self.menu_bar, tearoff=0, font=self.menu_font_family) self.menu_bar.add_cascade(label=self._("menu.cascade.edit"), menu=self.edit_menu) self.view_menu = Menu(self.menu_bar, tearoff=0, font=self.menu_font_family) self.menu_bar.add_cascade(label=self._("menu.cascade.view"), menu=self.view_menu) self.view_menu.add_command(label=self._("menu.zoom_in"), accelerator="Ctrl++", command=self.zoom_in) self.view_menu.add_command(label=self._("menu.zoom_out"), accelerator="Ctrl+-", command=self.zoom_out) self.filter_menu = Menu(self.menu_bar, tearoff=0, font=self.menu_font_family) self.menu_bar.add_cascade(label=self._("menu.cascade.filter"), menu=self.filter_menu) self.taxonomy_menu = Menu(self.menu_bar, tearoff=0, font=self.menu_font_family) self.menu_bar.add_cascade(label=self._("menu.cascade.taxonomy"), menu=self.taxonomy_menu) self.parent.config(menu=self.menu_bar) # make the frame take the whole window self.pack(fill=BOTH, expand=1) # input frame self.input_frame = None # output frame self.output_frame = Frame(self) self.output_frame.pack(fill=BOTH, anchor=N, expand=1) self.output_frame.grid_propagate(False) # ensure a consistent GUI size self.output_frame.grid_rowconfigure( 0, weight=30) # implement stretchability self.output_frame.grid_rowconfigure( 1, weight=1) # implement stretchability self.output_frame.grid_columnconfigure(0, weight=1) self.text = Text(self.output_frame, borderwidth=3, relief=SUNKEN, cursor="arrow", selectbackground=self.select_background, inactiveselectbackground=self.select_background) # Text widget self.text.grid(row=0, column=0, sticky="nsew", padx=self.padding, pady=(self.padding, 0)) self.text.config(font=(self.text_font_family, self.text_font_size), undo=True, wrap=WORD, bg=self.text_background, fg=self.text_foreground, state=DISABLED) # create a Scrollbar and associate it with text self.scrollbar = Scrollbar(self.output_frame, command=self.text.yview) self.scrollbar.grid(row=0, rowspan=3, column=1, sticky=NSEW) self.text["yscrollcommand"] = self.scrollbar.set # status bar self.status = Label(self.output_frame, font=(self.label_font_family, self.label_font_size, "bold")) self.status.grid(row=1, column=0, pady=0) # binds any typing to the command input field to the update_commands method sv = StringVar() sv.trace("w", lambda name, index, mode, sv=sv: self.update_commands()) # input frame self.input_frame = Frame(self) self.input_frame.pack(fill=X, side=BOTTOM) # makes the command input field self.entry = Entry(self.input_frame, font=(self.entry_font_family, self.entry_font_size), textvariable=sv, state=DISABLED) self.entry.bind( "<Return>", self.return_pressed ) # binds the Return key to the return_pressed() method self.entry.bind( "<KP_Enter>", self.return_pressed ) # binds the Return key to the return_pressed() method self.entry.bind( "<Tab>", self.tab_pressed) # binds the Tab key to the tab_pressed() method self.entry.pack(fill=X, side=BOTTOM, padx=2, pady=2) # places the input field self.entry.focus_set() # sets the focus on the input field # creates the frame containing buttons self.commands = Frame(self.input_frame) self.commands.pack(fill=X, side=BOTTOM) self.prompt = StringVar() self.prompt_label = Label(self.commands, font=(self.prompt_font_family, self.prompt_font_size, self.prompt_font_weight), textvariable=self.prompt) self.prompt_label.pack(side=LEFT, padx=(10, 15), pady=10) # creates the frame containing special buttons self.special_commands = Frame(self.input_frame) self.special_commands.pack(fill=X, side=BOTTOM) # default bindings self.parent.bind(config.get_string("toggle_fullscreen", "<F11>"), lambda event: self.toggle_fullscreen()) self.parent.bind(config.get_string("exit_prompt", "<Escape>"), lambda event: self.exit_prompt()) self.parent.bind(config.get_string("zoom_in", "<Control-KP_Add>"), lambda event: self.zoom_in()) self.parent.bind( config.get_string("zoom_out", "<Control-KP_Subtract>"), lambda event: self.zoom_out()) # binding mouse clicks and movement self.text.bind("<Button-1>", self.record_click) self.text.bind("<Button-2>", self.record_click) self.clickable_text_tag = "clickable_text_tag" self.text.bind("<ButtonRelease-1>", self.mouse_left_click) self.text.bind("<ButtonRelease-3>", self.mouse_right_click) self.text.bind("<Motion>", self.mouse_motion) self.last_click_index = "1.0" self.last_release_index = "1.0" self.command_list = [] # list of potential commands self.action = None # default command action self.default_action = None # default command action self.free_input = False # sets whether it's possible to input anything in the entry # basic text tags self.add_tag(GraphicalUserInterface.STRONG, font_weight="bold") self.add_tag(GraphicalUserInterface.ITALIC, font_weight="italic") self.add_tag(GraphicalUserInterface.HIGHLIGHT, background=self.highlight_background) self.text.tag_raise(SEL) # makes sure the selection is fisible ############################ # MOUSE MANAGEMENT METHODS # ############################ def record_click(self, event): """ Records last click index """ index = self.text.index("@%s,%s" % (event.x, event.y)) self.last_click_index = index def record_release(self, event): """ Records last click release index """ index = self.text.index("@%s,%s" % (event.x, event.y)) self.last_release_index = index def mouse_motion(self, event): """ Returns data when the cursor is on a clickable element """ start, end, text = self.examine_mouse_position(event) self.manage_motion(start, end, text) def mouse_left_click(self, event): """ Returns data when the user left clicks on a clickable element """ self.record_release(event) start, end, text = self.examine_mouse_position(event) self.manage_left_click(start, end, event.x_root, event.y_root, text) def mouse_right_click(self, event): """ Returns data when the user right clicks on a clickable element """ self.record_release(event) start, end, text = self.examine_mouse_position(event) self.manage_right_click(start, end, event.x_root, event.y_root, text) def examine_mouse_position(self, event): """ Examines the mouse position and returns data if a clickable element is hovered """ # get the index of the mouse click index = self.text.index("@%s,%s" % (event.x, event.y)) # get the indices of all clickable text tags tag_indices = list(self.text.tag_ranges(self.clickable_text_tag)) # iterate them pairwise (start and end index) for start, end in zip(tag_indices[0::2], tag_indices[1::2]): # check if the tag matches the mouse click index if self.text.compare(start, '<=', index) and self.text.compare( index, '<', end): # deals with string between tag start and end return start, end, self.text.get(start, end) return None, None, None def manage_motion(self, start, end, text): """ Mouse motion management """ pass # pass on purpose def manage_left_click(self, start, end, x, y, text): """ Mouse left click management """ pass # pass on purpose def manage_right_click(self, start, end, x, y, text): """ Mouse right click management """ pass # pass on purpose ############################### # KEYBOARD MANAGEMENT METHODS # ############################## def return_pressed(self, e): """ Handles presses of the Return key """ focus = self.parent.focus_get() if focus == self.entry: # if the input is free, the entry content is captured; else the only corresponding command is captured if self.free_input: text = self.entry.get() self.entry.delete(0, END) # clears the entry field self.process_input(text) elif len(self.entry.get()) == 0: self.default_action() # default action else: # list of buttons in the button list commands = self.commands.winfo_children() # if there is only one button (plus label), invokes it's function if len(commands) == 2: self.commands.winfo_children()[1].invoke() else: for button in self.commands.winfo_children(): if focus == button: self.button_pressed(button.cget("text")) def tab_pressed(self, e): """ Handles presses of the Tab key """ self.entry.focus_set() # sets the focus on the input field ############################ # INPUT MANAGEMENT METHODS # ############################ def button_pressed(self, b): """ Activates command buttons """ self.entry.delete(0, END) # clears the entry field self.process_input( b ) # processes input corresponding to the button to the output canvas def process_input(self, t): """ Processes user input """ self.free_input = False self.action(t) ############################## # DISPLAY MANAGEMENT METHODS # ############################## def update_status_message(self, text): """ Status message """ self.status.config(text=text) def update_commands(self): """ Updates the command button list """ for element in self.commands.winfo_children(): if isinstance(element, Button): element.destroy() input_text = self.entry.get() match = None for cmd in self.command_list: if cmd.lower() == input_text.lower(): match = cmd # if only one match if match: self.make_button(match) else: chunks = input_text.split(" ") for s in self.command_list: # only if the input text can correspond to the command if len([c for c in chunks if c.lower() in s.lower()]) == len(chunks): self.make_button(s) def make_button(self, text, disabled=False): b = Button(self.commands, text=text, command=lambda n=text: self.button_pressed(n)) b.bind("<Return>", self.return_pressed ) # binds the Return key to the return_pressed method b.pack(side=LEFT) if disabled: b.config(state=DISABLED) def toggle_fullscreen(self): """ Toggles between windowed and fullscreen """ self.parent.state = not self.parent.state # Just toggling the boolean self.parent.attributes("-fullscreen", self.parent.state) return "break" def exit_prompt(self): """ Show an exit prompt """ if messagebox.askyesno(self._("box.title.quit"), self._("box.text.quit")): self.parent.destroy() ########################### # TEXT MANAGEMENT METHODS # ########################### def select(self, start, end): """ Selects text """ self.text.tag_add(SEL, start, end) self.text.mark_set(INSERT, "1.0") def zoom_in(self): """ Increases Text widget font """ if self.text_font_size < 20: self.text_font_size += 1 self.update_font_size() def zoom_out(self): """ Decreases Text widget font """ if self.text_font_size > 8: self.text_font_size -= 1 self.update_font_size() def update_font_size(self): """ Updates Text widget font size """ self.text.config(font=(self.text_font_family, self.text_font_size)) self.text.tag_config(GraphicalUserInterface.STRONG, font=(self.text_font_family, self.text_font_size, "bold")) self.text.tag_config(GraphicalUserInterface.ITALIC, font=(self.text_font_family, self.text_font_size, "italic")) def clear_screen(self): """ Clears the text widget """ self.text.config(state=NORMAL) # makes the text editable self.text.delete("1.0", self.text.index(END)) self.text.config(state=DISABLED) # makes the text editable def clear_last_line(self): """ Clears the last line """ self.text.config(state=NORMAL) # makes the text editable self.text.delete("end-{}c linestart".format(self.previous_line_length), self.text.index(END)) self.text.config(state=DISABLED) # makes the text editable self.add_blank_lines(1) def highlight_last_line(self): """ Highlights the last line """ start = "{}.{}".format(int(self.text.index(END).split(".")[0]) - 2, 0) # start position of the line to output end = "{}.{}".format(int(self.text.index(END).split(".")[0]) - 1, 0) # adds style to the text self.text.tag_add(GraphicalUserInterface.HIGHLIGHT, start, end) def add_text(self, text, style=None, offset=0): """ Adds text to the text widget """ line_number = int(self.text.index(END).split( ".")[0]) - 1 # line number relative to the rest of the text self.text.config(state=NORMAL) # makes the text editable self.text.insert(END, text + "\n") # inserts text start = "{}.{}".format(line_number, 0 + offset) # start position of the line to output end = "{}.{}".format( line_number, len(text) + offset) # end position of the line that was outputted # adds style to the text if text and style: if isinstance(style, list): for s in style: self.text.tag_add(s, start, end) else: self.text.tag_add(style, start, end) self.text.config(state=DISABLED) # disabe the text field self.previous_tag_name = style self.previous_line_length = len(text) def add_to_last_line(self, text, style=None, offset=0): """ Adds text to the end of the previous line """ self.text.config(state=NORMAL) # makes the text editable self.text.delete("end-2c lineend", self.text.index(END)) self.text.config(state=DISABLED) # makes the text uneditable self.add_text(text, style=style, offset=offset) def add_blank_lines(self, n): """ Adds blank lines to the text widget """ if n > 0: self.add_text("" + "\n" * (n - 1)) def add_tag(self, name, foreground=None, background=None, justify=None, font_weight=None): """ Creates a new tag """ if foreground: self.text.tag_config(name, foreground=foreground) if background: self.text.tag_config(name, background=background) if justify: self.text.tag_config(name, justify=justify) if font_weight: self.text.tag_config(name, font=(self.text_font_family, self.text_font_size, font_weight)) ######################### # IO MANAGEMENT METHODS # ######################### def input(self, prompt, commands, action, free=False, sort=True, placeholder=""): """ Manages user input """ self.prompt.set(self._(prompt).title() + ":" if prompt else "") self.free_input = free self.action = action commands = sorted(list(commands)) if sort else list( commands) # sort commands alphabetically if necessary command_list = [str(c) for c in commands] if command_list != self.command_list: self.command_list = [str(c) for c in commands] self.update_commands() self.entry.delete(0, END) # clears the entry field self.entry.insert(0, placeholder) # inserts the placeholder self.entry.config(state=NORMAL) self.entry.focus_set() # sets the focus on the input field def output(self, message, style=None, blank_before=0, blank_after=0): """ Adds formatted text to the output buffer """ # convert numbers in the message to words self.add_blank_lines(blank_before) # blank lines before the output for line in message.split("\n"): self.add_text(line, style=style) self.add_blank_lines(blank_after) # blank lines after the output
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2020/6/10 18:58 # @File : entry控件.py # @Software: PyCharm import tkinter as tk from tkinter.ttk import Entry, Label root = tk.Tk() root.title("entry控件") root.geometry("300x160") Label(root, text='Name:').grid(row=0) Label(root, text='Pwd:').grid(row=1) var1 = tk.StringVar() var2 = tk.StringVar() entry1 = Entry(root, textvariable=var1, show="$") entry2 = Entry(root, textvariable=var2, show="$") entry1.grid(row=0, column=1) entry2.grid(row=1, column=1) entry1.focus_set() root.mainloop()
def __init__(self, parent, title, host=None, realm=None, useOsProxy=None, urlAddr=None, urlPort=None, user=None, password=None, database=None, timeout=None, dbType=None, showUrl=False, showUser=False, showHost=True, showRealm=True, showDatabase=False): super(DialogUserPassword, self).__init__(parent) self.parent = parent parentGeometry = re.match("(\d+)x(\d+)[+]?([-]?\d+)[+]?([-]?\d+)", parent.geometry()) dialogX = int(parentGeometry.group(3)) dialogY = int(parentGeometry.group(4)) self.accepted = False self.transient(self.parent) self.title(title) self.urlAddrVar = StringVar() self.urlAddrVar.set(urlAddr if urlAddr else "") self.urlPortVar = StringVar() self.urlPortVar.set(urlPort if urlPort else "") self.userVar = StringVar() self.userVar.set(user if user else "") self.passwordVar = StringVar() self.passwordVar.set(password if password else "") self.databaseVar = StringVar() self.databaseVar.set(database if database else "") self.timeoutVar = StringVar() self.timeoutVar.set(timeout if timeout else "") frame = Frame(self) y = 0 if showHost: hostLabel = Label(frame, text=_("Host:"), underline=0) hostDisplay = Label(frame, text=host, width=30) if host and len(host) > 30: ToolTip(hostDisplay, text=host, wraplength=240) hostLabel.grid(row=y, column=0, sticky=W, pady=3, padx=3) hostDisplay.grid(row=y, column=1, columnspan=4, sticky=EW, pady=3, padx=3) y += 1 if showRealm: realmLabel = Label(frame, text=_("Realm:"), underline=0) realmDisplay = Label(frame, text=realm, width=25) if realm and len(realm) > 30: ToolTip(realmDisplay, text=realm, wraplength=240) realmLabel.grid(row=y, column=0, sticky=W, pady=3, padx=3) realmDisplay.grid(row=y, column=1, columnspan=4, sticky=EW, pady=3, padx=3) y += 1 self.enabledWidgets = [] if useOsProxy is not None: if sys.platform.startswith("win"): hostProxy = _('Microsoft Windows Internet Settings') elif sys.platform in ("darwin", "macos"): hostProxy = _('Mac OS X System Configuration') else: # linux/unix hostProxy = _('environment variables') useOsProxyCb = checkbox(frame, 0, y, text=_("Use proxy server of {0}").format(hostProxy)) useOsProxyCb.grid(columnspan=5) useOsProxyCb.valueVar.set(useOsProxy) ToolTip(useOsProxyCb, text=_("Check to use {0} \n" "Uncheck to specify: \n" " No proxy if URL address is left blank, \n" " Proxy via URL address if it is not blank, \n" " with user and password (if provided)" ).format(hostProxy), wraplength=360) self.useOsProxyCb = useOsProxyCb useOsProxyCb.valueVar.trace("w", self.setEnabledState) y += 1 if showUrl: urlAddrLabel = Label(frame, text=_("Address:"), underline=0) urlAddrEntry = Entry(frame, textvariable=self.urlAddrVar, width=16) urlPortLabel = Label(frame, text=_("Port:"), underline=0) urlPortEntry = Entry(frame, textvariable=self.urlPortVar, width=5) urlAddrEntry.focus_set() urlAddrLabel.grid(row=y, column=0, sticky=W, pady=3, padx=3) urlAddrEntry.grid(row=y, column=1, columnspan=2, sticky=EW, pady=3, padx=3) urlPortLabel.grid(row=y, column=3, sticky=W, pady=3, padx=3) urlPortEntry.grid(row=y, column=4, sticky=EW, pady=3, padx=3) ToolTip(urlAddrEntry, text=_("Enter URL address and port number \n" " e.g., address: 168.1.2.3 port: 8080 \n" " or address: proxy.myCompany.com port: 8080 \n" " or leave blank to specify no proxy server"), wraplength=360) self.enabledWidgets.append(urlAddrEntry) self.enabledWidgets.append(urlPortEntry) y += 1 userLabel = Label(frame, text=_("User:"******"Password:"******"*") passwordLabel.grid(row=y, column=0, sticky=W, pady=3, padx=3) passwordEntry.grid(row=y, column=1, columnspan=4, sticky=EW, pady=3, padx=3) self.enabledWidgets.append(passwordEntry) y += 1 if showDatabase: urlDatabaseLabel = Label(frame, text=_("Database:"), underline=0) urlDatabaseEntry = Entry(frame, textvariable=self.databaseVar, width=25) urlDatabaseLabel.grid(row=y, column=0, sticky=W, pady=3, padx=3) urlDatabaseEntry.grid(row=y, column=1, columnspan=4, sticky=EW, pady=3, padx=3) ToolTip(urlAddrEntry, text=_("Enter database name (optional) or leave blank"), wraplength=360) self.enabledWidgets.append(urlDatabaseEntry) y += 1 urlTimeoutLabel = Label(frame, text=_("Timeout:"), underline=0) urlTimeoutEntry = Entry(frame, textvariable=self.timeoutVar, width=25) urlTimeoutLabel.grid(row=y, column=0, sticky=W, pady=3, padx=3) urlTimeoutEntry.grid(row=y, column=1, columnspan=4, sticky=EW, pady=3, padx=3) ToolTip(urlAddrEntry, text=_("Enter timeout seconds (optional) or leave blank for default (60 secs.)"), wraplength=360) self.enabledWidgets.append(urlTimeoutEntry) y += 1 dbTypeLabel = Label(frame, text=_("DB type:"), underline=0) dbTypeLabel.grid(row=y, column=0, sticky=W, pady=3, padx=3) self.cbDbType = gridCombobox(frame, 1, y, values=DBDescriptions, selectindex=DBTypes.index(dbType) if dbType in DBTypes else None) self.cbDbType.grid(columnspan=4, pady=3, padx=3) y += 1 okButton = Button(frame, text=_("OK"), command=self.ok) cancelButton = Button(frame, text=_("Cancel"), command=self.close) okButton.grid(row=y, column=2, sticky=E, pady=3) cancelButton.grid(row=y, column=3, columnspan=2, sticky=EW, pady=3, padx=3) y += 1 if useOsProxy is not None: self.setEnabledState() frame.grid(row=0, column=0, sticky=(N,S,E,W)) frame.columnconfigure(1, weight=1) window = self.winfo_toplevel() window.columnconfigure(0, weight=1) self.geometry("+{0}+{1}".format(dialogX+50,dialogY+100)) self.bind("<Return>", self.ok) self.bind("<Escape>", self.close) self.protocol("WM_DELETE_WINDOW", self.close) self.grab_set() self.wait_window(self)
class GetKeysDialog(Toplevel): # Dialog title for invalid key sequence keyerror_title = 'Key Sequence Error' def __init__(self, parent, title, action, current_key_sequences, *, _htest=False, _utest=False): """ parent - parent of this dialog title - string which is the title of the popup dialog action - string, the name of the virtual event these keys will be mapped to current_key_sequences - list, a list of all key sequence lists currently mapped to virtual events, for overlap checking _htest - bool, change box location when running htest _utest - bool, do not wait when running unittest """ Toplevel.__init__(self, parent) self.withdraw() # Hide while setting geometry. self.configure(borderwidth=5) self.resizable(height=False, width=False) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.cancel) self.parent = parent self.action = action self.current_key_sequences = current_key_sequences self.result = '' self.key_string = StringVar(self) self.key_string.set('') # Set self.modifiers, self.modifier_label. self.set_modifiers_for_platform() self.modifier_vars = [] for modifier in self.modifiers: variable = StringVar(self) variable.set('') self.modifier_vars.append(variable) self.advanced = False self.create_widgets() self.update_idletasks() self.geometry("+%d+%d" % (parent.winfo_rootx() + (parent.winfo_width() / 2 - self.winfo_reqwidth() / 2), parent.winfo_rooty() + ((parent.winfo_height() / 2 - self.winfo_reqheight() / 2) if not _htest else 150)) ) # Center dialog over parent (or below htest box). if not _utest: self.deiconify() # Geometry set, unhide. self.wait_window() def showerror(self, *args, **kwargs): # Make testing easier. Replace in #30751. messagebox.showerror(*args, **kwargs) def create_widgets(self): self.frame = frame = Frame(self, borderwidth=2, relief='sunken') frame.pack(side='top', expand=True, fill='both') frame_buttons = Frame(self) frame_buttons.pack(side='bottom', fill='x') self.button_ok = Button(frame_buttons, text='OK', width=8, command=self.ok) self.button_ok.grid(row=0, column=0, padx=5, pady=5) self.button_cancel = Button(frame_buttons, text='Cancel', width=8, command=self.cancel) self.button_cancel.grid(row=0, column=1, padx=5, pady=5) # Basic entry key sequence. self.frame_keyseq_basic = Frame(frame, name='keyseq_basic') self.frame_keyseq_basic.grid(row=0, column=0, sticky='nsew', padx=5, pady=5) basic_title = Label(self.frame_keyseq_basic, text=f"New keys for '{self.action}' :") basic_title.pack(anchor='w') basic_keys = Label(self.frame_keyseq_basic, justify='left', textvariable=self.key_string, relief='groove', borderwidth=2) basic_keys.pack(ipadx=5, ipady=5, fill='x') # Basic entry controls. self.frame_controls_basic = Frame(frame) self.frame_controls_basic.grid(row=1, column=0, sticky='nsew', padx=5) # Basic entry modifiers. self.modifier_checkbuttons = {} column = 0 for modifier, variable in zip(self.modifiers, self.modifier_vars): label = self.modifier_label.get(modifier, modifier) check = Checkbutton(self.frame_controls_basic, command=self.build_key_string, text=label, variable=variable, onvalue=modifier, offvalue='') check.grid(row=0, column=column, padx=2, sticky='w') self.modifier_checkbuttons[modifier] = check column += 1 # Basic entry help text. help_basic = Label(self.frame_controls_basic, justify='left', text="Select the desired modifier keys\n" + "above, and the final key from the\n" + "list on the right.\n\n" + "Use upper case Symbols when using\n" + "the Shift modifier. (Letters will be\n" + "converted automatically.)") help_basic.grid(row=1, column=0, columnspan=4, padx=2, sticky='w') # Basic entry key list. self.list_keys_final = Listbox(self.frame_controls_basic, width=15, height=10, selectmode='single') self.list_keys_final.insert('end', *AVAILABLE_KEYS) self.list_keys_final.bind('<ButtonRelease-1>', self.final_key_selected) self.list_keys_final.grid(row=0, column=4, rowspan=4, sticky='ns') scroll_keys_final = Scrollbar(self.frame_controls_basic, orient='vertical', command=self.list_keys_final.yview) self.list_keys_final.config(yscrollcommand=scroll_keys_final.set) scroll_keys_final.grid(row=0, column=5, rowspan=4, sticky='ns') self.button_clear = Button(self.frame_controls_basic, text='Clear Keys', command=self.clear_key_seq) self.button_clear.grid(row=2, column=0, columnspan=4) # Advanced entry key sequence. self.frame_keyseq_advanced = Frame(frame, name='keyseq_advanced') self.frame_keyseq_advanced.grid(row=0, column=0, sticky='nsew', padx=5, pady=5) advanced_title = Label( self.frame_keyseq_advanced, justify='left', text=f"Enter new binding(s) for '{self.action}' :\n" + "(These bindings will not be checked for validity!)") advanced_title.pack(anchor='w') self.advanced_keys = Entry(self.frame_keyseq_advanced, textvariable=self.key_string) self.advanced_keys.pack(fill='x') # Advanced entry help text. self.frame_help_advanced = Frame(frame) self.frame_help_advanced.grid(row=1, column=0, sticky='nsew', padx=5) help_advanced = Label( self.frame_help_advanced, justify='left', text="Key bindings are specified using Tkinter keysyms as\n" + "in these samples: <Control-f>, <Shift-F2>, <F12>,\n" "<Control-space>, <Meta-less>, <Control-Alt-Shift-X>.\n" "Upper case is used when the Shift modifier is present!\n\n" + "'Emacs style' multi-keystroke bindings are specified as\n" + "follows: <Control-x><Control-y>, where the first key\n" + "is the 'do-nothing' keybinding.\n\n" + "Multiple separate bindings for one action should be\n" + "separated by a space, eg., <Alt-v> <Meta-v>.") help_advanced.grid(row=0, column=0, sticky='nsew') # Switch between basic and advanced. self.button_level = Button(frame, command=self.toggle_level, text='<< Basic Key Binding Entry') self.button_level.grid(row=2, column=0, stick='ew', padx=5, pady=5) self.toggle_level() def set_modifiers_for_platform(self): """Determine list of names of key modifiers for this platform. The names are used to build Tk bindings -- it doesn't matter if the keyboard has these keys; it matters if Tk understands them. The order is also important: key binding equality depends on it, so config-keys.def must use the same ordering. """ if sys.platform == "darwin": self.modifiers = ['Shift', 'Control', 'Option', 'Command'] else: self.modifiers = ['Control', 'Alt', 'Shift'] self.modifier_label = {'Control': 'Ctrl'} # Short name. def toggle_level(self): "Toggle between basic and advanced keys." if self.button_level.cget('text').startswith('Advanced'): self.clear_key_seq() self.button_level.config(text='<< Basic Key Binding Entry') self.frame_keyseq_advanced.lift() self.frame_help_advanced.lift() self.advanced_keys.focus_set() self.advanced = True else: self.clear_key_seq() self.button_level.config(text='Advanced Key Binding Entry >>') self.frame_keyseq_basic.lift() self.frame_controls_basic.lift() self.advanced = False def final_key_selected(self, event=None): "Handler for clicking on key in basic settings list." self.build_key_string() def build_key_string(self): "Create formatted string of modifiers plus the key." keylist = modifiers = self.get_modifiers() final_key = self.list_keys_final.get('anchor') if final_key: final_key = translate_key(final_key, modifiers) keylist.append(final_key) self.key_string.set(f"<{'-'.join(keylist)}>") def get_modifiers(self): "Return ordered list of modifiers that have been selected." mod_list = [variable.get() for variable in self.modifier_vars] return [mod for mod in mod_list if mod] def clear_key_seq(self): "Clear modifiers and keys selection." self.list_keys_final.select_clear(0, 'end') self.list_keys_final.yview('moveto', '0.0') for variable in self.modifier_vars: variable.set('') self.key_string.set('') def ok(self, event=None): keys = self.key_string.get().strip() if not keys: self.showerror(title=self.keyerror_title, parent=self, message="No key specified.") return if (self.advanced or self.keys_ok(keys)) and self.bind_ok(keys): self.result = keys self.grab_release() self.destroy() def cancel(self, event=None): self.result = '' self.grab_release() self.destroy() def keys_ok(self, keys): """Validity check on user's 'basic' keybinding selection. Doesn't check the string produced by the advanced dialog because 'modifiers' isn't set. """ final_key = self.list_keys_final.get('anchor') modifiers = self.get_modifiers() title = self.keyerror_title key_sequences = [ key for keylist in self.current_key_sequences for key in keylist ] if not keys.endswith('>'): self.showerror(title, parent=self, message='Missing the final Key') elif (not modifiers and final_key not in FUNCTION_KEYS + MOVE_KEYS): self.showerror(title=title, parent=self, message='No modifier key(s) specified.') elif (modifiers == ['Shift']) \ and (final_key not in FUNCTION_KEYS + MOVE_KEYS + ('Tab', 'Space')): msg = 'The shift modifier by itself may not be used with'\ ' this key symbol.' self.showerror(title=title, parent=self, message=msg) elif keys in key_sequences: msg = 'This key combination is already in use.' self.showerror(title=title, parent=self, message=msg) else: return True return False def bind_ok(self, keys): "Return True if Tcl accepts the new keys else show message." try: binding = self.bind(keys, lambda: None) except TclError as err: self.showerror( title=self.keyerror_title, parent=self, message=(f'The entered key sequence is not accepted.\n\n' f'Error: {err}')) return False else: self.unbind(keys, binding) return True
class Query(Toplevel): """Base class for getting verified answer from a user. For this base class, accept any non-blank string. """ def __init__(self, parent, title, message, *, _htest=False, _utest=False): # Call from override. """Create popup, do not return until tk widget destroyed. Additional subclass init must be done before calling this. title - string, title of popup dialog message - string, informational message to display _htest - bool, change box location when running htest _utest - bool, leave window hidden and not modal """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.bind("<Key-Return>", self.ok) self.protocol("WM_DELETE_WINDOW", self.cancel) self.parent = parent self.message = message self.create_widgets() self.update_idletasks() # needs to be done here so that the winfo_reqwidth is valid self.withdraw() # Hide while configuring, especially geometry. self.geometry( "+%d+%d" % ( parent.winfo_rootx() + (parent.winfo_width() / 2 - self.winfo_reqwidth() / 2), parent.winfo_rooty() + ((parent.winfo_height() / 2 - self.winfo_reqheight() / 2) if not _htest else 150), ) ) # centre dialog over parent (or below htest box) if not _utest: self.deiconify() # geometry set, unhide self.wait_window() def create_widgets(self): # Call from override, if any. frame = Frame(self, borderwidth=2, relief="sunken") label = Label(frame, anchor="w", justify="left", text=self.message) self.entry = Entry(frame, width=30) # Bind name for entry_ok. self.entry.focus_set() buttons = Frame(self) # Bind buttons for invoke in unittest. self.button_ok = Button(buttons, text="Ok", width=8, command=self.ok) self.button_cancel = Button(buttons, text="Cancel", width=8, command=self.cancel) frame.pack(side="top", expand=TRUE, fill="both") label.pack(padx=5, pady=5) self.entry.pack(padx=5, pady=5) buttons.pack(side="bottom") self.button_ok.pack(side="left", padx=5) self.button_cancel.pack(side="right", padx=5) def entry_ok(self): # Usually replace. "Check that entry not blank." entry = self.entry.get().strip() if not entry: showerror(title="Entry Error", message="Blank line.", parent=self) return entry def ok(self, event=None): # Do not replace. """If entry is valid, bind it to 'result' and destroy tk widget. Otherwise leave dialog open for user to correct entry or cancel. """ entry = self.entry_ok() if entry: self.result = entry self.destroy() else: # [Ok] (but not <Return>) moves focus. Move it back. self.entry.focus_set() def cancel(self, event=None): # Do not replace. "Set dialog result to None and destroy tk widget." self.result = None self.destroy()
class Query(Toplevel): """Base class for getting verified answer from a user. For this base class, accept any non-blank string. """ def __init__(self, parent, title, message, *, text0='', used_names={}, _htest=False, _utest=False): """Create popup, do not return until tk widget destroyed. Additional subclass init must be done before calling this unless _utest=True is passed to suppress wait_window(). title - string, title of popup dialog message - string, informational message to display text0 - initial value for entry used_names - names already in use _htest - bool, change box location when running htest _utest - bool, leave window hidden and not modal """ Toplevel.__init__(self, parent) self.withdraw() # Hide while configuring, especially geometry. self.configure(borderwidth=5) self.resizable(height=False, width=False) self.title(title) self.transient(parent) self.grab_set() self.bind('<Key-Return>', self.ok) self.bind('<Key-Escape>', self.cancel) self.protocol("WM_DELETE_WINDOW", self.cancel) self.parent = parent self.message = message self.text0 = text0 self.used_names = used_names self.create_widgets() self.update_idletasks() # Needed here for winfo_reqwidth below. self.geometry( # Center dialog over parent (or below htest box). "+%d+%d" % ( parent.winfo_rootx() + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), parent.winfo_rooty() + ((parent.winfo_height()/2 - self.winfo_reqheight()/2) if not _htest else 150) ) ) if not _utest: self.deiconify() # Unhide now that geometry set. self.wait_window() def create_widgets(self): # Call from override, if any. # Bind to self widgets needed for entry_ok or unittest. self.frame = frame = Frame(self, borderwidth=2, relief='sunken', ) entrylabel = Label(frame, anchor='w', justify='left', text=self.message) self.entryvar = StringVar(self, self.text0) self.entry = Entry(frame, width=30, textvariable=self.entryvar) self.entry.focus_set() buttons = Frame(self) self.button_ok = Button(buttons, text='Ok', width=8, command=self.ok) self.button_cancel = Button(buttons, text='Cancel', width=8, command=self.cancel) frame.pack(side='top', expand=True, fill='both') entrylabel.pack(padx=5, pady=5) self.entry.pack(padx=5, pady=5) buttons.pack(side='bottom') self.button_ok.pack(side='left', padx=5) self.button_cancel.pack(side='right', padx=5) def entry_ok(self): # Example: usually replace. "Return non-blank entry or None." entry = self.entry.get().strip() if not entry: showerror(title='Entry Error', message='Blank line.', parent=self) return None return entry def ok(self, event=None): # Do not replace. '''If entry is valid, bind it to 'result' and destroy tk widget. Otherwise leave dialog open for user to correct entry or cancel. ''' entry = self.entry_ok() if entry is not None: self.result = entry self.destroy() else: # [Ok] moves focus. (<Return> does not.) Move it back. self.entry.focus_set() def cancel(self, event=None): # Do not replace. "Set dialog result to None and destroy tk widget." self.result = None self.destroy()
class Query(Toplevel): """Base class for getting verified answer from a user. For this base class, accept any non-blank string. """ def __init__(self, parent, title, message, *, text0='', used_names={}, _htest=False, _utest=False): """Create popup, do not return until tk widget destroyed. Additional subclass init must be done before calling this unless _utest=True is passed to suppress wait_window(). title - string, title of popup dialog message - string, informational message to display text0 - initial value for entry used_names - names already in use _htest - bool, change box location when running htest _utest - bool, leave window hidden and not modal """ Toplevel.__init__(self, parent) self.withdraw() self.parent = parent self.title(title) self.message = message self.text0 = text0 self.used_names = used_names self.transient(parent) self.grab_set() windowingsystem = self.tk.call('tk', 'windowingsystem') if windowingsystem == 'aqua': try: self.tk.call('::tk::unsupported::MacWindowStyle', 'style', self._w, 'moveableModal', '') except: pass self.bind('<Command-.>', self.cancel) self.bind('<Key-Escape>', self.cancel) self.protocol('WM_DELETE_WINDOW', self.cancel) self.bind('<Key-Return>', self.ok) self.bind('<KP_Enter>', self.ok) self.resizable(height=False, width=False) self.create_widgets() self.update_idletasks() self.geometry('+%d+%d' % (parent.winfo_rootx() + (parent.winfo_width() / 2 - self.winfo_reqwidth() / 2), parent.winfo_rooty() + (parent.winfo_height() / 2 - self.winfo_reqheight() / 2 if not _htest else 150))) if not _utest: self.deiconify() self.wait_window() def create_widgets(self): self.frame = frame = Frame(self, padding=10) frame.grid(column=0, row=0, sticky='news') frame.grid_columnconfigure(0, weight=1) entrylabel = Label(frame, anchor='w', justify='left', text=self.message) self.entryvar = StringVar(self, self.text0) self.entry = Entry(frame, width=30, textvariable=self.entryvar) self.entry.focus_set() self.error_font = Font(name='TkCaptionFont', exists=True, root=self.parent) self.entry_error = Label(frame, text=' ', foreground='red', font=self.error_font) self.button_ok = Button(frame, text='OK', default='active', command=self.ok) self.button_cancel = Button(frame, text='Cancel', command=self.cancel) entrylabel.grid(column=0, row=0, columnspan=3, padx=5, sticky=W) self.entry.grid(column=0, row=1, columnspan=3, padx=5, sticky=W + E, pady=[10, 0]) self.entry_error.grid(column=0, row=2, columnspan=3, padx=5, sticky=W + E) self.button_ok.grid(column=1, row=99, padx=5) self.button_cancel.grid(column=2, row=99, padx=5) def showerror(self, message, widget=None): (widget or self.entry_error)['text'] = 'ERROR: ' + message def entry_ok(self): """Return non-blank entry or None.""" self.entry_error['text'] = '' entry = self.entry.get().strip() if not entry: self.showerror('blank line.') return None return entry def ok(self, event=None): """If entry is valid, bind it to 'result' and destroy tk widget. Otherwise leave dialog open for user to correct entry or cancel. """ entry = self.entry_ok() if entry is not None: self.result = entry self.destroy() else: self.entry.focus_set() def cancel(self, event=None): """Set dialog result to None and destroy tk widget.""" self.result = None self.destroy()
def add_latex(self, img_name=None): def ok(event): latex = r'%s' % text.get() if latex: if img_name is None: l = [ int(os.path.splitext(f)[0]) for f in os.listdir(PATH_LATEX) ] l.sort() if l: i = l[-1] + 1 else: i = 0 img = "%i.png" % i self.txt.tag_bind(img, '<Double-Button-1>', lambda e: self.add_latex(img)) self.latex[img] = latex else: img = img_name im = os.path.join(PATH_LATEX, img) try: math_to_image(latex, im, fontsize=CONFIG.getint("Font", "text_size") - 2) self.images.append(PhotoImage(file=im, master=self)) if self.txt.tag_ranges("sel"): index = self.txt.index("sel.first") self.txt.delete('sel.first', 'sel.last') else: index = self.txt.index("current") self.txt.image_create(index, image=self.images[-1], name=im) self.txt.tag_add(img, index) top.destroy() except Exception as e: showerror(_("Error"), str(e)) top = Toplevel(self) top.transient(self) top.update_idletasks() top.geometry("+%i+%i" % top.winfo_pointerxy()) top.grab_set() top.resizable(True, False) top.title("LaTex") text = Entry(top, justify='center') if img_name is not None: text.insert(0, self.latex[img_name]) else: if self.txt.tag_ranges('sel'): text.insert(0, self.txt.get('sel.first', 'sel.last')) else: text.insert(0, '$$') text.icursor(1) text.pack(fill='x', expand=True) text.bind('<Return>', ok) text.focus_set()
class EmpDatTableCanvas(TableCanvas): """ Override for TableCanvas """ def __init__(self, *args, col_modifiers: dict = None, on_change=None, on_unsaved=None, on_selected=None, **kwargs): """ TableCanvas constructor :param args: blanket passthrough :param col_modifiers: dictionary modifying column entry Example: { 0: { 'read_only': True }, 1: { 'options': ['A', 'B', 'C"] # Options A, B, and C }, 2: { 'render_as': lambda X: Y # Render X as Y } } :param on_change: callback called on every change :param on_unsaved: callback called on every change, passes 1 parameter on whether there are pending changes :param on_selected: callback called on when a row is selected :param kwargs: blanket passthrough """ super().__init__(*args, **kwargs) self.col_modifiers = col_modifiers self.unsaved = set() self.on_change = on_change self.on_unsaved = on_unsaved self.on_selected = on_selected def drawText(self, row, col, celltxt, fgcolor=None, align=None): """Draw the text inside a cell area""" col_name = self.model.getColumnName(col) if col_name in self.col_modifiers and 'render_as' in self.col_modifiers[ col_name]: celltxt = self.col_modifiers[col_name]['render_as'](celltxt) if len(celltxt) == 0 or celltxt == 'None': celltxt = '' self.delete('celltext' + str(col) + '_' + str(row)) h = self.rowheight x1, y1, x2, y2 = self.getCellCoords(row, col) w = x2 - x1 wrap = False pad = 5 # If celltxt is a number then we make it a string if type(celltxt) is float or type(celltxt) is int: celltxt = str(celltxt) length = len(celltxt) if length == 0: return # if cell width is less than x, print nothing if w <= 10: return if fgcolor == None or fgcolor == "None": fgcolor = 'black' if align == None: align = 'w' if align == 'w': x1 = x1 - w / 2 + pad elif align == 'e': x1 = x1 + w / 2 - pad if w < 18: celltxt = '.' else: fontsize = self.fontsize colname = self.model.getColumnName(col) # scaling between canvas and text normalised to about font 14 scale = 8.5 * float(fontsize) / 12 size = length * scale if size > w: newlength = w / scale # print w, size, length, newlength celltxt = celltxt[0:int(math.floor(newlength))] # if celltxt is dict then we are drawing a hyperlink if self.isLink(celltxt) == True: haslink = 0 linktext = celltxt['text'] if len(linktext) > w / scale or w < 28: linktext = linktext[0:int(w / fontsize * 1.2) - 2] + '..' if celltxt['link'] != None and celltxt['link'] != '': f, s = self.thefont linkfont = (f, s, 'underline') linkcolor = 'blue' haslink = 1 else: linkfont = self.thefont linkcolor = fgcolor rect = self.create_text(x1 + w / 2, y1 + h / 2, text=linktext, fill=linkcolor, font=linkfont, tag=('text', 'hlink', 'celltext' + str(col) + '_' + str(row))) if haslink == 1: self.tag_bind(rect, '<Double-Button-1>', self.check_hyperlink) # just normal text else: rect = self.create_text(x1 + w / 2, y1 + h / 2, text=celltxt, fill=fgcolor, font=self.thefont, anchor=align, tag=('text', 'celltext' + str(col) + '_' + str(row))) return def drawCellEntry(self, row, col, text=None): """When the user single/double clicks on a text/number cell, bring up entry window""" col_name = self.model.getColumnName(col) if self.read_only or (col_name in self.col_modifiers and 'read_only' in self.col_modifiers[col_name] and self.col_modifiers[col_name]['read_only']): return # absrow = self.get_AbsoluteRow(row) height = self.rowheight model = self.getModel() cellvalue = self.model.getCellRecord(row, col) if Formula.isFormula(cellvalue): return else: text = self.model.getValueAt(row, col) if text == 'None': text = '' x1, y1, x2, y2 = self.getCellCoords(row, col) w = x2 - x1 # Draw an entry window txtvar = StringVar() txtvar.set(text) def callback(e): value = txtvar.get() if value == '=': # do a dialog that gets the formula into a text area # then they can click on the cells they want # when done the user presses ok and its entered into the cell self.cellentry.destroy() # its all done here.. self.formula_Dialog(row, col) return coltype = self.model.getColumnType(col) if coltype == 'number': sta = self.checkDataEntry(e) if sta == 1: self.unsaved.add(self.model.getRecName(row)) model.setValueAt(value, row, col) elif coltype == 'text': self.unsaved.add(self.model.getRecName(row)) model.setValueAt(value, row, col) color = self.model.getColorAt(row, col, 'fg') self.drawText(row, col, value, color, align=self.align) if not isinstance(e, str) and e.keysym == 'Return': self.delete('entry') # self.drawRect(row, col) # self.gotonextCell(e) if self.on_change: self.on_change() if len(self.unsaved) > 0: self.on_unsaved(False, row, col) else: self.on_unsaved(True, row, col) is_required = True if col_name in self.col_modifiers and \ 'required' in self.col_modifiers[col_name] else False if col_name in self.col_modifiers and 'validator' in self.col_modifiers[ col_name]: if not is_required and value == '': self.model.setColorAt(row, col, 'white') self.redrawCell(row, col) return if callable(self.col_modifiers[col_name]['validator']): if not self.col_modifiers[col_name]['validator'](value): self.model.setColorAt(row, col, 'coral') self.redrawCell(row, col) else: if not is_valid_against( self.col_modifiers[col_name]['validator'], value): self.model.setColorAt(row, col, 'coral') self.redrawCell(row, col) return if col_name in self.col_modifiers and 'options' in self.col_modifiers[ col_name]: options = list(self.col_modifiers[col_name]['options']) if cellvalue in options: first = cellvalue options.remove(first) options.insert(0, first) self.cellentry = OptionMenu(self.parentframe, txtvar, *options, command=callback) elif col_name in self.col_modifiers and 'date' in self.col_modifiers[ col_name]: self.cellentry = DateEntry(self.parentframe, width=20, textvariable=txtvar, takefocus=1, font=self.thefont) self.cellentry.bind('<<DateEntrySelected>>', callback) else: self.cellentry = Entry(self.parentframe, width=20, textvariable=txtvar, takefocus=1, font=self.thefont) self.cellentry.selection_range(0, END) try: self.cellentry.icursor(END) except AttributeError: pass self.cellentry.bind('<Return>', callback) self.cellentry.bind('<KeyRelease>', callback) self.cellentry.focus_set() self.entrywin = self.create_window(x1 + self.inset, y1 + self.inset, width=w - self.inset * 2, height=height - self.inset * 2, window=self.cellentry, anchor='nw', tag='entry') return def handle_left_click(self, event): """Respond to a single press""" self.on_selected() super().handle_left_click(event) def popupMenu(self, event, rows=None, cols=None, outside=None): """Add left and right click behaviour for canvas, should not have to override this function, it will take its values from defined dicts in constructor""" defaultactions = { "Set Fill Color": lambda: self.setcellColor(rows, cols, key='bg'), "Set Text Color": lambda: self.setcellColor(rows, cols, key='fg'), "Copy": lambda: self.copyCell(rows, cols), "Paste": lambda: self.pasteCell(rows, cols), "Fill Down": lambda: self.fillDown(rows, cols), "Fill Right": lambda: self.fillAcross(cols, rows), "Add Row(s)": lambda: self.addRows(), "Delete Row(s)": lambda: self.deleteRow(), "View Record": lambda: self.getRecordInfo(row), "Clear Data": lambda: self.deleteCells(rows, cols), "Select All": self.select_All, "Auto Fit Columns": self.autoResizeColumns, "Filter Records": self.showFilteringBar, "New": self.new, "Load": self.load, "Save": self.save, "Import text": self.importTable, "Export csv": self.exportTable, "Plot Selected": self.plotSelected, "Plot Options": self.plotSetup, "Export Table": self.exportTable, "Preferences": self.showtablePrefs, "Formulae->Value": lambda: self.convertFormulae(rows, cols) } main = [ "Set Fill Color", "Set Text Color", "Copy", "Paste", "Fill Down", "Fill Right", "Clear Data" ] general = [ "Select All", "Auto Fit Columns", "Filter Records", "Preferences" ] def createSubMenu(parent, label, commands): menu = Menu(parent, tearoff=0) popupmenu.add_cascade(label=label, menu=menu) for action in commands: menu.add_command(label=action, command=defaultactions[action]) return menu def add_commands(fieldtype): """Add commands to popup menu for column type and specific cell""" functions = self.columnactions[fieldtype] for f in functions.keys(): func = getattr(self, functions[f]) popupmenu.add_command(label=f, command=lambda: func(row, col)) return popupmenu = Menu(self, tearoff=0) def popupFocusOut(event): popupmenu.unpost() if outside == None: # if outside table, just show general items row = self.get_row_clicked(event) col = self.get_col_clicked(event) coltype = self.model.getColumnType(col) def add_defaultcommands(): """now add general actions for all cells""" for action in main: if action == 'Fill Down' and (rows == None or len(rows) <= 1): continue if action == 'Fill Right' and (cols == None or len(cols) <= 1): continue else: popupmenu.add_command(label=action, command=defaultactions[action]) return if coltype in self.columnactions: add_commands(coltype) add_defaultcommands() for action in general: popupmenu.add_command(label=action, command=defaultactions[action]) popupmenu.add_separator() # createSubMenu(popupmenu, 'File', filecommands) # createSubMenu(popupmenu, 'Plot', plotcommands) popupmenu.bind("<FocusOut>", popupFocusOut) popupmenu.focus_set() popupmenu.post(event.x_root, event.y_root) return popupmenu