|
Post by Uncle Buddy on Jan 14, 2024 23:40:28 GMT -8
place_autofill_values_model_2024.py last changed 2024-01-15
import tkinter as tk import sqlite3 from query_strings import select_nested_place_id_inclusion import dev_tools as dt from dev_tools import look, seeline
# There's a problem with refreshing the values list after a change to the db-- # hard to get it to work simply every time. Also the autofill list shd be a dict # with other values than just an autofill string like nested_place_id and nest0 # so these don't have to be gone after, they should already be associated with # the nesting in the collection. Also the main values should be a list, not a # dict, since single-nest nestings--which are made automatically when a new # single place is added--might have more than one nested_place_id.
current_tree = "d:/treebard_gps/data/sample_tree/sample_tree.tbd"
place_data1 = { "Glenwood Springs, Garfield County, Colorado, USA": [ {"nested_place_id": 225, "local_place_id": 14}], "Paris": [{"nested_place_id": 23, "local_place_id": 198}, {"nested_place_id": 56, "local_place_id": 91}], "Paris, Lamar County, Texas, USA": [ {"nested_place_id": 114, "local_place_id": 91}], "Paris, Ile-de-France, France": [ {"nested_place_id": 17, "local_place_id": 198}]}
place_data2 = { "Filbert Springs, Garsley County, Stegosaurus, USA": [ {"nested_place_id": 76, "local_place_id": 34}], "Maine": [{"nested_place_id": 97, "local_place_id": 21}, {"nested_place_id": 45, "local_place_id": 22}], "Maine, Maine County, Maine, USA": [ {"nested_place_id": 47, "local_place_id": 22}], "Maine, Ile-de-France, France": [ {"nested_place_id": 99, "local_place_id": 21}]}
def get_place_data(): conn = sqlite3.connect(current_tree) cur = conn.cursor() cur.execute( cur.close() conn.close()
place_data2 = get_place_data()
class EntryAutoPlace(tk.Entry): def __init__(self, master, family_tree, width=20, *args, **kwargs): tk.Entry.__init__(self, master, *args, **kwargs) self.master = master self.family_tree = family_tree self.width = width # used to size Radiobutton self.values = self.family_tree.place_autofill_values
self.family_tree.place_autofill_inputs.append(self) self.widget_data = {}
self.autofilled = None self.config(bd=0, width=width)
self.bind("<KeyPress>", self.detect_pressed) self.bind("<KeyRelease>", self.get_typed) self.bind("<FocusIn>", self.deselect, add="+") self.bind("<FocusOut>", self.get_ids, add="+")
def get_nestings(self, local_place_id): conn = sqlite3.connect(current_tree) cur = conn.cursor() cur.execute(select_nested_place_id_inclusion, (local_place_id,)*9) nestings = cur.fetchall() print("line", look(seeline()).lineno, "nestings", nestings)
cur.close() conn.close()
def get_ids(self, evt):
def close_overlay(): local_place_id = self.radvar.get() overlay.destroy() widg.delete(0, "end") # resize entry print("line", look(seeline()).lineno, "ids", ids) for dkt in ids: smallest = dkt["local_place_id"] self.get_nestings(smallest) if smallest == local_place_id: nested_place_id = dkt["nested_place_id"] self.widget_data = dkt break print("line", look(seeline()).lineno, "self.widget_data", self.widget_data)
widg = evt.widget content = widg.get() if len(content) == 0: return nested_place_id = 1 local_place_id = 1 ids = self.family_tree.place_data[content] if len(ids) == 1: dkt = ids[0] nested_place_id = dkt["nested_place_id"] local_place_id = dkt["local_place_id"] self.widget_data = dkt elif len(ids) > 1: # Get examples of nestings in which each local_place_id appears # and use them as text for radiobuttons in an overlay gridded # in the autofill so the autofill can't be used till choice made. # The clicking of the radiobutton will destroy the overlay and # insert the correct text while setting a variable to the right # nested place id as per the selected radio. Include a None of # the Above radio which will act like a Cancel button by # destroying the overlay, clearing the entry, and putting the # focus in the entry. # self.width prevents width change to the Entry when gridding and ungridding radiobuttons in it
overlay = tk.Frame(widg) overlay.grid(sticky="news")
self.radvar = tk.IntVar(None, 99) for idx, dkt in enumerate(ids): rad = tk.Radiobutton( overlay, text=dkt, command=close_overlay, cursor="hand2", anchor="w", variable=self.radvar, value=dkt["local_place_id"]) rad.grid(column=0, row=idx, sticky="ew") idx=idx none = tk.Radiobutton( overlay, text="None of the above.", command=close_overlay, cursor="hand2", variable=self.radvar, value=99, width=self.width, anchor="w") none.grid(column=0, row=idx+1, sticky="ew")
print("line", look(seeline()).lineno, "self.widget_data", self.widget_data)
def detect_pressed(self, evt): """ Run on every key press. """ key = evt.keysym if len(key) == 1: self.pos = self.index('insert') keep = self.get()[0:self.pos].strip("+") self.delete(0, 'end') self.insert(0, keep)
def get_typed(self, evt): """ Run on every key release; filters out most non-alpha-numeric keys; runs the functions not triggered by events. """ def do_it(): hits = self.match_string() self.show_hits(hits, self.pos)
key = evt.keysym # allow alphanumeric characters if len(key) == 1: do_it() # allow hyphens and apostrophes elif key in ('minus', 'quoteright'): do_it() # look for other chars that should be allowed in nested names else: pass
def match_string(self): hits = [] got = self.get() use_list = self.values for item in use_list: if item.lower().startswith(got.lower()): hits.append(item) return hits
def show_hits(self, hits, pos): cursor = pos + 1 if len(hits) != 0: self.autofilled = hits[0] self.delete(0, 'end') self.insert(0, self.autofilled) self.icursor(cursor) def deselect(self, evt): """ Since this is an autofill, replacement of selected text doesn't work as expected, so clear the selection as a workaround. """ self.select_clear()
class FamilyTree(tk.Toplevel): def __init__(self, master, place_data=None, *args, **kwargs): tk.Toplevel.__init__(self, master, *args, **kwargs)
self.family_tree = master self.place_data = place_data self.place_autofill_inputs = []
place_autofill_values = list(self.place_data.keys()) self.config_values(place_data)
def config_values(self, place_data): self.place_autofill_values = list(place_data.keys()) self.place_data = place_data for ent in self.place_autofill_inputs: ent.values = self.place_autofill_values
if __name__ == "__main__":
def ok(tree, place_data): tree.config_values(place_data)
def open_trees(): tree1 = FamilyTree(root, place_data=place_data1) tree1.title("Tree 1") ent1 = EntryAutoPlace(tree1, tree1, width=48) ent1.grid() tree1.geometry("+100+400") b1 = tk.Button( tree1, text='OK', command=lambda tree=tree1, place_data=place_data2: ok( tree, place_data)) b1.grid()
tree2 = FamilyTree(root, place_data=place_data2) tree2.title("Tree 2") ent2 = EntryAutoPlace(tree2, tree2, width=48) ent2.grid() ent2.focus_set() tree2.geometry("+100+600") b2 = tk.Button( tree2, text='OK', command=lambda tree=tree2, place_data=place_data1: ok( tree, place_data)) b2.grid()
root = tk.Tk()
open_trees()
root.mainloop()
# DO LIST
# After ids can be gotten when tabbing out of an autofill, do prepending but don't change the order of the dict, just the autofill values list.
|
|
|
Post by Uncle Buddy on Jan 14, 2024 23:46:08 GMT -8
events_table_model_2024.py last changed 2024-01-11
import tkinter as tk import sqlite3 from files import appwide_db_path, current_drive, get_current_tree from widgets import make_formats_dict from redraw import redraw_person_tab, redraw_gui
import dev_tools as dt from dev_tools import look, seeline
COLUMNS = ("EVENT", "DATE", "PLACE", "PARTICULARS", "AGE", "ROLES", "NOTES", "SOURCES")
stuff = [ ["Maecenas", "quis", "elit", "eleifend lobortis lobortis lobortis lobortis lobortis", "lobortis", "turpis", "at", "iaculis"], ["odio", "Phasellus", "congue", "urna", "sit", "amet", "posuere", "luctus"], ["mauris", "risus", "tincidunt", "sapien", "vulputate", "scelerisque", "ipsum", "libero"], ["at", "neque", "Nunc", "accumsan", "pellentesque", "nulla", "a", "ultricies"], ["ex", "convallis", "sit", "amet", "Etiam", "ut", "sollicitudin", "felis"], ["sit", "amet", "dictum", "lacus", "Mauris", "sed", "mattis", "diam"], ["Pellentesque", "eu", "malesuada", "ipsum", "vitae", "sagittis", "nisl", "Morbi"], ["a", "mi", "vitae", "nunc", "varius", "ullamcorper", "in", "ut"], ["urna", "Maecenas", "auctor", "ultrices", "orci", "Donec", "facilisis", "a"], ["tortor", "pellentesque", "venenatis", "Curabitur", "pulvinar", "bibendum", "sem", "id"], ["eleifend", "lorem", "sodales", "nec", "Mauris", "eget", "scelerisque", "libero"], ["Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit"], ["Integer", "vel", "tellus", "nec", "orci", "finibus", "ornare", "Praesent"], ["pellentesque", "aliquet", "augue", "nec", "feugiat", "augue", "posuere", "a"]]
values = ( "marriage", "marriage banns", "residence", "birth", "death", "occupation", "divorce", "divorce filing")
class EventsTable(tk.Frame): def __init__(self, master, rows, columns, *args, **kwargs): tk.Frame.__init__(self, master, *args, **kwargs)
self["bg"] = treebard.formats["bg"] self.make_widgets(rows, columns)
def make_widgets(self, rows, columns):
for col, head in enumerate(COLUMNS): label = LabelH3(self, anchor="w") label["text"] = head label.grid(row=0, column=col, sticky="nsew")
sep = FrameHighlighted(self, height=2) sep.grid(column=0, row=1, sticky="ew", columnspan=8)
self.cells = [] for row, value in enumerate(stuff, 1): current_row = [] for idx, head in enumerate(COLUMNS): cell = CellAutofill(self, values=values, width=20, height=1) # cell = Cell(self, width=20, height=1) cell.insert(1.0, stuff[row-1][idx]) cell.grid(row=row+2, column=idx, sticky="nsew", padx=1, pady=1) cell.set_height() current_row.append(cell) # list of widgets by row self.cells.append(current_row) # list of rows
def edit(self, column, row, value): widget = self.cells[row][column] print('widget is', widget)
class Toplevel(tk.Toplevel): def __init__(self, master, *args, **kwargs): tk.Toplevel.__init__(self, master, *args, **kwargs) self["bg"] = treebard.formats["bg"]
class Button(tk.Button): def __init__(self, master, *args, **kwargs): tk.Button.__init__(self, master, *args, **kwargs) self["bg"] = treebard.formats["bg"] self["fg"] = treebard.formats["fg"] self["font"] = treebard.formats["output_font"] self["activebackground"] = treebard.formats["highlight_bg"]
class Entry(tk.Entry): def __init__(self, master, *args, **kwargs): tk.Entry.__init__(self, master, *args, **kwargs) self["bg"] = treebard.formats["highlight_bg"] self["insertbackground"] = treebard.formats["fg"] self["fg"] = treebard.formats["fg"] self["font"] = treebard.formats["input_font"] self["selectbackground"] = treebard.formats["head_bg"] self["selectforeground"] = treebard.formats["fg"]
class FrameHighlighted(tk.Frame): def __init__(self, master, *args, **kwargs): tk.Frame.__init__(self, master, *args, **kwargs)
self["bg"] = treebard.formats["highlight_bg"]
class LabelH3(tk.Label): def __init__(self, master, *args, **kwargs): tk.Label.__init__(self, master, *args, **kwargs) self["bg"] = treebard.formats["bg"] self["fg"] = treebard.formats["fg"] self["font"] = treebard.formats["heading3"]
class Label(tk.Label): def __init__(self, master, *args, **kwargs): tk.Label.__init__(self, master, *args, **kwargs) self["bg"] = treebard.formats["bg"] self["fg"] = treebard.formats["fg"] self["font"] = treebard.formats["output_font"]
class Cell(tk.Text): def __init__(self, master, *args, **kwargs): tk.Text.__init__(self, master, *args, **kwargs)
self["wrap"] = "word" self["bd"] = 0
self["bg"] = treebard.formats["bg"] self["fg"] = treebard.formats["fg"] self["font"] = treebard.formats["output_font"] self["insertbackground"] = treebard.formats["fg"] self["selectbackground"] = treebard.formats["highlight_bg"] self["selectforeground"] = treebard.formats["fg"]
self.bind("<Tab>", self.focus_next_window) self.bind("<Shift-Tab>", self.focus_prev_window) self.bind("<FocusIn>", self.highlight) self.bind("<FocusOut>", self.unhighlight)
def set_height(self): """ After gridding this widget, run `self.set_height()`. The answer returned by `displaylines` is wrong first time thru mainloop so `update_idletasks()` has to run. """ self.update_idletasks() lines = self.count('1.0', 'end', 'displaylines') self.config(height=lines)
self.tag_configure('left', justify='left') self.tag_add('left', '1.0', 'end')
def highlight(self, evt): self["bg"] = treebard.formats["head_bg"]
def unhighlight(self, evt): self["bg"] = treebard.formats["bg"]
# Make the Text widget use the TAB key for traversal like other widgets. # `return('break')` prevents the built-in binding to TAB. def focus_next_window(self, evt): evt.widget.tk_focusNext().focus() return("break")
def focus_prev_window(self, evt): evt.widget.tk_focusPrev().focus() return("break")
class CellAutofill(Cell): def __init__(self, master, values=None, *args, **kwargs): Cell.__init__(self, master, *args, **kwargs)
self.master = master self.values = values
self.bind("<KeyPress>", self.detect_pressed) self.bind("<KeyRelease>", self.get_typed)
def detect_pressed(self, evt): """ Runs on every key press. """ key = evt.keysym if len(key) == 1: self.pos = self.index('insert') print("line", look(seeline()).lineno, "self.pos", self.pos) line, char = self.pos.split(".") # keep = self.get()[0:self.pos] keep = self.get("1.0", "end")[0:int(char)] # keep = self.get("1.0", "end")[0:self.pos] self.delete("1.0", 'end') self.insert("1.0", keep)
# def detect_pressed(evt): # """ Runs on every key press. """ # key = evt.keysym # widg = evt.widget # if len(key) == 1: # widg.pos = widg.index('insert') # print("line", look(seeline()).lineno, "widg.pos", widg.pos) # if key == "b": # word = "blue" # elif key == "p": # word = "pink" # widg.insert("1.0", word) # line, char = widg.pos.split(".") # widg.mark_set("insert", f"{line}.{char}")
def get_typed(self, evt): """ Run on every key release; filters out most non-alpha-numeric keys; runs the functions not triggered by events. """ def do_it(): hits = self.match_string() self.show_hits(hits, self.pos)
# if self.autofill is False: # return key = evt.keysym # allow alphanumeric characters if len(key) == 1: do_it() # allow hyphens and apostrophes elif key in ('minus', 'quoteright'): do_it() # look for other chars that should be allowed in nested names else: pass
def match_string(self): hits = [] got = self.get("1.0", "end").replace("\n", "") print("line", look(seeline()).lineno, "got", got) use_list = self.values for item in use_list: if item.lower().startswith(got.lower()): hits.append(item) return hits
def show_hits(self, hits, pos): # line, col = pos.split(".") # cursor = int(col) + 1 # cursor = pos + 1 if len(hits) != 0: self.autofilled = hits[0] self.delete("1.0", 'end') self.insert("1.0", self.autofilled) # self.icursor(cursor) line, char = pos.split(".") cursor = int(char) + 1 self.mark_set("insert", f"{line}.{str(cursor)}")
# def deselect(self, evt): # """ Since this is an autofill, replacement of selected text doesn't # work as expected, so clear the selection as a workaround. # """ # self.select_clear()
if __name__ == "__main__":
styles = { "LabelH3": {"bg": "bg", "fg": "fg"}, "Cell": { "bg": "bg", "fg": "fg", "insertbackground": "fg", "selectbackground": "highlight_bg", "selectforeground": "fg", "font": "output_font"}, "EventsTable": {"bg": "bg",}, "FrameHighlighted": {"bg": "highlight_bg"}, "Button": { "bg": "bg", "fg": "fg", "font": "output_font", "activebackground": "highlight_bg"}, "Entry": { "bg": "bg", "fg": "fg", "insertbackground": "fg", "selectbackground": "highlight_bg", "selectforeground": "fg", "font": "input_font"}, "Toplevel": {"bg": "bg"}, "Label": {"bg": "bg", "fg": "fg", "font": "output_font"}}
new_colors = ("beige", "steelblue", "yellow", "black") options = ("bg", "highlight_bg", "head_bg", "fg") descendants = []
def get_all_descendants (ancestor, deep_list): """ List every widget in the app by running recursively. """ lst = ancestor.winfo_children() for item in lst: deep_list.append(item) get_all_descendants(item, deep_list) return deep_list
def change_colors():
def ok(): for ent in entries: widg, key = ent color = widg.get() treebard.formats[key] = color get_all_descendants(treebard, descendants) for widget in descendants: subclass = type(widget).__name__ opts = styles[subclass] for key, val in opts.items(): widget[key] = treebard.formats[val] treebard["bg"] = treebard.formats["bg"] color_dlg.destroy()
entries = [] color_dlg = Toplevel(treebard) for idx, text in enumerate(options): lab = Label(color_dlg, text=text, anchor="w") ent = Entry(color_dlg) lab.grid(column=0, row=idx, sticky="e") ent.grid(column=1, row=idx, sticky="w") ent.insert(0, new_colors[idx]) entries.append((ent, text)) okok = Button(color_dlg, text="OK", command=ok) okok.grid(sticky="e")
treebard = tk.Tk() formats = make_formats_dict(root=True) treebard.formats = dict(formats) treebard["bg"] = treebard.formats["bg"] events_table = EventsTable(treebard, 8, 10) events_table.edit(0, 1, "Dooby dooby doo") events_table.grid()
colorizer = Button(treebard, text="COLORIZE", command=change_colors) colorizer.grid(sticky="e") colorizer.focus_set()
treebard.mainloop()
# DO LIST
# rewrite the events table: # redo the color scheme so it's efficient and uses dicts not lists and doesn't have post-created attributes like lab.style. # in the minified model include a table with all the features mentioned including kintips # get rid of the cell_pool do everything straightforward # get rid of the overlay use text widgets directly for the autofill # make sure redraw works doesn't make reference problems for destroyed widgets doesn't grid widgets on top of widgets and doesn't introduce weird bugs or change appearance
|
|